dgatwood updated this revision to Diff 218478.
dgatwood added a comment.

Incorporated feedback.


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.WhitelistedPrefixes, 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)unprefixedMethodInWhitelistedClass;
+@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 a
+(configurable) whitelisted prefix.
+
+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:: WhitelistedPrefixes
+
+   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
@@ -67,6 +67,13 @@
 Improvements to clang-tidy
 --------------------------
 
+- New :doc:`google-objc-require-category-method-prefixes
+  <clang-tidy/checks/google-objc-require-category-method-prefixes>` check.
+
+  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:`bugprone-dynamic-static-initializers
   <clang-tidy/checks/bugprone-dynamic-static-initializers>` check.
 
Index: clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/google/RequireCategoryMethodPrefixesCheck.h
@@ -0,0 +1,39 @@
+#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),
+        WhitelistedPrefixes(Options.get("WhitelistedPrefixes", "")) {}
+
+  /// 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 WhitelistedPrefixes;
+
+  llvm::Optional<std::string>
+      matchingWhitelistedPrefix(StringRef class_name);
+};
+
+} // 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,104 @@
+#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 {
+
+namespace {
+static const char *kCustomCategoryMethodIdentifier = "ThisIsACategoryMethod";
+} // anonymous namespace
+
+void RequireCategoryMethodPrefixesCheck::registerMatchers(MatchFinder *finder) {
+  // This check should be run only on Objective-C code.
+  if (!getLangOpts().ObjC)
+    return;
+
+  finder->addMatcher(objcMethodDecl().bind(kCustomCategoryMethodIdentifier),
+      this);
+}
+
+void RequireCategoryMethodPrefixesCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WhitelistedPrefixes", WhitelistedPrefixes);
+}
+
+llvm::Optional<std::string>
+RequireCategoryMethodPrefixesCheck::matchingWhitelistedPrefix(
+    StringRef class_name) {
+  std::vector<std::string> PrefixArray =
+      utils::options::parseStringList(WhitelistedPrefixes);
+
+  for (auto &Prefix : PrefixArray) {
+    if (class_name.startswith(Prefix)) {
+      return Prefix;
+    }
+  }
+  return llvm::None;
+}
+
+void RequireCategoryMethodPrefixesCheck::check(
+    const MatchFinder::MatchResult &result) {
+  const clang::ObjCMethodDecl *method_declaration =
+      result.Nodes.getNodeAs<ObjCMethodDecl>(kCustomCategoryMethodIdentifier);
+  if (!method_declaration)
+    return;
+
+  // We cannot safely use getName here, because it will often fail with
+  // the error "Name is not a simple identifier".
+  StringRef method_name = method_declaration->getNameAsString();
+  const clang::ObjCInterfaceDecl *owning_objc_class_interface =
+      method_declaration->getClassInterface();
+  if (!owning_objc_class_interface)
+    return;
+
+  StringRef class_name = owning_objc_class_interface->getName();
+
+  const clang::ObjCCategoryDecl *OwningObjCCategory =
+      dyn_cast<clang::ObjCCategoryDecl>(method_declaration->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.
+  llvm::Optional<std::string> MatchingPrefix =
+      matchingWhitelistedPrefix(class_name);
+
+  if (MatchingPrefix.hasValue())
+    return;
+
+  // Check whether the method name has a proper prefix.
+  for (const char *pos = method_name.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(method_declaration->getBeginLoc(),
+       "the category method %0 is not properly prefixed")
+      << method_declaration;
+}
+
+} // 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