brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · 7470b1e Raw
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