Index: clang-tidy/google/CMakeLists.txt =================================================================== --- clang-tidy/google/CMakeLists.txt +++ clang-tidy/google/CMakeLists.txt @@ -9,6 +9,7 @@ GoogleTidyModule.cpp IntegerTypesCheck.cpp NonConstReferences.cpp + ObjcForbiddenSubclassingCheck.cpp OverloadedUnaryAndCheck.cpp StringReferenceMemberCheck.cpp TodoCommentCheck.cpp Index: clang-tidy/google/GoogleTidyModule.cpp =================================================================== --- clang-tidy/google/GoogleTidyModule.cpp +++ clang-tidy/google/GoogleTidyModule.cpp @@ -21,6 +21,7 @@ #include "GlobalNamesInHeadersCheck.h" #include "IntegerTypesCheck.h" #include "NonConstReferences.h" +#include "ObjcForbiddenSubclassingCheck.h" #include "OverloadedUnaryAndCheck.h" #include "StringReferenceMemberCheck.h" #include "TodoCommentCheck.h" @@ -46,6 +47,8 @@ "google-default-arguments"); CheckFactories.registerCheck( "google-explicit-constructor"); + CheckFactories.registerCheck( + "google-objc-forbidden-subclassing"); CheckFactories.registerCheck( "google-runtime-int"); CheckFactories.registerCheck( Index: clang-tidy/google/ObjcForbiddenSubclassingCheck.h =================================================================== --- /dev/null +++ clang-tidy/google/ObjcForbiddenSubclassingCheck.h @@ -0,0 +1,48 @@ +//===--- ObjcForbiddenSubclassingCheck.h - clang-tidy--------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_APPLE_OBJC_FORBIDDEN_SUBCLASSING_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_APPLE_OBJC_FORBIDDEN_SUBCLASSING_H + +#include "../ClangTidy.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace clang { +namespace tidy { +namespace google { + +/// Container type to hold the names of superclasses which must not be +/// subclassed. +/// +/// Note that this holds std::string and not StringRef, since the +/// underlying memory to which a StringRef points might be reclaimed +/// after we store it in this container. +typedef llvm::SmallSet ForbiddenSuperclassNamesSetType; + +/// Finds Objective-C classes which have a superclass which is +/// documented to not support subclassing. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/google-objc-forbidden-subclassing.html +class ObjcForbiddenSubclassingCheck : public ClangTidyCheck { +public: + ObjcForbiddenSubclassingCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + ForbiddenSuperclassNamesSetType ForbiddenSuperclassNames; +}; + +} // namespace google +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_APPLE_OBJC_FORBIDDEN_SUBCLASSING_H Index: clang-tidy/google/ObjcForbiddenSubclassingCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/google/ObjcForbiddenSubclassingCheck.cpp @@ -0,0 +1,82 @@ +//===--- ObjcForbiddenSubclassingCheck.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 "ObjcForbiddenSubclassingCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace google { + +namespace { + +static const StringRef DefaultForbiddenObjcClassNames = + "ABNewPersonViewController,ABPeoplePickerNavigationController," + "ABPersonViewController,ABUnknownPersonViewController,NSHashTable," + "NSMapTable,NSPointerArray,NSPointerFunctions,NSTimer,UIActionSheet," + "UIAlertView,UIImagePickerController,UITextInputMode,UIWebView"; + +void parseOptionAndAddForbiddenSuperclassNames( + StringRef ForbiddenSuperclassOption, + ForbiddenSuperclassNamesSetType &ForbiddenSuperclassNames) { + SmallVector ForbiddenSuperclassOptionNames; + ForbiddenSuperclassOption.split(ForbiddenSuperclassOptionNames, ','); + for (StringRef ForbiddenSuperclass : ForbiddenSuperclassOptionNames) { + // It's important here to convert from StringRef to std::string by + // calling str() when storing the result of trim() inside + // ForbiddenSuperclassNames. + // + // Otherwise, if we stored StringRef inside ForbiddenSuperclassNames + // directly, the memory to which StringRef points will be + // reclaimed and re-used by other objects. + ForbiddenSuperclassNames.insert(ForbiddenSuperclass.trim().str()); + } +} + +} + +ObjcForbiddenSubclassingCheck::ObjcForbiddenSubclassingCheck( + StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) { + parseOptionAndAddForbiddenSuperclassNames( + Options.get("ClassNames", DefaultForbiddenObjcClassNames), + ForbiddenSuperclassNames); +} + +void ObjcForbiddenSubclassingCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(objcInterfaceDecl().bind("objc_interface"), this); +} + +void ObjcForbiddenSubclassingCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs( + "objc_interface"); + for (auto *SuperClass = MatchedDecl->getSuperClass(); + SuperClass != nullptr; + SuperClass = SuperClass->getSuperClass()) { + if (ForbiddenSuperclassNames.count( + SuperClass->getIdentifier()->getName().str())) { + diag(MatchedDecl->getLocation(), + "Objective-C interface %0 subclasses %1, which is not " + "intended to be subclassed") + << MatchedDecl + << SuperClass; + } + } +} + +} // namespace google +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -57,6 +57,12 @@ Improvements to clang-tidy -------------------------- +- New `google-objc-forbidden-subclassing + `_ check + + Ensures Objective-C classes do not subclass any classes which are + not intended to be subclassed. + - Renamed checks to use correct term "implicit conversion" instead of "implicit cast" and modified messages and option names accordingly: Index: docs/clang-tidy/checks/google-objc-forbidden-subclassing.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/google-objc-forbidden-subclassing.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - google-objc-forbidden-subclassing + +google-objc-forbidden-subclassing +================================== + +Finds Objective-C classes which are subclasses of classes which are +not designed to be subclassed. + +By default, includes a list of Objective-C subclassing which +are publicly documented as not supporting subclassing. + +.. note:: + + In code under your control, you should instead add + ``__attribute__((objc_subclassing_restricted))`` before your ``@interface`` declaration. + See https://clang.llvm.org/docs/AttributeReference.html#objc-subclassing-restricted + +Options +------- + +.. option:: ClassNames + + Comma-separated list of names of Objective-C classes which + do not support subclassing. + + Defaults to ``ABNewPersonViewController,ABPeoplePickerNavigationController,ABPersonViewController,ABUnknownPersonViewController,NSHashTable,NSMapTable,NSPointerArray,NSPointerFunctions,NSTimer,UIActionSheet,UIAlertView,UIImagePickerController,UITextInputMode,UIWebView``. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -7,9 +7,9 @@ android-cloexec-accept android-cloexec-accept4 android-cloexec-creat + android-cloexec-dup android-cloexec-epoll-create android-cloexec-epoll-create1 - android-cloexec-dup android-cloexec-fopen android-cloexec-inotify-init android-cloexec-inotify-init1 @@ -38,7 +38,7 @@ cert-msc30-c (redirects to cert-msc50-cpp) cert-msc50-cpp cert-oop11-cpp (redirects to misc-move-constructor-init) - cppcoreguidelines-c-copy-assignment-signature + cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) cppcoreguidelines-interfaces-global-init cppcoreguidelines-no-malloc cppcoreguidelines-owning-memory @@ -60,6 +60,7 @@ google-default-arguments google-explicit-constructor google-global-names-in-headers + google-objc-forbidden-subclassing google-readability-braces-around-statements (redirects to readability-braces-around-statements) google-readability-casting google-readability-function-size (redirects to readability-function-size) @@ -76,8 +77,8 @@ hicpp-explicit-conversions (redirects to google-explicit-constructor) hicpp-function-size (redirects to readability-function-size) hicpp-invalid-access-moved (redirects to misc-use-after-move) - hicpp-move-const-arg (redirects to misc-move-const-arg) hicpp-member-init (redirects to cppcoreguidelines-pro-type-member-init) + hicpp-move-const-arg (redirects to misc-move-const-arg) hicpp-named-parameter (redirects to readability-named-parameter) hicpp-new-delete-operators (redirects to misc-new-delete-overloads) hicpp-no-array-decay (redirects to cppcoreguidelines-pro-bounds-array-to-pointer-decay) @@ -95,7 +96,7 @@ hicpp-use-noexcept (redirects to modernize-use-noexcept) hicpp-use-nullptr (redirects to modernize-use-nullptr) hicpp-use-override (redirects to modernize-use-override) - hicpp-vararg (redirects to cppcoreguidelines-pro-type-varg) + hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) llvm-header-guard llvm-include-order llvm-namespace-comment Index: test/clang-tidy/google-objc-forbidden-subclassing-custom.m =================================================================== --- /dev/null +++ test/clang-tidy/google-objc-forbidden-subclassing-custom.m @@ -0,0 +1,39 @@ +// RUN: %check_clang_tidy %s google-objc-forbidden-subclassing %t \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: google-objc-forbidden-subclassing.ClassNames, value: "Foo,Quux"}]}' \ +// RUN: -- + +@interface UIImagePickerController +@end + +// Make sure custom config options replace (not add to) the default list. +@interface Waldo : UIImagePickerController +// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Waldo' subclasses 'UIImagePickerController', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +@interface Foo +@end + +@interface Bar : Foo +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Bar' subclasses 'Foo', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +// Check subclasses of subclasses. +@interface Baz : Bar +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Baz' subclasses 'Foo', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +@interface Quux +@end + +// Check that more than one forbidden superclass can be specified. +@interface Xyzzy : Quux +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Xyzzy' subclasses 'Quux', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +@interface Plugh +@end + +@interface Corge : Plugh +// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Corge' subclasses 'Plugh', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end Index: test/clang-tidy/google-objc-forbidden-subclassing.m =================================================================== --- /dev/null +++ test/clang-tidy/google-objc-forbidden-subclassing.m @@ -0,0 +1,21 @@ +// RUN: %check_clang_tidy %s google-objc-forbidden-subclassing %t + +@interface UIImagePickerController +@end + +@interface Foo : UIImagePickerController +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Foo' subclasses 'UIImagePickerController', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +// Check subclasses of subclasses. +@interface Bar : Foo +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Bar' subclasses 'UIImagePickerController', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end + +@interface Baz +@end + +// Make sure innocent subclasses aren't caught by the check. +@interface Blech : Baz +// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Blech' subclasses 'Baz', which is not intended to be subclassed [google-objc-forbidden-subclassing] +@end