1 //===--- ForbiddenSubclassingCheck.cpp - clang-tidy -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ForbiddenSubclassingCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "llvm/ADT/Hashing.h" 14 #include "llvm/ADT/SmallVector.h" 15 #include "../utils/OptionsUtils.h" 16 17 using namespace clang::ast_matchers; 18 19 namespace clang { 20 namespace tidy { 21 namespace objc { 22 23 namespace { 24 25 constexpr char DefaultForbiddenSuperClassNames[] = 26 "ABNewPersonViewController;" 27 "ABPeoplePickerNavigationController;" 28 "ABPersonViewController;" 29 "ABUnknownPersonViewController;" 30 "NSHashTable;" 31 "NSMapTable;" 32 "NSPointerArray;" 33 "NSPointerFunctions;" 34 "NSTimer;" 35 "UIActionSheet;" 36 "UIAlertView;" 37 "UIImagePickerController;" 38 "UITextInputMode;" 39 "UIWebView"; 40 41 /// \brief Matches Objective-C classes that directly or indirectly 42 /// have a superclass matching \c Base. 43 /// 44 /// Note that a class is not considered to be a subclass of itself. 45 /// 46 /// Example matches Y, Z 47 /// (matcher = objcInterfaceDecl(hasName("X"))) 48 /// \code 49 /// @interface X 50 /// @end 51 /// @interface Y : X // directly derived 52 /// @end 53 /// @interface Z : Y // indirectly derived 54 /// @end 55 /// \endcode 56 AST_MATCHER_P(ObjCInterfaceDecl, isSubclassOf, 57 ast_matchers::internal::Matcher<ObjCInterfaceDecl>, Base) { 58 for (const auto *SuperClass = Node.getSuperClass(); 59 SuperClass != nullptr; 60 SuperClass = SuperClass->getSuperClass()) { 61 if (Base.matches(*SuperClass, Finder, Builder)) { 62 return true; 63 } 64 } 65 return false; 66 } 67 68 } // namespace 69 70 ForbiddenSubclassingCheck::ForbiddenSubclassingCheck( 71 StringRef Name, 72 ClangTidyContext *Context) 73 : ClangTidyCheck(Name, Context), 74 ForbiddenSuperClassNames( 75 utils::options::parseStringList( 76 Options.get("ClassNames", DefaultForbiddenSuperClassNames))) { 77 } 78 79 void ForbiddenSubclassingCheck::registerMatchers(MatchFinder *Finder) { 80 Finder->addMatcher( 81 objcInterfaceDecl( 82 isSubclassOf( 83 objcInterfaceDecl( 84 hasAnyName( 85 std::vector<StringRef>( 86 ForbiddenSuperClassNames.begin(), 87 ForbiddenSuperClassNames.end()))) 88 .bind("superclass"))) 89 .bind("subclass"), 90 this); 91 } 92 93 void ForbiddenSubclassingCheck::check( 94 const MatchFinder::MatchResult &Result) { 95 const auto *SubClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>( 96 "subclass"); 97 assert(SubClass != nullptr); 98 const auto *SuperClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>( 99 "superclass"); 100 assert(SuperClass != nullptr); 101 diag(SubClass->getLocation(), 102 "Objective-C interface %0 subclasses %1, which is not " 103 "intended to be subclassed") 104 << SubClass 105 << SuperClass; 106 } 107 108 void ForbiddenSubclassingCheck::storeOptions( 109 ClangTidyOptions::OptionMap &Opts) { 110 Options.store( 111 Opts, 112 "ForbiddenSuperClassNames", 113 utils::options::serializeStringList(ForbiddenSuperClassNames)); 114 } 115 116 } // namespace objc 117 } // namespace tidy 118 } // namespace clang 119