[clang-tools-extra] r316744 - [clang-tidy ObjC] [3/3] New check objc-forbidden-subclassing

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 27 00:41:36 PDT 2017


Author: hokein
Date: Fri Oct 27 00:41:36 2017
New Revision: 316744

URL: http://llvm.org/viewvc/llvm-project?rev=316744&view=rev
Log:
[clang-tidy ObjC] [3/3] New check objc-forbidden-subclassing

Summary:
This is part 3 of 3 of a series of changes to improve Objective-C
linting in clang-tidy.

This adds a new clang-tidy check `objc-forbidden-subclassing` which
ensures clients do not create subclasses of Objective-C classes which
are not designed to be subclassed.

(Note that for code under your control, you should use
__attribute__((objc_subclassing_restricted)) instead -- this
is intended for third-party APIs which cannot be modified.)

By default, the following classes (which are publicly documented
as not supporting subclassing) are forbidden from subclassing:

ABNewPersonViewController
ABPeoplePickerNavigationController
ABPersonViewController
ABUnknownPersonViewController
NSHashTable
NSMapTable
NSPointerArray
NSPointerFunctions
NSTimer
UIActionSheet
UIAlertView
UIImagePickerController
UITextInputMode
UIWebView

Clients can set a CheckOption
`objc-forbidden-subclassing.ClassNames` to a semicolon-separated
list of class names, which overrides this list.

Test Plan: `ninja check-clang-tools`

Patch by Ben Hamilton!

Reviewers: hokein, alexfh

Reviewed By: hokein

Subscribers: saidinwot, Wizard, srhines, mgorny, xazax.hun

Differential Revision: https://reviews.llvm.org/D39142

Added:
    clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.cpp
    clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/objc-forbidden-subclassing.rst
    clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing-custom.m
    clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing.m
Modified:
    clang-tools-extra/trunk/clang-tidy/objc/CMakeLists.txt
    clang-tools-extra/trunk/clang-tidy/objc/ObjCTidyModule.cpp
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
    clang-tools-extra/trunk/unittests/clang-tidy/ObjCModuleTest.cpp

Modified: clang-tools-extra/trunk/clang-tidy/objc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/objc/CMakeLists.txt?rev=316744&r1=316743&r2=316744&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/objc/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/objc/CMakeLists.txt Fri Oct 27 00:41:36 2017
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyObjCModule
+  ForbiddenSubclassingCheck.cpp
   ObjCTidyModule.cpp
 
   LINK_LIBS

Added: clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.cpp?rev=316744&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.cpp Fri Oct 27 00:41:36 2017
@@ -0,0 +1,118 @@
+//===--- 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<ObjCInterfaceDecl>, 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) {
+  Finder->addMatcher(
+      objcInterfaceDecl(
+          isSubclassOf(
+              objcInterfaceDecl(
+                  hasAnyName(
+                      std::vector<StringRef>(
+                          ForbiddenSuperClassNames.begin(),
+                          ForbiddenSuperClassNames.end())))
+              .bind("superclass")))
+      .bind("subclass"),
+      this);
+}
+
+void ForbiddenSubclassingCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *SubClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(
+      "subclass");
+  assert(SubClass != nullptr);
+  const auto *SuperClass = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(
+      "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

Added: clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.h?rev=316744&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/objc/ForbiddenSubclassingCheck.h Fri Oct 27 00:41:36 2017
@@ -0,0 +1,42 @@
+//===--- ForbiddenSubclassingCheck.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_OBJC_FORBIDDEN_SUBCLASSING_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_FORBIDDEN_SUBCLASSING_CHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+/// 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/objc-forbidden-subclassing.html
+class ForbiddenSubclassingCheck : public ClangTidyCheck {
+public:
+  ForbiddenSubclassingCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+private:
+  const std::vector<std::string> ForbiddenSuperClassNames;
+};
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_FORBIDDEN_SUBCLASSING_CHECK_H

Modified: clang-tools-extra/trunk/clang-tidy/objc/ObjCTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/objc/ObjCTidyModule.cpp?rev=316744&r1=316743&r2=316744&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/objc/ObjCTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/objc/ObjCTidyModule.cpp Fri Oct 27 00:41:36 2017
@@ -10,6 +10,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "ForbiddenSubclassingCheck.h"
 
 using namespace clang::ast_matchers;
 
@@ -20,7 +21,8 @@ namespace objc {
 class ObjCModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
-    // TODO(D39142): Add checks here.
+    CheckFactories.registerCheck<ForbiddenSubclassingCheck>(
+        "objc-forbidden-subclassing");
   }
 };
 

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=316744&r1=316743&r2=316744&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Oct 27 00:41:36 2017
@@ -59,6 +59,12 @@ Improvements to clang-tidy
 
 - New module `objc` for Objective-C style checks.
 
