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 // this check should only be applied to ObjC sources. 81 if (!getLangOpts().ObjC1 && !getLangOpts().ObjC2) { 82 return; 83 } 84 Finder->addMatcher( 85 objcInterfaceDecl( 86 isSubclassOf( 87 objcInterfaceDecl( 88 hasAnyName( 89 std::vector<StringRef>( 90 ForbiddenSuperClassNames.begin(), 91 ForbiddenSuperClassNames.end()))) 92 .bind("superclass"))) 93 .bind("subclass"), 94 this); 95 } 96 97 void ForbiddenSubclassingCheck::check( 98 const MatchFinder::MatchResult &Result) { 99 const auto *SubClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>( 100 "subclass"); 101 assert(SubClass != nullptr); 102 const auto *SuperClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>( 103 "superclass"); 104 assert(SuperClass != nullptr); 105 diag(SubClass->getLocation(), 106 "Objective-C interface %0 subclasses %1, which is not " 107 "intended to be subclassed") 108 << SubClass 109 << SuperClass; 110 } 111 112 void ForbiddenSubclassingCheck::storeOptions( 113 ClangTidyOptions::OptionMap &Opts) { 114 Options.store( 115 Opts, 116 "ForbiddenSuperClassNames", 117 utils::options::serializeStringList(ForbiddenSuperClassNames)); 118 } 119 120 } // namespace objc 121 } // namespace tidy 122 } // namespace clang 123