dgatwood updated this revision to Diff 218953.
dgatwood marked 7 inline comments as done.
dgatwood added a comment.

Fixed a couple of variable names in the .h file, renamed 
kCustomCategoryMethodIdentifier to CustomCategoryMethodIdentifier, and switched 
to llvm::any_of.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65917/new/

https://reviews.llvm.org/D65917

Files:
  clang-tools-extra/clang-tidy/google/CMakeLists.txt
  clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
  clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.cpp
  clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/google-objc-require-category-method-prefixes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/google-objc-require-category-method-prefixes.m

Index: clang-tools-extra/test/clang-tidy/google-objc-require-category-method-prefixes.m
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/google-objc-require-category-method-prefixes.m
@@ -0,0 +1,35 @@
+// RUN: %check_clang_tidy %s google-objc-require-category-method-prefixes %t -config="{CheckOptions: [{key: google-objc-require-category-method-prefixes.ExpectedPrefixes, value: GMO}]}"
+
+@class NSString;
+
+@interface NSURL
++ (nullable instancetype)URLWithString:(NSString *)URLString;
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+@interface NSURL (CustomExtension)
+
+- (void)unprefixedMethod;
+- (void)unprefixed;
+- (void)justprefixed_;
+
+@end
+
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: the category method 'unprefixedMethod' is not properly prefixed [google-objc-require-category-method-prefixes]
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: the category method 'unprefixed' is not properly prefixed [google-objc-require-category-method-prefixes]
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: the category method 'justprefixed_' is not properly prefixed [google-objc-require-category-method-prefixes]
+
+@interface NSURL (CustomExtension2)
+- (void)gmo_prefixedMethod;
+@end
+
+@interface GMOURL
++ (nullable instancetype)URLWithString:(NSString *)URLString;
++ (instancetype)alloc;
+- (instancetype)init;
+@end
+
+@interface GMOURL (CustomExtension3)
+- (void)unprefixedMethodInClassWithExpectedPrefix;
+@end
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -230,6 +230,7 @@
    google-objc-avoid-throwing-exception
    google-objc-function-naming
    google-objc-global-variable-declaration
+   google-objc-require-category-method-prefixes
    google-readability-avoid-underscore-in-googletest-name
    google-readability-braces-around-statements (redirects to readability-braces-around-statements) <google-readability-braces-around-statements>
    google-readability-casting
Index: clang-tools-extra/docs/clang-tidy/checks/google-objc-require-category-method-prefixes.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/google-objc-require-category-method-prefixes.rst
@@ -0,0 +1,44 @@
+.. title:: clang-tidy - google-objc-require-category-method-prefixes
+
+google-objc-require-category-method-prefixes
+============================================
+
+Warns when Objective-C category method names are not properly prefixed (e.g.
+``gmo_methodName``) unless the category is extending a class with an
+expected prefix (configurable).
+
+The Google Objective-C style guide requires
+`prefixes for methods http://go/objc-style#Category_Names`_ in categories on
+classes that you don't control (for example, categories on Apple or third-party
+framework classes or classes created by other teams) to prevent name collisions
+when those frameworks are updated.
+
+This checker ensures that all methods in categories have some sort of prefix
+(e.g. ``gmo_``). It excludes categories on classes whose names have a
+whitelisted three-letter prefix.
+
+For example, the following code sample is a properly prefixed method on a
+non-owned class (``NSObject``):
+
+.. code-block:: objc
+  @interface NSObject (QEDMyCategory)
+  - (BOOL)qed_myCustomMethod;
+  @end
+
+If you whitelist the ``QED`` three-letter prefix, the following code sample
+is also allowed:
+
+.. code-block:: objc
+
+  @interface QEDMyClass (MyCategory)
+  - (BOOL)myCustomMethod;
+  @end
+
+Options
+-------
+
+.. option:: ExpectedPrefixes
+
+   A semicolon-delimited list of class name prefixes.  Methods in categories
+   that extend classes whose names begin with any of these prefixes are exempt
+   from the method name prefixing requirement.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -73,11 +73,12 @@
   Finds instances where variables with static storage are initialized
   dynamically in header files.
 
-- New :doc:`linuxkernel-must-use-errs
-  <clang-tidy/checks/linuxkernel-must-use-errs>` check.
+- New :doc:`google-objc-require-category-method-prefixes
+  <clang-tidy/checks/google-objc-require-category-method-prefixes>` check.
 
-  Checks Linux kernel code to see if it uses the results from the functions in
-  ``linux/err.h``.
+  Warns when Objective-C category method names are not properly prefixed (e.g.
+  ``gmo_methodName``) unless the category is extending a class with a
+  (configurable) whitelisted prefix.
 
 - New :doc:`google-upgrade-googletest-case
   <clang-tidy/checks/google-upgrade-googletest-case>` check.
@@ -85,6 +86,12 @@
   Finds uses of deprecated Googletest APIs with names containing ``case`` and
   replaces them with equivalent APIs with ``suite``.
 
+- New :doc:`linuxkernel-must-use-errs
+  <clang-tidy/checks/linuxkernel-must-use-errs>` check.
+
+  Checks Linux kernel code to see if it uses the results from the functions in
+  ``linux/err.h``.
+
 - New :doc:`llvm-prefer-register-over-unsigned
   <clang-tidy/checks/llvm-prefer-register-over-unsigned>` check.
 
