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