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