Index: clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.h
@@ -0,0 +1,38 @@
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_REQUIRECATEGORYMETHODPREFIXES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_REQUIRECATEGORYMETHODPREFIXES_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace objc {
+
+// Warn about missing prefixes in category method names.
+class RequireCategoryMethodPrefixesCheck : public ClangTidyCheck {
+public:
+  RequireCategoryMethodPrefixesCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        ExpectedPrefixes(Options.get("ExpectedPrefixes", "")) {}
+
+  /// Make configuration of checker discoverable.
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  /// Semicolon-separated list of whitelisted class prefixes.
+  /// Defaults to empty.
+  const std::string ExpectedPrefixes;
+
+  bool matchesExpectedPrefix(StringRef ClassName);
+};
+
+} // namespace objc
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_REQUIRECATEGORYMETHODPREFIXES_H
Index: clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.cpp
@@ -0,0 +1,97 @@
+#include "RequireCategoryMethodPrefixesCheck.h"
+
+#include "../ClangTidyOptions.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers; // NOLINT: Too many names.
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace objc {
+
+static const char *CustomCategoryMethodIdentifier = "ThisIsACategoryMethod";
+
+void RequireCategoryMethodPrefixesCheck::registerMatchers(MatchFinder *Finder) {
+  // This check should be run only on Objective-C code.
+  if (!getLangOpts().ObjC)
+    return;
+
+  Finder->addMatcher(objcMethodDecl().bind(CustomCategoryMethodIdentifier),
+      this);
+}
+
+void RequireCategoryMethodPrefixesCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ExpectedPrefixes", ExpectedPrefixes);
+}
+
+bool RequireCategoryMethodPrefixesCheck::matchesExpectedPrefix(
+    StringRef ClassName) {
+  std::vector<std::string> PrefixArray =
+      utils::options::parseStringList(ExpectedPrefixes);
+
+  return llvm::any_of(PrefixArray,
+      [ClassName](const std::string &Str) {
+          return ClassName.startswith(Str);
+      });
+}
+
+void RequireCategoryMethodPrefixesCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const ObjCMethodDecl *MethodDeclaration =
+      Result.Nodes.getNodeAs<ObjCMethodDecl>(CustomCategoryMethodIdentifier);
+  if (!MethodDeclaration)
+    return;
+
+  // We cannot safely use getName here, because it will often fail with
+  // the error "Name is not a simple identifier", and getIdentifier() returns
+  // nullptr.
+  StringRef MethodName = MethodDeclaration->getNameAsString();
+  const ObjCInterfaceDecl *OwningObjCClassInterface =
+      MethodDeclaration->getClassInterface();
+  if (!OwningObjCClassInterface)
+    return;
+
+  StringRef ClassName = OwningObjCClassInterface->getName();
+
+  const auto *OwningObjCCategory =
+      dyn_cast<ObjCCategoryDecl>(MethodDeclaration->getDeclContext());
+  // Bail early if the method is not in a category.
+  if (!OwningObjCCategory || OwningObjCCategory->isInvalidDecl())
+    return;
+
+  // Bail early if the method is in a category on a class with a whitelisted
+  // prefix.
+  if (matchesExpectedPrefix(ClassName))
+    return;
+
+  // Check whether the method name has a proper prefix.
+  for (const char *pos = MethodName.str().c_str(); pos && *pos; ++pos) {
+    // The characters a-z are allowed in a prefix.  Go to the next
+    // character.
+    if (*pos >= 'a' && *pos <= 'z')
+      continue;
+
+    // The underscore character (_) terminates the prefix.  As long as it is
+    // not the last character in the name, the declaration passes.
+    if (*pos == '_' && *(pos + 1) != '\0')
+      return;
+
+    // The name does not start with /[a-z]+_/.  This declaration fails.
+    break;
+  }
+  // If the end of the string is reached or an illegal character appears
+  // before the terminating underscore, then the method name is not
+  // properly prefixed.  Throw an error.
+  diag(MethodDeclaration->getBeginLoc(),
+       "the category method %0 is not properly prefixed")
+      << MethodDeclaration;
+}
+
+} // namespace objc
+} // namespace google
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
+++ clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
@@ -25,6 +25,7 @@
 #include "IntegerTypesCheck.h"
 #include "NonConstReferences.h"
 #include "OverloadedUnaryAndCheck.h"
+#include "RequireCategoryMethodPrefixesCheck.h"
 #include "TodoCommentCheck.h"
 #include "UnnamedNamespaceInHeaderCheck.h"
 #include "UpgradeGoogletestCaseCheck.h"
@@ -59,6 +60,8 @@
         "google-objc-function-naming");
     CheckFactories.registerCheck<objc::GlobalVariableDeclarationCheck>(
         "google-objc-global-variable-declaration");
+    CheckFactories.registerCheck<objc::RequireCategoryMethodPrefixesCheck>(
+        "google-objc-require-category-method-prefixes");
     CheckFactories.registerCheck<runtime::IntegerTypesCheck>(
         "google-runtime-int");
     CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
Index: clang-tools-extra/clang-tidy/google/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/google/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/google/CMakeLists.txt
@@ -15,6 +15,7 @@
   IntegerTypesCheck.cpp
   NonConstReferences.cpp
   OverloadedUnaryAndCheck.cpp
+  RequireCategoryMethodPrefixesCheck.cpp
   TodoCommentCheck.cpp
   UnnamedNamespaceInHeaderCheck.cpp
   UpgradeGoogletestCaseCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to