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 auto WarnIfTooShort = [&](const ValueDecl *Var, unsigned MinNameLength,
142 const llvm::Regex &IgnoredNames, unsigned VarKind) {
143 if (!Var->getIdentifier())
144 return;
145
146 const StringRef VarName = Var->getName();
147 if (VarName.size() >= MinNameLength || IgnoredNames.match(VarName))
148 return;
149
150 if (isShortLived(Var, Result.SourceManager, Result.Context,
151 LineCountThreshold))
152 return;
153
154 diag(Var->getLocation(), ErrorMessage) << VarKind << Var << MinNameLength;
155 };
156
157 if (const auto *StandaloneVar =
158 Result.Nodes.getNodeAs<ValueDecl>("standaloneVar")) {
159 WarnIfTooShort(StandaloneVar, MinimumVariableNameLength,
160 IgnoredVariableNames, 0);
161 return;
162 }
163
164 if (const auto *BindingVar =
165 Result.Nodes.getNodeAs<ValueDecl>("bindingVar")) {
166 WarnIfTooShort(BindingVar, MinimumBindingNameLength, IgnoredBindingNames,
167 1);
168 return;
169 }
170
171 if (const auto *ExceptionVar =
172 Result.Nodes.getNodeAs<ValueDecl>("exceptionVar")) {
173 WarnIfTooShort(ExceptionVar, MinimumExceptionNameLength,
174 IgnoredExceptionVariableNames, 2);
175 return;
176 }
177
178 if (const auto *LoopVar = Result.Nodes.getNodeAs<ValueDecl>("loopVar")) {
179 WarnIfTooShort(LoopVar, MinimumLoopCounterNameLength,
180 IgnoredLoopCounterNames, 3);
181 return;
182 }
183
184 if (const auto *ParamVar = Result.Nodes.getNodeAs<ValueDecl>("paramVar")) {
185 WarnIfTooShort(ParamVar, MinimumParameterNameLength, IgnoredParameterNames,
186 4);
187 return;
188 }
189}
190
191} // 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