//===--- ForbiddenSubclassingCheck.cpp - clang-tidy -----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ForbiddenSubclassingCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallVector.h" #include "../utils/OptionsUtils.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace objc { namespace { constexpr char DefaultForbiddenSuperClassNames[] = "ABNewPersonViewController;" "ABPeoplePickerNavigationController;" "ABPersonViewController;" "ABUnknownPersonViewController;" "NSHashTable;" "NSMapTable;" "NSPointerArray;" "NSPointerFunctions;" "NSTimer;" "UIActionSheet;" "UIAlertView;" "UIImagePickerController;" "UITextInputMode;" "UIWebView"; /// \brief Matches Objective-C classes that directly or indirectly /// have a superclass matching \c Base. /// /// Note that a class is not considered to be a subclass of itself. /// /// Example matches Y, Z /// (matcher = objcInterfaceDecl(hasName("X"))) /// \code /// @interface X /// @end /// @interface Y : X // directly derived /// @end /// @interface Z : Y // indirectly derived /// @end /// \endcode AST_MATCHER_P(ObjCInterfaceDecl, isSubclassOf, ast_matchers::internal::Matcher, Base) { for (const auto *SuperClass = Node.getSuperClass(); SuperClass != nullptr; SuperClass = SuperClass->getSuperClass()) { if (Base.matches(*SuperClass, Finder, Builder)) { return true; } } return false; } } // namespace ForbiddenSubclassingCheck::ForbiddenSubclassingCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), ForbiddenSuperClassNames( utils::options::parseStringList( Options.get("ClassNames", DefaultForbiddenSuperClassNames))) { } void ForbiddenSubclassingCheck::registerMatchers(MatchFinder *Finder) { // this check should only be applied to ObjC sources. if (!getLangOpts().ObjC) return; Finder->addMatcher( objcInterfaceDecl( isSubclassOf( objcInterfaceDecl( hasAnyName( std::vector( ForbiddenSuperClassNames.begin(), ForbiddenSuperClassNames.end()))) .bind("superclass"))) .bind("subclass"), this); } void ForbiddenSubclassingCheck::check( const MatchFinder::MatchResult &Result) { const auto *SubClass = Result.Nodes.getNodeAs( "subclass"); assert(SubClass != nullptr); const auto *SuperClass = Result.Nodes.getNodeAs( "superclass"); assert(SuperClass != nullptr); diag(SubClass->getLocation(), "Objective-C interface %0 subclasses %1, which is not " "intended to be subclassed") << SubClass << SuperClass; } void ForbiddenSubclassingCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store( Opts, "ForbiddenSuperClassNames", utils::options::serializeStringList(ForbiddenSuperClassNames)); } } // namespace objc } // namespace tidy } // namespace clang