+- New `objc-forbidden-subclassing
+  <http://clang.llvm.org/extra/clang-tidy/checks/objc-forbidden-subclassing.html>`_ 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:
 

Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=316744&r1=316743&r2=316744&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Oct 27 00:41:36 2017
@@ -7,9 +7,9 @@ Clang-Tidy Checks
    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 @@ Clang-Tidy Checks
    cert-msc30-c (redirects to cert-msc50-cpp) <cert-msc30-c>
    cert-msc50-cpp
    cert-oop11-cpp (redirects to misc-move-constructor-init) <cert-oop11-cpp>
-   cppcoreguidelines-c-copy-assignment-signature
+   cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature>
    cppcoreguidelines-interfaces-global-init
    cppcoreguidelines-no-malloc
    cppcoreguidelines-owning-memory
@@ -76,8 +76,8 @@ Clang-Tidy Checks
    hicpp-explicit-conversions (redirects to google-explicit-constructor) <hicpp-explicit-conversions>
    hicpp-function-size (redirects to readability-function-size) <hicpp-function-size>
    hicpp-invalid-access-moved (redirects to misc-use-after-move) <hicpp-invalid-access-moved>
-   hicpp-move-const-arg (redirects to misc-move-const-arg) <hicpp-move-const-arg>
    hicpp-member-init (redirects to cppcoreguidelines-pro-type-member-init) <hicpp-member-init>
+   hicpp-move-const-arg (redirects to misc-move-const-arg) <hicpp-move-const-arg>
    hicpp-named-parameter (redirects to readability-named-parameter) <hicpp-named-parameter>
    hicpp-new-delete-operators (redirects to misc-new-delete-overloads) <hicpp-new-delete-operators>
    hicpp-no-array-decay (redirects to cppcoreguidelines-pro-bounds-array-to-pointer-decay) <hicpp-no-array-decay>
@@ -95,7 +95,7 @@ Clang-Tidy Checks
    hicpp-use-noexcept (redirects to modernize-use-noexcept) <hicpp-use-noexcept>
    hicpp-use-nullptr (redirects to modernize-use-nullptr) <hicpp-use-nullptr>
    hicpp-use-override (redirects to modernize-use-override) <hicpp-use-override>
-   hicpp-vararg (redirects to cppcoreguidelines-pro-type-varg) <hicpp-vararg>
+   hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) <hicpp-vararg>
    llvm-header-guard
    llvm-include-order
    llvm-namespace-comment
@@ -180,6 +180,7 @@ Clang-Tidy Checks
    performance-type-promotion-in-math-fn
    performance-unnecessary-copy-initialization
    performance-unnecessary-value-param
+   objc-forbidden-subclassing
    readability-avoid-const-params-in-decls
    readability-braces-around-statements
    readability-container-size-empty

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/objc-forbidden-subclassing.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/objc-forbidden-subclassing.rst?rev=316744&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/objc-forbidden-subclassing.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/objc-forbidden-subclassing.rst Fri Oct 27 00:41:36 2017
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - objc-forbidden-subclassing
+
+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 classes which
+are publicly documented as not supporting subclassing.
+
+.. note::
+
+   Instead of using this check, for code under your control, you should add
+   ``__attribute__((objc_subclassing_restricted))`` before your ``@interface`` declarations
+   to ensure the compiler prevents others from subclassing your Objective-C classes.
+   See https://clang.llvm.org/docs/AttributeReference.html#objc-subclassing-restricted
+
+Options
+-------
+
+.. option:: ForbiddenSuperClassNames
+
+   Semicolon-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``.

