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 "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/Support/Regex.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::objc {
17
18namespace {
19
20// For StandardProperty the naming style is 'lowerCamelCase'.
21// For CategoryProperty especially in categories of system class,
22// to avoid naming conflict, the suggested naming style is
23// 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
24// Regardless of the style, all acronyms and initialisms should be capitalized.
25enum NamingStyle {
26 StandardProperty = 1,
27 CategoryProperty = 2,
28};
29
30/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
31/// 'camelCase' or 'abc_camelCase'. For other cases the users need to
32/// come up with a proper name by their own.
33/// FIXME: provide fix for snake_case to snakeCase
34FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
35 auto Name = Decl->getName();
36 auto NewName = Decl->getName().str();
37 size_t Index = 0;
38 if (Style == CategoryProperty) {
39 Index = Name.find_first_of('_') + 1;
40 NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
41 }
42 if (Index < Name.size()) {
43 NewName[Index] = tolower(NewName[Index]);
44 if (NewName != Name) {
45 return FixItHint::CreateReplacement(
46 RemoveRange: CharSourceRange::getTokenRange(R: SourceRange(Decl->getLocation())),
47 Code: llvm::StringRef(NewName));
48 }
49 }
50 return {};
51}
52
53std::string validPropertyNameRegex(bool UsedInMatcher) {
54 // Allow any of these names:
55 // foo
56 // fooBar
57 // url
58 // urlString
59 // ID
60 // IDs
61 // URL
62 // URLString
63 // bundleID
64 // CIColor
65 //
66 // Disallow names of this form:
67 // LongString
68 //
69 // aRbITRaRyCapS is allowed to avoid generating false positives for names
70 // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.
71 std::string StartMatcher = UsedInMatcher ? "::" : "^";
72 return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
73}
74
75bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
76 auto RegexExp =
77 llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");
78 return RegexExp.match(String: PropertyName);
79}
80
81bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
82 size_t Start = PropertyName.find_first_of(C: '_');
83 assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
84 auto Prefix = PropertyName.substr(Start: 0, N: Start);
85 if (Prefix.lower() != Prefix) {
86 return false;
87 }
88 auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(UsedInMatcher: false)));
89 return RegexExp.match(String: PropertyName.substr(Start: Start + 1));
90}
91} // namespace
92
93void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
94 Finder->addMatcher(NodeMatch: objcPropertyDecl(
95 // the property name should be in Lower Camel Case like
96 // 'lowerCamelCase'
97 unless(matchesName(RegExp: validPropertyNameRegex(UsedInMatcher: true))))
98 .bind(ID: "property"),
99 Action: this);
100}
101
102void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
103 const auto *MatchedDecl =
104 Result.Nodes.getNodeAs<ObjCPropertyDecl>(ID: "property");
105 assert(MatchedDecl->getName().size() > 0);
106 auto *DeclContext = MatchedDecl->getDeclContext();
107 auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);
108
109 if (CategoryDecl != nullptr &&
110 hasCategoryPropertyPrefix(MatchedDecl->getName())) {
111 if (!prefixedPropertyNameValid(MatchedDecl->getName()) ||
112 CategoryDecl->IsClassExtension()) {
113 NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
114 : CategoryProperty;
115 diag(MatchedDecl->getLocation(),
116 "property name '%0' not using lowerCamelCase style or not prefixed "
117 "in a category, according to the Apple Coding Guidelines")
118 << MatchedDecl->getName() << generateFixItHint(Decl: MatchedDecl, Style);
119 }
120 return;
121 }
122 diag(MatchedDecl->getLocation(),
123 "property name '%0' not using lowerCamelCase style or not prefixed in "
124 "a category, according to the Apple Coding Guidelines")
125 << MatchedDecl->getName()
126 << generateFixItHint(Decl: MatchedDecl, Style: StandardProperty);
127}
128
129} // namespace clang::tidy::objc
130

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp