1 //===--- GlobalVariableDeclarationCheck.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 "GlobalVariableDeclarationCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "llvm/ADT/StringExtras.h" 13 #include "llvm/ADT/StringRef.h" 14 15 #include <string> 16 17 using namespace clang::ast_matchers; 18 19 namespace clang { 20 namespace tidy { 21 namespace google { 22 namespace objc { 23 24 namespace { 25 26 AST_MATCHER(VarDecl, isLocalVariable) { 27 return Node.isLocalVarDecl(); 28 } 29 30 FixItHint generateFixItHint(const VarDecl *Decl, bool IsConst) { 31 char FC = Decl->getName()[0]; 32 if (!llvm::isAlpha(FC) || Decl->getName().size() == 1) { 33 // No fix available if first character is not alphabetical character, or it 34 // is a single-character variable, since it is difficult to determine the 35 // proper fix in this case. Users should create a proper variable name by 36 // their own. 37 return FixItHint(); 38 } 39 char SC = Decl->getName()[1]; 40 if ((FC == 'k' || FC == 'g') && !llvm::isAlpha(SC)) { 41 // No fix available if the prefix is correct but the second character is not 42 // alphabetical, since it is difficult to determine the proper fix in this 43 // case. 44 return FixItHint(); 45 } 46 auto NewName = (IsConst ? "k" : "g") + 47 llvm::StringRef(std::string(1, FC)).upper() + 48 Decl->getName().substr(1).str(); 49 return FixItHint::CreateReplacement( 50 CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())), 51 llvm::StringRef(NewName)); 52 } 53 } // namespace 54 55 void GlobalVariableDeclarationCheck::registerMatchers(MatchFinder *Finder) { 56 // The relevant Style Guide rule only applies to Objective-C. 57 if (!getLangOpts().ObjC) 58 return; 59 60 // need to add two matchers since we need to bind different ids to distinguish 61 // constants and variables. Since bind() can only be called on node matchers, 62 // we cannot make it in one matcher. 63 // 64 // Note that hasGlobalStorage() matches static variables declared locally 65 // inside a function or method, so we need to exclude those with 66 // isLocalVariable(). 67 Finder->addMatcher( 68 varDecl(hasGlobalStorage(), unless(hasType(isConstQualified())), 69 unless(isLocalVariable()), unless(matchesName("::g[A-Z]"))) 70 .bind("global_var"), 71 this); 72 Finder->addMatcher(varDecl(hasGlobalStorage(), hasType(isConstQualified()), 73 unless(isLocalVariable()), 74 unless(matchesName("::(k[A-Z]|[A-Z]{2,})"))) 75 .bind("global_const"), 76 this); 77 } 78 79 void GlobalVariableDeclarationCheck::check( 80 const MatchFinder::MatchResult &Result) { 81 if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_var")) { 82 if (Decl->isStaticDataMember()) 83 return; 84 diag(Decl->getLocation(), 85 "non-const global variable '%0' must have a name which starts with " 86 "'g[A-Z]'") 87 << Decl->getName() << generateFixItHint(Decl, false); 88 } 89 if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_const")) { 90 if (Decl->isStaticDataMember()) 91 return; 92 diag(Decl->getLocation(), 93 "const global variable '%0' must have a name which starts with " 94 "an appropriate prefix") 95 << Decl->getName() << generateFixItHint(Decl, true); 96 } 97 } 98 99 } // namespace objc 100 } // namespace google 101 } // namespace tidy 102 } // namespace clang 103