//===--- GlobalVariableDeclarationCheck.cpp - clang-tidy-------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "GlobalVariableDeclarationCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace google { namespace objc { namespace { AST_MATCHER(VarDecl, isLocalVariable) { return Node.isLocalVarDecl(); } FixItHint generateFixItHint(const VarDecl *Decl, bool IsConst) { if (IsConst && (Decl->getStorageClass() != SC_Static)) { // No fix available if it is not a static constant, since it is difficult // to determine the proper fix in this case. return FixItHint(); } char FC = Decl->getName()[0]; if (!llvm::isAlpha(FC) || Decl->getName().size() == 1) { // No fix available if first character is not alphabetical character, or it // is a single-character variable, since it is difficult to determine the // proper fix in this case. Users should create a proper variable name by // their own. return FixItHint(); } char SC = Decl->getName()[1]; if ((FC == 'k' || FC == 'g') && !llvm::isAlpha(SC)) { // No fix available if the prefix is correct but the second character is // not alphabetical, since it is difficult to determine the proper fix in // this case. return FixItHint(); } auto NewName = (IsConst ? "k" : "g") + llvm::StringRef(std::string(1, FC)).upper() + Decl->getName().substr(1).str(); return FixItHint::CreateReplacement( CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())), llvm::StringRef(NewName)); } } // namespace void GlobalVariableDeclarationCheck::registerMatchers(MatchFinder *Finder) { // need to add two matchers since we need to bind different ids to distinguish // constants and variables. Since bind() can only be called on node matchers, // we cannot make it in one matcher. // // Note that hasGlobalStorage() matches static variables declared locally // inside a function or method, so we need to exclude those with // isLocalVariable(). Finder->addMatcher( varDecl(hasGlobalStorage(), unless(hasType(isConstQualified())), unless(isLocalVariable()), unless(matchesName("::g[A-Z]"))) .bind("global_var"), this); Finder->addMatcher(varDecl(hasGlobalStorage(), hasType(isConstQualified()), unless(isLocalVariable()), unless(matchesName("::(k[A-Z])|([A-Z][A-Z0-9])"))) .bind("global_const"), this); } void GlobalVariableDeclarationCheck::check( const MatchFinder::MatchResult &Result) { if (const auto *Decl = Result.Nodes.getNodeAs("global_var")) { if (Decl->isStaticDataMember()) return; diag(Decl->getLocation(), "non-const global variable '%0' must have a name which starts with " "'g[A-Z]'") << Decl->getName() << generateFixItHint(Decl, false); } if (const auto *Decl = Result.Nodes.getNodeAs("global_const")) { if (Decl->isStaticDataMember()) return; diag(Decl->getLocation(), "const global variable '%0' must have a name which starts with " "an appropriate prefix") << Decl->getName() << generateFixItHint(Decl, true); } } } // namespace objc } // namespace google } // namespace tidy } // namespace clang