Added: clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing-custom.m
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing-custom.m?rev=316744&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing-custom.m (added)
+++ clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing-custom.m Fri Oct 27 00:41:36 2017
@@ -0,0 +1,39 @@
+// RUN: %check_clang_tidy %s objc-forbidden-subclassing %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: objc-forbidden-subclassing.ClassNames, value: "Foo;Quux"}]}' \
+// RUN: --
+
+ at interface UIImagePickerController
+ at end
+
+// Make sure custom config options replace (not add to) the default list.
+ at interface Waldo : UIImagePickerController
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Waldo' subclasses 'UIImagePickerController', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+ at interface Foo
+ at end
+
+ at interface Bar : Foo
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Bar' subclasses 'Foo', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+// Check subclasses of subclasses.
+ at interface Baz : Bar
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Baz' subclasses 'Foo', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+ at interface Quux
+ at end
+
+// Check that more than one forbidden superclass can be specified.
+ at interface Xyzzy : Quux
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Xyzzy' subclasses 'Quux', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+ at interface Plugh
+ at end
+
+ at interface Corge : Plugh
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Corge' subclasses 'Plugh', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end

Added: clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing.m
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing.m?rev=316744&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing.m (added)
+++ clang-tools-extra/trunk/test/clang-tidy/objc-forbidden-subclassing.m Fri Oct 27 00:41:36 2017
@@ -0,0 +1,21 @@
+// RUN: %check_clang_tidy %s objc-forbidden-subclassing %t
+
+ at interface UIImagePickerController
+ at end
+
+ at interface Foo : UIImagePickerController
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Foo' subclasses 'UIImagePickerController', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+// Check subclasses of subclasses.
+ at interface Bar : Foo
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Objective-C interface 'Bar' subclasses 'UIImagePickerController', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end
+
+ at interface Baz
+ at end
+
+// Make sure innocent subclasses aren't caught by the check.
+ at interface Blech : Baz
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: Objective-C interface 'Blech' subclasses 'Baz', which is not intended to be subclassed [objc-forbidden-subclassing]
+ at end

Modified: clang-tools-extra/trunk/unittests/clang-tidy/ObjCModuleTest.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/ObjCModuleTest.cpp?rev=316744&r1=316743&r2=316744&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clang-tidy/ObjCModuleTest.cpp (original)
+++ clang-tools-extra/trunk/unittests/clang-tidy/ObjCModuleTest.cpp Fri Oct 27 00:41:36 2017
@@ -9,12 +9,40 @@
 
 #include "ClangTidyTest.h"
 #include "gtest/gtest.h"
+#include "objc/ForbiddenSubclassingCheck.h"
+
+using namespace clang::tidy::objc;
 
 namespace clang {
 namespace tidy {
 namespace test {
 
-// TODO(D39142) Add unit tests for the ObjC module here once a check lands.
+TEST(ObjCForbiddenSubclassing, AllowedSubclass) {
+  std::vector<ClangTidyError> Errors;
+  runCheckOnCode<ForbiddenSubclassingCheck>(
+      "@interface Foo\n"
+      "@end\n"
+      "@interface Bar : Foo\n"
+      "@end\n",
+      &Errors,
+      "input.m");
+  EXPECT_EQ(0ul, Errors.size());
+}
+
+TEST(ObjCForbiddenSubclassing, ForbiddenSubclass) {
+  std::vector<ClangTidyError> Errors;
+  runCheckOnCode<ForbiddenSubclassingCheck>(
+      "@interface UIImagePickerController\n"
+      "@end\n"
+      "@interface Foo : UIImagePickerController\n"
+      "@end\n",
+      &Errors,
+      "input.m");
+  EXPECT_EQ(1ul, Errors.size());
+  EXPECT_EQ(
+      "Objective-C interface 'Foo' subclasses 'UIImagePickerController', which is not intended to be subclassed",
+      Errors[0].Message.Message);
+}
 
 } // namespace test
 } // namespace tidy




More information about the cfe-commits mailing list