clang-tools  8.0.0
PropertyDeclarationCheck.cpp
Go to the documentation of this file.
1 //===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include <algorithm>
12 #include "../utils/OptionsUtils.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/Basic/CharInfo.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/Support/Regex.h"
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang {
23 namespace tidy {
24 namespace objc {
25 
26 namespace {
27 
28 // For StandardProperty the naming style is 'lowerCamelCase'.
29 // For CategoryProperty especially in categories of system class,
30 // to avoid naming conflict, the suggested naming style is
31 // 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
32 // Regardless of the style, all acronyms and initialisms should be capitalized.
34  StandardProperty = 1,
35  CategoryProperty = 2,
36 };
37 
38 /// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
39 /// 'camelCase' or 'abc_camelCase'. For other cases the users need to
40 /// come up with a proper name by their own.
41 /// FIXME: provide fix for snake_case to snakeCase
42 FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
43  auto Name = Decl->getName();
44  auto NewName = Decl->getName().str();
45  size_t Index = 0;
46  if (Style == CategoryProperty) {
47  Index = Name.find_first_of('_') + 1;
48  NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
49  }
50  if (Index < Name.size()) {
51  NewName[Index] = tolower(NewName[Index]);
52  if (NewName != Name) {
53  return FixItHint::CreateReplacement(
54  CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
55  llvm::StringRef(NewName));
56  }
57  }
58  return FixItHint();
59 }
60 
61 std::string validPropertyNameRegex(bool UsedInMatcher) {
62  // Allow any of these names:
63  // foo
64  // fooBar
65  // url
66  // urlString
67  // ID
68  // IDs
69  // URL
70  // URLString
71  // bundleID
72  // CIColor
73  //
74  // Disallow names of this form:
75  // LongString
76  //
77  // aRbITRaRyCapS is allowed to avoid generating false positives for names
78  // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.
79  std::string StartMatcher = UsedInMatcher ? "::" : "^";
80  return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
81 }
82 
83 bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
84  auto RegexExp = llvm::Regex("^[a-zA-Z]+_[a-zA-Z0-9][a-zA-Z0-9_]+$");
85  return RegexExp.match(PropertyName);
86 }
87 
88 bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
89  size_t Start = PropertyName.find_first_of('_');
90  assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
91  auto Prefix = PropertyName.substr(0, Start);
92  if (Prefix.lower() != Prefix) {
93  return false;
94  }
95  auto RegexExp =
96  llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
97  return RegexExp.match(PropertyName.substr(Start + 1));
98 }
99 } // namespace
100 
101 PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
102  ClangTidyContext *Context)
103  : ClangTidyCheck(Name, Context),
104  SpecialAcronyms(
105  utils::options::parseStringList(Options.get("Acronyms", ""))),
106  IncludeDefaultAcronyms(Options.get("IncludeDefaultAcronyms", true)),
107  EscapedAcronyms() {}
108 
110  // this check should only be applied to ObjC sources.
111  if (!getLangOpts().ObjC) return;
112 
113  Finder->addMatcher(
114  objcPropertyDecl(
115  // the property name should be in Lower Camel Case like
116  // 'lowerCamelCase'
117  unless(matchesName(validPropertyNameRegex(true))))
118  .bind("property"),
119  this);
120 }
121 
122 void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
123  const auto *MatchedDecl =
124  Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
125  assert(MatchedDecl->getName().size() > 0);
126  auto *DeclContext = MatchedDecl->getDeclContext();
127  auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);
128 
129  if (CategoryDecl != nullptr &&
130  hasCategoryPropertyPrefix(MatchedDecl->getName())) {
131  if (!prefixedPropertyNameValid(MatchedDecl->getName()) ||
132  CategoryDecl->IsClassExtension()) {
133  NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
134  : CategoryProperty;
135  diag(MatchedDecl->getLocation(),
136  "property name '%0' not using lowerCamelCase style or not prefixed "
137  "in a category, according to the Apple Coding Guidelines")
138  << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);
139  }
140  return;
141  }
142  diag(MatchedDecl->getLocation(),
143  "property name '%0' not using lowerCamelCase style or not prefixed in "
144  "a category, according to the Apple Coding Guidelines")
145  << MatchedDecl->getName()
146  << generateFixItHint(MatchedDecl, StandardProperty);
147 }
148 
150  Options.store(Opts, "Acronyms",
151  utils::options::serializeStringList(SpecialAcronyms));
152  Options.store(Opts, "IncludeDefaultAcronyms", IncludeDefaultAcronyms);
153 }
154 
155 } // namespace objc
156 } // namespace tidy
157 } // namespace clang
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
const SymbolIndex * Index
Definition: Dexp.cpp:85