1 //===--- IdentifierLengthCheck.cpp - clang-tidy
2 //-----------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "IdentifierLengthCheck.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace readability {
20 
21 const unsigned DefaultMinimumVariableNameLength = 3;
22 const unsigned DefaultMinimumLoopCounterNameLength = 2;
23 const unsigned DefaultMinimumExceptionNameLength = 2;
24 const unsigned DefaultMinimumParameterNameLength = 3;
25 const char DefaultIgnoredLoopCounterNames[] = "^[ijk_]$";
26 const char DefaultIgnoredVariableNames[] = "";
27 const char DefaultIgnoredExceptionVariableNames[] = "^[e]$";
28 const char DefaultIgnoredParameterNames[] = "^[n]$";
29 
30 const char ErrorMessage[] =
31     "%select{variable|exception variable|loop variable|"
32     "parameter}0 name %1 is too short, expected at least %2 characters";
33 
IdentifierLengthCheck(StringRef Name,ClangTidyContext * Context)34 IdentifierLengthCheck::IdentifierLengthCheck(StringRef Name,
35                                              ClangTidyContext *Context)
36     : ClangTidyCheck(Name, Context),
37       MinimumVariableNameLength(Options.get("MinimumVariableNameLength",
38                                             DefaultMinimumVariableNameLength)),
39       MinimumLoopCounterNameLength(Options.get(
40           "MinimumLoopCounterNameLength", DefaultMinimumLoopCounterNameLength)),
41       MinimumExceptionNameLength(Options.get(
42           "MinimumExceptionNameLength", DefaultMinimumExceptionNameLength)),
43       MinimumParameterNameLength(Options.get(
44           "MinimumParameterNameLength", DefaultMinimumParameterNameLength)),
45       IgnoredVariableNamesInput(
46           Options.get("IgnoredVariableNames", DefaultIgnoredVariableNames)),
47       IgnoredVariableNames(IgnoredVariableNamesInput),
48       IgnoredLoopCounterNamesInput(Options.get("IgnoredLoopCounterNames",
49                                                DefaultIgnoredLoopCounterNames)),
50       IgnoredLoopCounterNames(IgnoredLoopCounterNamesInput),
51       IgnoredExceptionVariableNamesInput(
52           Options.get("IgnoredExceptionVariableNames",
53                       DefaultIgnoredExceptionVariableNames)),
54       IgnoredExceptionVariableNames(IgnoredExceptionVariableNamesInput),
55       IgnoredParameterNamesInput(
56           Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames)),
57       IgnoredParameterNames(IgnoredParameterNamesInput) {}
58 
storeOptions(ClangTidyOptions::OptionMap & Opts)59 void IdentifierLengthCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
60   Options.store(Opts, "MinimumVariableNameLength", MinimumVariableNameLength);
61   Options.store(Opts, "MinimumLoopCounterNameLength",
62                 MinimumLoopCounterNameLength);
63   Options.store(Opts, "MinimumExceptionNameLength", MinimumExceptionNameLength);
64   Options.store(Opts, "MinimumParameterNameLength", MinimumParameterNameLength);
65   Options.store(Opts, "IgnoredLoopCounterNames", IgnoredLoopCounterNamesInput);
66   Options.store(Opts, "IgnoredVariableNames", IgnoredVariableNamesInput);
67   Options.store(Opts, "IgnoredExceptionVariableNames",
68                 IgnoredExceptionVariableNamesInput);
69   Options.store(Opts, "IgnoredParameterNames", IgnoredParameterNamesInput);
70 }
71 
registerMatchers(MatchFinder * Finder)72 void IdentifierLengthCheck::registerMatchers(MatchFinder *Finder) {
73   if (MinimumLoopCounterNameLength > 1)
74     Finder->addMatcher(
75         forStmt(hasLoopInit(declStmt(forEach(varDecl().bind("loopVar"))))),
76         this);
77 
78   if (MinimumExceptionNameLength > 1)
79     Finder->addMatcher(varDecl(hasParent(cxxCatchStmt())).bind("exceptionVar"),
80                        this);
81 
82   if (MinimumParameterNameLength > 1)
83     Finder->addMatcher(parmVarDecl().bind("paramVar"), this);
84 
85   if (MinimumVariableNameLength > 1)
86     Finder->addMatcher(
87         varDecl(unless(anyOf(hasParent(declStmt(hasParent(forStmt()))),
88                              hasParent(cxxCatchStmt()), parmVarDecl())))
89             .bind("standaloneVar"),
90         this);
91 }
92 
check(const MatchFinder::MatchResult & Result)93 void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
94   const auto *StandaloneVar = Result.Nodes.getNodeAs<VarDecl>("standaloneVar");
95   if (StandaloneVar) {
96     if (!StandaloneVar->getIdentifier())
97       return;
98 
99     StringRef VarName = StandaloneVar->getName();
100 
101     if (VarName.size() >= MinimumVariableNameLength ||
102         IgnoredVariableNames.match(VarName))
103       return;
104 
105     diag(StandaloneVar->getLocation(), ErrorMessage)
106         << 0 << StandaloneVar << MinimumVariableNameLength;
107   }
108 
109   auto *ExceptionVarName = Result.Nodes.getNodeAs<VarDecl>("exceptionVar");
110   if (ExceptionVarName) {
111     if (!ExceptionVarName->getIdentifier())
112       return;
113 
114     StringRef VarName = ExceptionVarName->getName();
115     if (VarName.size() >= MinimumExceptionNameLength ||
116         IgnoredExceptionVariableNames.match(VarName))
117       return;
118 
119     diag(ExceptionVarName->getLocation(), ErrorMessage)
120         << 1 << ExceptionVarName << MinimumExceptionNameLength;
121   }
122 
123   const auto *LoopVar = Result.Nodes.getNodeAs<VarDecl>("loopVar");
124   if (LoopVar) {
125     if (!LoopVar->getIdentifier())
126       return;
127 
128     StringRef VarName = LoopVar->getName();
129 
130     if (VarName.size() >= MinimumLoopCounterNameLength ||
131         IgnoredLoopCounterNames.match(VarName))
132       return;
133 
134     diag(LoopVar->getLocation(), ErrorMessage)
135         << 2 << LoopVar << MinimumLoopCounterNameLength;
136   }
137 
138   const auto *ParamVar = Result.Nodes.getNodeAs<VarDecl>("paramVar");
139   if (ParamVar) {
140     if (!ParamVar->getIdentifier())
141       return;
142 
143     StringRef VarName = ParamVar->getName();
144 
145     if (VarName.size() >= MinimumParameterNameLength ||
146         IgnoredParameterNames.match(VarName))
147       return;
148 
149     diag(ParamVar->getLocation(), ErrorMessage)
150         << 3 << ParamVar << MinimumParameterNameLength;
151   }
152 }
153 
154 } // namespace readability
155 } // namespace tidy
156 } // namespace clang
157