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"
39FixItHint generateFixItHint(
const ObjCPropertyDecl *
Decl, NamingStyle Style) {
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));
58std::string validPropertyNameRegex(
bool UsedInMatcher) {
76 std::string StartMatcher = UsedInMatcher ?
"::" :
"^";
77 return StartMatcher +
"([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
80bool 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);
86bool 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));
99 Finder->addMatcher(objcPropertyDecl(
102 unless(matchesName(validPropertyNameRegex(
true))))
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);
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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.