clang-tools 10.0.0

PropertyDeclarationCheck.cpp
Go to the documentation of this file.
1 //===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PropertyDeclarationCheck.h"
10 #include <algorithm>
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Basic/CharInfo.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/Support/Regex.h"
18
19 using namespace clang::ast_matchers;
20
21 namespace clang {
22 namespace tidy {
23 namespace objc {
24
25 namespace {
26
27 // For StandardProperty the naming style is 'lowerCamelCase'.
28 // For CategoryProperty especially in categories of system class,
29 // to avoid naming conflict, the suggested naming style is
30 // 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
31 // Regardless of the style, all acronyms and initialisms should be capitalized.
32 enum NamingStyle {
33 StandardProperty = 1,
34 CategoryProperty = 2,
35 };
36
37 /// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
38 /// 'camelCase' or 'abc_camelCase'. For other cases the users need to
39 /// come up with a proper name by their own.
40 /// FIXME: provide fix for snake_case to snakeCase
41 FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
42 auto Name = Decl->getName();
43 auto NewName = Decl->getName().str();
44 size_t Index = 0;
45 if (Style == CategoryProperty) {
46 Index = Name.find_first_of('_') + 1;
47 NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
48 }
49 if (Index < Name.size()) {
50 NewName[Index] = tolower(NewName[Index]);
51 if (NewName != Name) {
52 return FixItHint::CreateReplacement(
53 CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
54 llvm::StringRef(NewName));
55 }
56 }
57 return FixItHint();
58 }
59
60 std::string validPropertyNameRegex(bool UsedInMatcher) {
61 // Allow any of these names:
62 // foo
63 // fooBar
64 // url
65 // urlString
66 // ID
67 // IDs
68 // URL
69 // URLString
70 // bundleID
71 // CIColor
72 //
73 // Disallow names of this form:
74 // LongString
75 //
76 // aRbITRaRyCapS is allowed to avoid generating false positives for names
77 // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.
78 std::string StartMatcher = UsedInMatcher ? "::" : "^";
79 return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
80 }
81
82 bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
83 auto RegexExp =
84 llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[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 = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
96 return RegexExp.match(PropertyName.substr(Start + 1));
97 }
98 } // namespace
99
100 void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
101 // this check should only be applied to ObjC sources.
102 if (!getLangOpts().ObjC) return;
103
104 Finder->addMatcher(objcPropertyDecl(
105 // the property name should be in Lower Camel Case like
106 // 'lowerCamelCase'
107 unless(matchesName(validPropertyNameRegex(true))))
108 .bind("property"),
109 this);
110 }
111
112 void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
113 const auto *MatchedDecl =
114 Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
115 assert(MatchedDecl->getName().size() > 0);
116 auto *DeclContext = MatchedDecl->getDeclContext();
117 auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);
118
119 if (CategoryDecl != nullptr &&
120 hasCategoryPropertyPrefix(MatchedDecl->getName())) {
121 if (!prefixedPropertyNameValid(MatchedDecl->getName()) ||
122 CategoryDecl->IsClassExtension()) {
123 NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
124 : CategoryProperty;
125 diag(MatchedDecl->getLocation(),
126 "property name '%0' not using lowerCamelCase style or not prefixed "
127 "in a category, according to the Apple Coding Guidelines")
128 << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);
129 }
130 return;
131 }
132 diag(MatchedDecl->getLocation(),
133 "property name '%0' not using lowerCamelCase style or not prefixed in "
134 "a category, according to the Apple Coding Guidelines")
135 << MatchedDecl->getName()
136 << generateFixItHint(MatchedDecl, StandardProperty);
137 }
138
139 } // namespace objc
140 } // namespace tidy
141 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:93
clang::tidy::objc::@474::NamingStyle
NamingStyle
Definition: PropertyDeclarationCheck.cpp:32
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
PropertyDeclarationCheck.h
Index
const SymbolIndex * Index
Definition: Dexp.cpp:84