add new check for property declaration

Summary:
This check finds property declarations in Objective-C files that do not follow the pattern of property names in Apple's programming guide. The property name should be in the format of Lower Camel Case or with some particular acronyms as prefix.

Example:
@property(nonatomic, assign) int lowerCamelCase;

@property(nonatomic, strong) NSString *URLString;

Test plan:  ninja check-clang-tools

Reviewers: benhamilton, hokein

Reviewed By: hokein

Subscribers: cfe-commits, mgorny

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

llvm-svn: 318117
diff --git a/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp
new file mode 100644
index 0000000..e47b241
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp
@@ -0,0 +1,115 @@
+//===--- PropertyDeclarationCheck.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 "PropertyDeclarationCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Regex.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+namespace {
+/// The acronyms are from
+/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
+constexpr char DefaultSpecialAcronyms[] =
+    "ASCII;"
+    "PDF;"
+    "XML;"
+    "HTML;"
+    "URL;"
+    "RTF;"
+    "HTTP;"
+    "TIFF;"
+    "JPG;"
+    "PNG;"
+    "GIF;"
+    "LZW;"
+    "ROM;"
+    "RGB;"
+    "CMYK;"
+    "MIDI;"
+    "FTP";
+
+/// For now we will only fix 'CamelCase' property to
+/// '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));
+  }
+  return FixItHint();
+}
+
+std::string validPropertyNameRegex(const std::vector<std::string> &Prefixes) {
+  std::vector<std::string> EscapedPrefixes;
+  EscapedPrefixes.reserve(Prefixes.size());
+  // In case someone defines a custom prefix which includes a regex
+  // special character, escape all the prefixes.
+  std::transform(Prefixes.begin(), Prefixes.end(),
+                 std::back_inserter(EscapedPrefixes), [](const std::string& s) {
+                   return llvm::Regex::escape(s); });
+  // Allow any of these names:
+  // foo
+  // fooBar
+  // url
+  // urlString
+  // URL
+  // URLString
+  return std::string("::((") +
+      llvm::join(EscapedPrefixes.begin(), EscapedPrefixes.end(), "|") +
+      ")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*$";
+}
+}  // namespace
+
+PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      SpecialAcronyms(utils::options::parseStringList(
+          Options.get("Acronyms", DefaultSpecialAcronyms))) {}
+
+void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      objcPropertyDecl(
+          // the property name should be in Lower Camel Case like
+          // 'lowerCamelCase'
+          unless(matchesName(validPropertyNameRegex(SpecialAcronyms))))
+          .bind("property"),
+      this);
+}
+
+void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl =
+      Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
+  assert(MatchedDecl->getName().size() > 0);
+  diag(MatchedDecl->getLocation(),
+       "property name '%0' should use lowerCamelCase style, according to "
+       "the Apple Coding Guidelines")
+      << MatchedDecl->getName() << generateFixItHint(MatchedDecl);
+}
+
+void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Acronyms",
+                utils::options::serializeStringList(SpecialAcronyms));
+}
+
+}  // namespace objc
+}  // namespace tidy
+}  // namespace clang