xref: /llvm-project/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp (revision ec5f4be4521c3b28d6bb14f34f39a87c36fe8c00)
152161a5aSBen Hamilton //===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
252161a5aSBen Hamilton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
652161a5aSBen Hamilton //
752161a5aSBen Hamilton //===----------------------------------------------------------------------===//
852161a5aSBen Hamilton 
952161a5aSBen Hamilton #include "PropertyDeclarationCheck.h"
1052161a5aSBen Hamilton #include "../utils/OptionsUtils.h"
1152161a5aSBen Hamilton #include "clang/AST/ASTContext.h"
1252161a5aSBen Hamilton #include "clang/ASTMatchers/ASTMatchFinder.h"
1375b3b541SYan Zhang #include "clang/Basic/CharInfo.h"
14ae5bc5aeSYan Zhang #include "llvm/ADT/STLExtras.h"
1552161a5aSBen Hamilton #include "llvm/ADT/StringExtras.h"
1652161a5aSBen Hamilton #include "llvm/Support/Regex.h"
17ab2d3ce4SAlexander Kornienko #include <algorithm>
1852161a5aSBen Hamilton 
1952161a5aSBen Hamilton using namespace clang::ast_matchers;
2052161a5aSBen Hamilton 
217d2ea6c4SCarlos Galvez namespace clang::tidy::objc {
2252161a5aSBen Hamilton 
2352161a5aSBen Hamilton namespace {
2475b3b541SYan Zhang 
2575b3b541SYan Zhang // For StandardProperty the naming style is 'lowerCamelCase'.
2675b3b541SYan Zhang // For CategoryProperty especially in categories of system class,
2775b3b541SYan Zhang // to avoid naming conflict, the suggested naming style is
2875b3b541SYan Zhang // 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
29af4755acSStephane Moore // Regardless of the style, all acronyms and initialisms should be capitalized.
3075b3b541SYan Zhang enum NamingStyle {
3175b3b541SYan Zhang   StandardProperty = 1,
3275b3b541SYan Zhang   CategoryProperty = 2,
3375b3b541SYan Zhang };
3475b3b541SYan Zhang 
3575b3b541SYan Zhang /// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
3675b3b541SYan Zhang /// 'camelCase' or 'abc_camelCase'. For other cases the users need to
3752161a5aSBen Hamilton /// come up with a proper name by their own.
3852161a5aSBen Hamilton /// FIXME: provide fix for snake_case to snakeCase
generateFixItHint(const ObjCPropertyDecl * Decl,NamingStyle Style)3975b3b541SYan Zhang FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
4075b3b541SYan Zhang   auto Name = Decl->getName();
4152161a5aSBen Hamilton   auto NewName = Decl->getName().str();
4275b3b541SYan Zhang   size_t Index = 0;
4375b3b541SYan Zhang   if (Style == CategoryProperty) {
4475b3b541SYan Zhang     Index = Name.find_first_of('_') + 1;
4575b3b541SYan Zhang     NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
4675b3b541SYan Zhang   }
4775b3b541SYan Zhang   if (Index < Name.size()) {
4875b3b541SYan Zhang     NewName[Index] = tolower(NewName[Index]);
4975b3b541SYan Zhang     if (NewName != Name) {
5052161a5aSBen Hamilton       return FixItHint::CreateReplacement(
5152161a5aSBen Hamilton           CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
5252161a5aSBen Hamilton           llvm::StringRef(NewName));
5352161a5aSBen Hamilton     }
5475b3b541SYan Zhang   }
55*ec5f4be4SPiotr Zegar   return {};
5652161a5aSBen Hamilton }
5752161a5aSBen Hamilton 
validPropertyNameRegex(bool UsedInMatcher)58af4755acSStephane Moore std::string validPropertyNameRegex(bool UsedInMatcher) {
5952161a5aSBen Hamilton   // Allow any of these names:
6052161a5aSBen Hamilton   // foo
6152161a5aSBen Hamilton   // fooBar
6252161a5aSBen Hamilton   // url
6352161a5aSBen Hamilton   // urlString
64af4755acSStephane Moore   // ID
65af4755acSStephane Moore   // IDs
6652161a5aSBen Hamilton   // URL
6752161a5aSBen Hamilton   // URLString
688c298d2fSYan Zhang   // bundleID
69af4755acSStephane Moore   // CIColor
70af4755acSStephane Moore   //
71af4755acSStephane Moore   // Disallow names of this form:
72af4755acSStephane Moore   // LongString
73af4755acSStephane Moore   //
74af4755acSStephane Moore   // aRbITRaRyCapS is allowed to avoid generating false positives for names
75af4755acSStephane Moore   // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.
7675b3b541SYan Zhang   std::string StartMatcher = UsedInMatcher ? "::" : "^";
77af4755acSStephane Moore   return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
7875b3b541SYan Zhang }
7975b3b541SYan Zhang 
hasCategoryPropertyPrefix(llvm::StringRef PropertyName)8075b3b541SYan Zhang bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
8119bceda8SYan Zhang   auto RegexExp =
8219bceda8SYan Zhang       llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");
8375b3b541SYan Zhang   return RegexExp.match(PropertyName);
8475b3b541SYan Zhang }
8575b3b541SYan Zhang 
prefixedPropertyNameValid(llvm::StringRef PropertyName)86af4755acSStephane Moore bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
8775b3b541SYan Zhang   size_t Start = PropertyName.find_first_of('_');
8875b3b541SYan Zhang   assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
8975b3b541SYan Zhang   auto Prefix = PropertyName.substr(0, Start);
9075b3b541SYan Zhang   if (Prefix.lower() != Prefix) {
9175b3b541SYan Zhang     return false;
9275b3b541SYan Zhang   }
9319bceda8SYan Zhang   auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
9475b3b541SYan Zhang   return RegexExp.match(PropertyName.substr(Start + 1));
9552161a5aSBen Hamilton }
9652161a5aSBen Hamilton }  // namespace
9752161a5aSBen Hamilton 
registerMatchers(MatchFinder * Finder)9852161a5aSBen Hamilton void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
9919bceda8SYan Zhang   Finder->addMatcher(objcPropertyDecl(
10052161a5aSBen Hamilton                          // the property name should be in Lower Camel Case like
10152161a5aSBen Hamilton                          // 'lowerCamelCase'
102af4755acSStephane Moore                          unless(matchesName(validPropertyNameRegex(true))))
10352161a5aSBen Hamilton                          .bind("property"),
10452161a5aSBen Hamilton                      this);
10552161a5aSBen Hamilton }
10652161a5aSBen Hamilton 
check(const MatchFinder::MatchResult & Result)10752161a5aSBen Hamilton void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
10852161a5aSBen Hamilton   const auto *MatchedDecl =
10952161a5aSBen Hamilton       Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
11052161a5aSBen Hamilton   assert(MatchedDecl->getName().size() > 0);
11175b3b541SYan Zhang   auto *DeclContext = MatchedDecl->getDeclContext();
11275b3b541SYan Zhang   auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);
1134f9ead23SYan Zhang 
11475b3b541SYan Zhang   if (CategoryDecl != nullptr &&
11575b3b541SYan Zhang       hasCategoryPropertyPrefix(MatchedDecl->getName())) {
116af4755acSStephane Moore     if (!prefixedPropertyNameValid(MatchedDecl->getName()) ||
11775b3b541SYan Zhang         CategoryDecl->IsClassExtension()) {
11875b3b541SYan Zhang       NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
11975b3b541SYan Zhang                                                            : CategoryProperty;
12052161a5aSBen Hamilton       diag(MatchedDecl->getLocation(),
12175b3b541SYan Zhang            "property name '%0' not using lowerCamelCase style or not prefixed "
12275b3b541SYan Zhang            "in a category, according to the Apple Coding Guidelines")
12375b3b541SYan Zhang           << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);
12475b3b541SYan Zhang     }
12575b3b541SYan Zhang     return;
12675b3b541SYan Zhang   }
12775b3b541SYan Zhang   diag(MatchedDecl->getLocation(),
12875b3b541SYan Zhang        "property name '%0' not using lowerCamelCase style or not prefixed in "
12975b3b541SYan Zhang        "a category, according to the Apple Coding Guidelines")
13075b3b541SYan Zhang       << MatchedDecl->getName()
13175b3b541SYan Zhang       << generateFixItHint(MatchedDecl, StandardProperty);
13252161a5aSBen Hamilton }
13352161a5aSBen Hamilton 
1347d2ea6c4SCarlos Galvez } // namespace clang::tidy::objc
135