Wizard created this revision.
Herald added subscribers: cfe-commits, klimek.

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D42464

Files:
  clang-tidy/objc/PropertyDeclarationCheck.cpp
  docs/clang-tidy/checks/objc-property-declaration.rst
  test/clang-tidy/objc-property-declaration.m

Index: test/clang-tidy/objc-property-declaration.m
===================================================================
--- test/clang-tidy/objc-property-declaration.m
+++ test/clang-tidy/objc-property-declaration.m
@@ -11,3 +11,9 @@
 @property(strong, nonatomic) NSString *URL_string;
 // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: property name 'URL_string' should use lowerCamelCase style, according to the Apple Coding Guidelines [objc-property-declaration]
 @end
+
+@interface Foo (Bar)
+@property(assign, nonatomic) int abc_NotCamelCase;
+// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: property name 'abc_NotCamelCase' not using lowerCamelCase style or not prefixed in a category, according to the Apple Coding Guidelines [objc-property-declaration]
+@property(assign, nonatomic) int abc_camelCase;
+@end
\ No newline at end of file
Index: docs/clang-tidy/checks/objc-property-declaration.rst
===================================================================
--- docs/clang-tidy/checks/objc-property-declaration.rst
+++ docs/clang-tidy/checks/objc-property-declaration.rst
@@ -32,6 +32,15 @@
 
 The corresponding style rule: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-1001757
 
+The check will also accept property declared in category with a prefix of
+lowercase letters followed by a '_' to avoid naming conflict. For example:
+
+.. code-block:: objc
+@property(nonatomic, assign) int abc_lowerCamelCase;
+
+The corresponding style rule: https://developer.apple.com/library/content/qa/qa1908/_index.html
+
+
 Options
 -------
 
Index: clang-tidy/objc/PropertyDeclarationCheck.cpp
===================================================================
--- clang-tidy/objc/PropertyDeclarationCheck.cpp
+++ clang-tidy/objc/PropertyDeclarationCheck.cpp
@@ -8,12 +8,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "PropertyDeclarationCheck.h"
+#include <algorithm>
 #include "../utils/OptionsUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/CharInfo.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Regex.h"
-#include <algorithm>
 
 using namespace clang::ast_matchers;
 
@@ -48,13 +49,28 @@
 /// 'camelCase'. For other cases the users need to
 /// come up with a proper name by their own.
 /// FIXME: provide fix for snake_case to snakeCase
-FixItHint generateFixItHint(const ObjCPropertyDecl *Decl) {
-  if (isupper(Decl->getName()[0])) {
-    auto NewName = Decl->getName().str();
-    NewName[0] = tolower(NewName[0]);
-    return FixItHint::CreateReplacement(
-        CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
-        llvm::StringRef(NewName));
+FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, bool isCategory) {
+  auto Name = Decl->getName();
+  auto NewName = Decl->getName().str();
+  size_t Index = 0;
+  if (isCategory) {
+    for (size_t i = 0; i < Name.size(); ++i) {
+      if (Name[i] == '_') {
+        Index = i + 1;
+        break;
+      }
+      if (isUppercase(Name[i])) {
+        NewName[i] = tolower(Name[i]);
+      }
+    }
+  }
+  if (Index < Name.size()) {
+    NewName[Index] = tolower(NewName[Index]);
+    if (NewName != Name) {
+      return FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
+          llvm::StringRef(NewName));
+    }
   }
   return FixItHint();
 }
@@ -65,8 +81,8 @@
   // In case someone defines a custom prefix which includes a regex
   // special character, escape all the prefixes.
   std::transform(Acronyms.begin(), Acronyms.end(),
-                 std::back_inserter(EscapedAcronyms), [](const std::string& s) {
-                   return llvm::Regex::escape(s); });
+                 std::back_inserter(EscapedAcronyms),
+                 [](const std::string &s) { return llvm::Regex::escape(s); });
   // Allow any of these names:
   // foo
   // fooBar
@@ -76,9 +92,33 @@
   // URLString
   // bundleID
   return std::string("::((") +
-      llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") +
-      ")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*" + "(" +
-      llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") + ")?$";
+         llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") +
+         ")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*" + "(" +
+         llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") +
+         ")?$";
+}
+
+bool hasCategoryPropertyPrefix(const llvm::StringRef &PropertyName) {
+  for (size_t i = 0; i < PropertyName.size() - 1; ++i) {
+    if (PropertyName[i] == '_') {
+      return true;
+    }
+    if (!llvm::isAlpha(PropertyName[i])) {
+      return false;
+    }
+  }
+  return false;
+}
+
+bool prefixedPropertyNameMatches(const llvm::StringRef &PropertyName,
+                                 const std::vector<std::string> &Acronyms) {
+  size_t Start = 0;
+  while (PropertyName[Start] != '_') {
+    Start++;
+  }
+  auto RegexExp = llvm::Regex(
+      llvm::StringRef(validPropertyNameRegex(Acronyms).replace(0, 2, "^")));
+  return RegexExp.match(llvm::StringRef(PropertyName.substr(Start + 1)));
 }
 }  // namespace
 
@@ -102,10 +142,24 @@
   const auto *MatchedDecl =
       Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
   assert(MatchedDecl->getName().size() > 0);
+  const auto *ParentContext = MatchedDecl->getDeclContext();
+  auto NodeKind = std::string(ParentContext->getDeclKindName());
+  if (NodeKind == "ObjCCategory" &&
+      hasCategoryPropertyPrefix(MatchedDecl->getName())) {
+    if (!prefixedPropertyNameMatches(MatchedDecl->getName(), SpecialAcronyms)) {
+      diag(MatchedDecl->getLocation(),
+           "property name '%0' not using lowerCamelCase style or not prefixed "
+           "in "
+           "a category, according to "
+           "the Apple Coding Guidelines")
+          << MatchedDecl->getName() << generateFixItHint(MatchedDecl, true);
+    }
+    return;
+  }
   diag(MatchedDecl->getLocation(),
        "property name '%0' should use lowerCamelCase style, according to "
        "the Apple Coding Guidelines")
-      << MatchedDecl->getName() << generateFixItHint(MatchedDecl);
+      << MatchedDecl->getName() << generateFixItHint(MatchedDecl, false);
 }
 
 void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to