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