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