clang-tools 23.0.0git
IdentifierLengthCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12
13using namespace clang::ast_matchers;
14
16
23const char DefaultIgnoredBindingNames[] = "^[_]$";
24const char DefaultIgnoredLoopCounterNames[] = "^[ijk_]$";
26const char DefaultIgnoredParameterNames[] = "^[n]$";
27const unsigned DefaultLineCountThreshold = 0;
28
29const char ErrorMessage[] =
30 "%select{variable|binding variable|exception variable|loop variable|"
31 "parameter}0 name %1 is too short, expected at least %2 characters";
32
34 ClangTidyContext *Context)
35 : ClangTidyCheck(Name, Context),
36 MinimumVariableNameLength(Options.get("MinimumVariableNameLength",
38 MinimumBindingNameLength(Options.get("MinimumBindingNameLength",
40 MinimumLoopCounterNameLength(Options.get(
41 "MinimumLoopCounterNameLength", DefaultMinimumLoopCounterNameLength)),
42 MinimumExceptionNameLength(Options.get(
43 "MinimumExceptionNameLength", DefaultMinimumExceptionNameLength)),
44 MinimumParameterNameLength(Options.get(
45 "MinimumParameterNameLength", DefaultMinimumParameterNameLength)),
46 IgnoredVariableNamesInput(
47 Options.get("IgnoredVariableNames", DefaultIgnoredVariableNames)),
48 IgnoredVariableNames(IgnoredVariableNamesInput),
49 IgnoredBindingNamesInput(
50 Options.get("IgnoredBindingNames", DefaultIgnoredBindingNames)),
51 IgnoredBindingNames(IgnoredBindingNamesInput),
52 IgnoredLoopCounterNamesInput(Options.get("IgnoredLoopCounterNames",
54 IgnoredLoopCounterNames(IgnoredLoopCounterNamesInput),
55 IgnoredExceptionVariableNamesInput(
56 Options.get("IgnoredExceptionVariableNames",
58 IgnoredExceptionVariableNames(IgnoredExceptionVariableNamesInput),
59 IgnoredParameterNamesInput(
60 Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames)),
61 IgnoredParameterNames(IgnoredParameterNamesInput),
62 LineCountThreshold(
63 Options.get("LineCountThreshold", DefaultLineCountThreshold)) {}
64
66 Options.store(Opts, "MinimumVariableNameLength", MinimumVariableNameLength);
67 Options.store(Opts, "MinimumBindingNameLength", MinimumBindingNameLength);
68 Options.store(Opts, "MinimumLoopCounterNameLength",
69 MinimumLoopCounterNameLength);
70 Options.store(Opts, "MinimumExceptionNameLength", MinimumExceptionNameLength);
71 Options.store(Opts, "MinimumParameterNameLength", MinimumParameterNameLength);
72 Options.store(Opts, "IgnoredVariableNames", IgnoredVariableNamesInput);
73 Options.store(Opts, "IgnoredBindingNames", IgnoredBindingNamesInput);
74 Options.store(Opts, "IgnoredLoopCounterNames", IgnoredLoopCounterNamesInput);
75 Options.store(Opts, "IgnoredExceptionVariableNames",
76 IgnoredExceptionVariableNamesInput);
77 Options.store(Opts, "IgnoredParameterNames", IgnoredParameterNamesInput);
78 Options.store(Opts, "LineCountThreshold", LineCountThreshold);
79}
80
81void IdentifierLengthCheck::registerMatchers(MatchFinder *Finder) {
82 if (MinimumLoopCounterNameLength > 1)
83 Finder->addMatcher(
84 forStmt(hasLoopInit(declStmt(forEach(varDecl().bind("loopVar"))))),
85 this);
86
87 if (MinimumExceptionNameLength > 1)
88 Finder->addMatcher(varDecl(hasParent(cxxCatchStmt())).bind("exceptionVar"),
89 this);
90
91 if (MinimumParameterNameLength > 1)
92 Finder->addMatcher(parmVarDecl().bind("paramVar"), this);
93
94 if (MinimumBindingNameLength > 1)
95 Finder->addMatcher(bindingDecl().bind("bindingVar"), this);
96
97 if (MinimumVariableNameLength > 1)
98 Finder->addMatcher(
99 varDecl(unless(anyOf(hasParent(declStmt(hasParent(forStmt()))),
100 hasParent(cxxCatchStmt()), parmVarDecl())))
101 .bind("standaloneVar"),
102 this);
103}
104
105static std::optional<unsigned> countLinesToLastUse(const ValueDecl *Var,
106 const SourceManager *SrcMgr,
107 ASTContext *Ctx) {
108 const auto *ParentScope = llvm::dyn_cast<FunctionDecl>(Var->getDeclContext());
109 if (ParentScope == nullptr)
110 return std::nullopt;
111
112 auto AllRefs =
113 utils::decl_ref_expr::allDeclRefExprs(*Var, *ParentScope, *Ctx);
114
115 auto AllRefLines =
116 llvm::map_range(AllRefs, [&](const DeclRefExpr *RefToVar) -> unsigned {
117 return SrcMgr->getSpellingLineNumber(RefToVar->getLocation());
118 });
119
120 const unsigned DeclLine = SrcMgr->getSpellingLineNumber(Var->getLocation());
121 const unsigned LastUseLine =
122 AllRefLines.empty() ? DeclLine
123 : std::max(DeclLine, *llvm::max_element(AllRefLines));
124
125 return LastUseLine - DeclLine + 1;
126}
127
128static bool isShortLived(const ValueDecl *Var, const SourceManager *SrcMgr,
129 ASTContext *Ctx, unsigned LineCountThreshold) {
130 if (LineCountThreshold == 0)
131 return false;
132
133 std::optional<unsigned> LineCount = countLinesToLastUse(Var, SrcMgr, Ctx);
134 if (LineCount && LineCount.value() <= LineCountThreshold)
135 return true;
136
137 return false;
138}
139
140void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
141 const auto *StandaloneVar =
142 Result.Nodes.getNodeAs<ValueDecl>("standaloneVar");
143 if (StandaloneVar) {
144 if (!StandaloneVar->getIdentifier())
145 return;
146
147 const StringRef VarName = StandaloneVar->getName();
148
149 if (VarName.size() >= MinimumVariableNameLength ||
150 IgnoredVariableNames.match(VarName))
151 return;
152
153 if (isShortLived(StandaloneVar, Result.SourceManager, Result.Context,
154 LineCountThreshold))
155 return;
156
157 diag(StandaloneVar->getLocation(), ErrorMessage)
158 << 0 << StandaloneVar << MinimumVariableNameLength;
159 }
160
161 const auto *BindingVar = Result.Nodes.getNodeAs<ValueDecl>("bindingVar");
162 if (BindingVar) {
163 if (!BindingVar->getIdentifier())
164 return;
165
166 const StringRef VarName = BindingVar->getName();
167
168 if (VarName.size() >= MinimumBindingNameLength ||
169 IgnoredBindingNames.match(VarName))
170 return;
171
172 if (isShortLived(BindingVar, Result.SourceManager, Result.Context,
173 LineCountThreshold))
174 return;
175
176 diag(BindingVar->getLocation(), ErrorMessage)
177 << 1 << BindingVar << MinimumBindingNameLength;
178 }
179
180 auto *ExceptionVarName = Result.Nodes.getNodeAs<ValueDecl>("exceptionVar");
181 if (ExceptionVarName) {
182 if (!ExceptionVarName->getIdentifier())
183 return;
184
185 const StringRef VarName = ExceptionVarName->getName();
186 if (VarName.size() >= MinimumExceptionNameLength ||
187 IgnoredExceptionVariableNames.match(VarName))
188 return;
189
190 if (isShortLived(ExceptionVarName, Result.SourceManager, Result.Context,
191 LineCountThreshold))
192 return;
193
194 diag(ExceptionVarName->getLocation(), ErrorMessage)
195 << 2 << ExceptionVarName << MinimumExceptionNameLength;
196 }
197
198 const auto *LoopVar = Result.Nodes.getNodeAs<ValueDecl>("loopVar");
199 if (LoopVar) {
200 if (!LoopVar->getIdentifier())
201 return;
202
203 const StringRef VarName = LoopVar->getName();
204
205 if (VarName.size() >= MinimumLoopCounterNameLength ||
206 IgnoredLoopCounterNames.match(VarName))
207 return;
208
209 if (isShortLived(LoopVar, Result.SourceManager, Result.Context,
210 LineCountThreshold))
211 return;
212
213 diag(LoopVar->getLocation(), ErrorMessage)
214 << 3 << LoopVar << MinimumLoopCounterNameLength;
215 }
216
217 const auto *ParamVar = Result.Nodes.getNodeAs<ValueDecl>("paramVar");
218 if (ParamVar) {
219 if (!ParamVar->getIdentifier())
220 return;
221
222 const StringRef VarName = ParamVar->getName();
223
224 if (VarName.size() >= MinimumParameterNameLength ||
225 IgnoredParameterNames.match(VarName))
226 return;
227
228 if (isShortLived(ParamVar, Result.SourceManager, Result.Context,
229 LineCountThreshold))
230 return;
231
232 diag(ParamVar->getLocation(), ErrorMessage)
233 << 4 << ParamVar << MinimumParameterNameLength;
234 }
235}
236
237} // namespace clang::tidy::readability
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
IdentifierLengthCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
const char DefaultIgnoredExceptionVariableNames[]
static bool isShortLived(const ValueDecl *Var, const SourceManager *SrcMgr, ASTContext *Ctx, unsigned LineCountThreshold)
const unsigned DefaultMinimumParameterNameLength
const unsigned DefaultMinimumExceptionNameLength
const unsigned DefaultMinimumBindingNameLength
static std::optional< unsigned > countLinesToLastUse(const ValueDecl *Var, const SourceManager *SrcMgr, ASTContext *Ctx)
const unsigned DefaultMinimumLoopCounterNameLength
const unsigned DefaultMinimumVariableNameLength
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const ValueDecl &ValueDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to ValueDecl within Stmt.
llvm::StringMap< ClangTidyValue > OptionMap