1 //===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "PropertyDeclarationCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/CharInfo.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/Regex.h"
19 using namespace clang::ast_matchers
;
21 namespace clang::tidy::objc
{
25 // For StandardProperty the naming style is 'lowerCamelCase'.
26 // For CategoryProperty especially in categories of system class,
27 // to avoid naming conflict, the suggested naming style is
28 // 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
29 // Regardless of the style, all acronyms and initialisms should be capitalized.
35 /// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
36 /// 'camelCase' or 'abc_camelCase'. For other cases the users need to
37 /// come up with a proper name by their own.
38 /// FIXME: provide fix for snake_case to snakeCase
39 FixItHint
generateFixItHint(const ObjCPropertyDecl
*Decl
, NamingStyle Style
) {
40 auto Name
= Decl
->getName();
41 auto NewName
= Decl
->getName().str();
43 if (Style
== CategoryProperty
) {
44 Index
= Name
.find_first_of('_') + 1;
45 NewName
.replace(0, Index
- 1, Name
.substr(0, Index
- 1).lower());
47 if (Index
< Name
.size()) {
48 NewName
[Index
] = tolower(NewName
[Index
]);
49 if (NewName
!= Name
) {
50 return FixItHint::CreateReplacement(
51 CharSourceRange::getTokenRange(SourceRange(Decl
->getLocation())),
52 llvm::StringRef(NewName
));
58 std::string
validPropertyNameRegex(bool UsedInMatcher
) {
59 // Allow any of these names:
71 // Disallow names of this form:
74 // aRbITRaRyCapS is allowed to avoid generating false positives for names
75 // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.
76 std::string StartMatcher
= UsedInMatcher
? "::" : "^";
77 return StartMatcher
+ "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
80 bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName
) {
82 llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");
83 return RegexExp
.match(PropertyName
);
86 bool prefixedPropertyNameValid(llvm::StringRef PropertyName
) {
87 size_t Start
= PropertyName
.find_first_of('_');
88 assert(Start
!= llvm::StringRef::npos
&& Start
+ 1 < PropertyName
.size());
89 auto Prefix
= PropertyName
.substr(0, Start
);
90 if (Prefix
.lower() != Prefix
) {
93 auto RegexExp
= llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
94 return RegexExp
.match(PropertyName
.substr(Start
+ 1));
98 void PropertyDeclarationCheck::registerMatchers(MatchFinder
*Finder
) {
99 Finder
->addMatcher(objcPropertyDecl(
100 // the property name should be in Lower Camel Case like
102 unless(matchesName(validPropertyNameRegex(true))))
107 void PropertyDeclarationCheck::check(const MatchFinder::MatchResult
&Result
) {
108 const auto *MatchedDecl
=
109 Result
.Nodes
.getNodeAs
<ObjCPropertyDecl
>("property");
110 assert(MatchedDecl
->getName().size() > 0);
111 auto *DeclContext
= MatchedDecl
->getDeclContext();
112 auto *CategoryDecl
= llvm::dyn_cast
<ObjCCategoryDecl
>(DeclContext
);
114 if (CategoryDecl
!= nullptr &&
115 hasCategoryPropertyPrefix(MatchedDecl
->getName())) {
116 if (!prefixedPropertyNameValid(MatchedDecl
->getName()) ||
117 CategoryDecl
->IsClassExtension()) {
118 NamingStyle Style
= CategoryDecl
->IsClassExtension() ? StandardProperty
120 diag(MatchedDecl
->getLocation(),
121 "property name '%0' not using lowerCamelCase style or not prefixed "
122 "in a category, according to the Apple Coding Guidelines")
123 << MatchedDecl
->getName() << generateFixItHint(MatchedDecl
, Style
);
127 diag(MatchedDecl
->getLocation(),
128 "property name '%0' not using lowerCamelCase style or not prefixed in "
129 "a category, according to the Apple Coding Guidelines")
130 << MatchedDecl
->getName()
131 << generateFixItHint(MatchedDecl
, StandardProperty
);
134 } // namespace clang::tidy::objc