15529a244SHaojian Wu //===--- GlobalVariableDeclarationCheck.cpp - clang-tidy-------------------===//
25529a244SHaojian Wu //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65529a244SHaojian Wu //
75529a244SHaojian Wu //===----------------------------------------------------------------------===//
85529a244SHaojian Wu
95529a244SHaojian Wu #include "GlobalVariableDeclarationCheck.h"
105529a244SHaojian Wu #include "clang/AST/ASTContext.h"
115529a244SHaojian Wu #include "clang/ASTMatchers/ASTMatchFinder.h"
125529a244SHaojian Wu #include "llvm/ADT/StringExtras.h"
135529a244SHaojian Wu #include "llvm/ADT/StringRef.h"
145529a244SHaojian Wu
155529a244SHaojian Wu #include <string>
165529a244SHaojian Wu
175529a244SHaojian Wu using namespace clang::ast_matchers;
185529a244SHaojian Wu
197d2ea6c4SCarlos Galvez namespace clang::tidy::google::objc {
205529a244SHaojian Wu
215529a244SHaojian Wu namespace {
225529a244SHaojian Wu
AST_MATCHER(VarDecl,isLocalVariable)23ec1982f0SStephane Moore AST_MATCHER(VarDecl, isLocalVariable) { return Node.isLocalVarDecl(); }
2412e3726fSStephane Moore
generateFixItHint(const VarDecl * Decl,bool IsConst)259ac757bfSStephane Moore FixItHint generateFixItHint(const VarDecl *Decl, bool IsConst) {
262fd11e0bSThorsten Schütt if (IsConst && (Decl->getStorageClass() != SC_Static)) {
27ec1982f0SStephane Moore // No fix available if it is not a static constant, since it is difficult
28ec1982f0SStephane Moore // to determine the proper fix in this case.
29*ec5f4be4SPiotr Zegar return {};
30ec1982f0SStephane Moore }
31ec1982f0SStephane Moore
325529a244SHaojian Wu char FC = Decl->getName()[0];
335529a244SHaojian Wu if (!llvm::isAlpha(FC) || Decl->getName().size() == 1) {
345529a244SHaojian Wu // No fix available if first character is not alphabetical character, or it
355529a244SHaojian Wu // is a single-character variable, since it is difficult to determine the
365529a244SHaojian Wu // proper fix in this case. Users should create a proper variable name by
375529a244SHaojian Wu // their own.
38*ec5f4be4SPiotr Zegar return {};
395529a244SHaojian Wu }
405529a244SHaojian Wu char SC = Decl->getName()[1];
415529a244SHaojian Wu if ((FC == 'k' || FC == 'g') && !llvm::isAlpha(SC)) {
42ec1982f0SStephane Moore // No fix available if the prefix is correct but the second character is
43ec1982f0SStephane Moore // not alphabetical, since it is difficult to determine the proper fix in
44ec1982f0SStephane Moore // this case.
45*ec5f4be4SPiotr Zegar return {};
465529a244SHaojian Wu }
47ec1982f0SStephane Moore
485529a244SHaojian Wu auto NewName = (IsConst ? "k" : "g") +
495529a244SHaojian Wu llvm::StringRef(std::string(1, FC)).upper() +
505529a244SHaojian Wu Decl->getName().substr(1).str();
51ec1982f0SStephane Moore
525529a244SHaojian Wu return FixItHint::CreateReplacement(
535529a244SHaojian Wu CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
545529a244SHaojian Wu llvm::StringRef(NewName));
555529a244SHaojian Wu }
565529a244SHaojian Wu } // namespace
575529a244SHaojian Wu
registerMatchers(MatchFinder * Finder)585529a244SHaojian Wu void GlobalVariableDeclarationCheck::registerMatchers(MatchFinder *Finder) {
595529a244SHaojian Wu // need to add two matchers since we need to bind different ids to distinguish
605529a244SHaojian Wu // constants and variables. Since bind() can only be called on node matchers,
615529a244SHaojian Wu // we cannot make it in one matcher.
62490811ecSBen Hamilton //
63490811ecSBen Hamilton // Note that hasGlobalStorage() matches static variables declared locally
64490811ecSBen Hamilton // inside a function or method, so we need to exclude those with
65490811ecSBen Hamilton // isLocalVariable().
665529a244SHaojian Wu Finder->addMatcher(
675529a244SHaojian Wu varDecl(hasGlobalStorage(), unless(hasType(isConstQualified())),
68490811ecSBen Hamilton unless(isLocalVariable()), unless(matchesName("::g[A-Z]")))
695529a244SHaojian Wu .bind("global_var"),
705529a244SHaojian Wu this);
715529a244SHaojian Wu Finder->addMatcher(varDecl(hasGlobalStorage(), hasType(isConstQualified()),
72490811ecSBen Hamilton unless(isLocalVariable()),
73ec1982f0SStephane Moore unless(matchesName("::(k[A-Z])|([A-Z][A-Z0-9])")))
745529a244SHaojian Wu .bind("global_const"),
755529a244SHaojian Wu this);
765529a244SHaojian Wu }
775529a244SHaojian Wu
check(const MatchFinder::MatchResult & Result)785529a244SHaojian Wu void GlobalVariableDeclarationCheck::check(
795529a244SHaojian Wu const MatchFinder::MatchResult &Result) {
805529a244SHaojian Wu if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_var")) {
81787a7734SYan Zhang if (Decl->isStaticDataMember())
82787a7734SYan Zhang return;
835529a244SHaojian Wu diag(Decl->getLocation(),
845529a244SHaojian Wu "non-const global variable '%0' must have a name which starts with "
855529a244SHaojian Wu "'g[A-Z]'")
865529a244SHaojian Wu << Decl->getName() << generateFixItHint(Decl, false);
875529a244SHaojian Wu }
885529a244SHaojian Wu if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_const")) {
89787a7734SYan Zhang if (Decl->isStaticDataMember())
90787a7734SYan Zhang return;
915529a244SHaojian Wu diag(Decl->getLocation(),
925529a244SHaojian Wu "const global variable '%0' must have a name which starts with "
932d836470SYan Zhang "an appropriate prefix")
945529a244SHaojian Wu << Decl->getName() << generateFixItHint(Decl, true);
955529a244SHaojian Wu }
965529a244SHaojian Wu }
975529a244SHaojian Wu
987d2ea6c4SCarlos Galvez } // namespace clang::tidy::google::objc
99