[clang-tools-extra] 3c88abe - [clang-tidy][readability-identifier-length] Add a line count threshold (#185319)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 21 00:58:25 PDT 2026
Author: Alex Dutka
Date: 2026-04-21T15:58:20+08:00
New Revision: 3c88abe3206bb944566ff4b62aa4b9874327f37d
URL: https://github.com/llvm/llvm-project/commit/3c88abe3206bb944566ff4b62aa4b9874327f37d
DIFF: https://github.com/llvm/llvm-project/commit/3c88abe3206bb944566ff4b62aa4b9874327f37d.diff
LOG: [clang-tidy][readability-identifier-length] Add a line count threshold (#185319)
This PR implements the feature described in #185318
A new parameter named `LineCountThreshold` is added to the
`readability-identifier-length` check, which controls how many lines of
code must separate the the last use of a variable from its declaration
for the check to warn. For backwards-compatibility, the default value
for this parameter is set to 0.
Increasing the threshold to 1 allows for short names in one-liners (for
example: `std::transform(..., [](auto i){ return i*i; });`), and in the
general case with `LineCountThreshold = N` a variable is allowed to have
a shorter name than otherwise required if it is never used again after
`N` lines (including its declaration line).
This feature is implemented ~using a secondary `MatchFinder`~ by calling
the `utils::decl_ref_expr::allDeclRefExprs` helper function for each
variable with a short name. For performance reasons, the new piece of
code is short-circuited if `LineCountThreshold` is set to 0 (the default
value).
Added:
clang-tools-extra/test/clang-tidy/checkers/readability/identifier-length-line-count-threshold.cpp
Modified:
clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.cpp
clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/readability/identifier-length.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.cpp
index a6204de16224d..7182d424c0273 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "IdentifierLengthCheck.h"
+#include "../utils/DeclRefExprUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
@@ -21,6 +22,7 @@ const char DefaultIgnoredLoopCounterNames[] = "^[ijk_]$";
const char DefaultIgnoredVariableNames[] = "";
const char DefaultIgnoredExceptionVariableNames[] = "^[e]$";
const char DefaultIgnoredParameterNames[] = "^[n]$";
+const unsigned DefaultLineCountThreshold = 0;
const char ErrorMessage[] =
"%select{variable|exception variable|loop variable|"
@@ -49,7 +51,9 @@ IdentifierLengthCheck::IdentifierLengthCheck(StringRef Name,
IgnoredExceptionVariableNames(IgnoredExceptionVariableNamesInput),
IgnoredParameterNamesInput(
Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames)),
- IgnoredParameterNames(IgnoredParameterNamesInput) {}
+ IgnoredParameterNames(IgnoredParameterNamesInput),
+ LineCountThreshold(
+ Options.get("LineCountThreshold", DefaultLineCountThreshold)) {}
void IdentifierLengthCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "MinimumVariableNameLength", MinimumVariableNameLength);
@@ -62,6 +66,7 @@ void IdentifierLengthCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoredExceptionVariableNames",
IgnoredExceptionVariableNamesInput);
Options.store(Opts, "IgnoredParameterNames", IgnoredParameterNamesInput);
+ Options.store(Opts, "LineCountThreshold", LineCountThreshold);
}
void IdentifierLengthCheck::registerMatchers(MatchFinder *Finder) {
@@ -85,6 +90,39 @@ void IdentifierLengthCheck::registerMatchers(MatchFinder *Finder) {
this);
}
+static std::optional<unsigned> countLinesToLastUse(const VarDecl *Var,
+ const SourceManager *SrcMgr,
+ ASTContext *Ctx) {
+ const auto *ParentScope = llvm::dyn_cast<FunctionDecl>(Var->getDeclContext());
+ if (ParentScope == nullptr)
+ return std::nullopt;
+
+ auto AllRefs =
+ utils::decl_ref_expr::allDeclRefExprs(*Var, *ParentScope, *Ctx);
+
+ const unsigned DeclLine = SrcMgr->getSpellingLineNumber(Var->getLocation());
+ const unsigned LastUseLine = std::transform_reduce(
+ AllRefs.begin(), AllRefs.end(), DeclLine,
+ [](unsigned Lhs, unsigned Rhs) -> unsigned { return std::max(Lhs, Rhs); },
+ [&](const DeclRefExpr *RefToVar) -> unsigned {
+ return SrcMgr->getSpellingLineNumber(RefToVar->getLocation());
+ });
+
+ return LastUseLine - DeclLine + 1;
+}
+
+static bool isShortLived(const VarDecl *Var, const SourceManager *SrcMgr,
+ ASTContext *Ctx, unsigned LineCountThreshold) {
+ if (LineCountThreshold == 0)
+ return false;
+
+ std::optional<unsigned> LineCount = countLinesToLastUse(Var, SrcMgr, Ctx);
+ if (LineCount && LineCount.value() <= LineCountThreshold)
+ return true;
+
+ return false;
+}
+
void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
const auto *StandaloneVar = Result.Nodes.getNodeAs<VarDecl>("standaloneVar");
if (StandaloneVar) {
@@ -97,6 +135,10 @@ void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
IgnoredVariableNames.match(VarName))
return;
+ if (isShortLived(StandaloneVar, Result.SourceManager, Result.Context,
+ LineCountThreshold))
+ return;
+
diag(StandaloneVar->getLocation(), ErrorMessage)
<< 0 << StandaloneVar << MinimumVariableNameLength;
}
@@ -111,6 +153,10 @@ void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
IgnoredExceptionVariableNames.match(VarName))
return;
+ if (isShortLived(ExceptionVarName, Result.SourceManager, Result.Context,
+ LineCountThreshold))
+ return;
+
diag(ExceptionVarName->getLocation(), ErrorMessage)
<< 1 << ExceptionVarName << MinimumExceptionNameLength;
}
@@ -126,6 +172,10 @@ void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
IgnoredLoopCounterNames.match(VarName))
return;
+ if (isShortLived(LoopVar, Result.SourceManager, Result.Context,
+ LineCountThreshold))
+ return;
+
diag(LoopVar->getLocation(), ErrorMessage)
<< 2 << LoopVar << MinimumLoopCounterNameLength;
}
@@ -141,6 +191,10 @@ void IdentifierLengthCheck::check(const MatchFinder::MatchResult &Result) {
IgnoredParameterNames.match(VarName))
return;
+ if (isShortLived(ParamVar, Result.SourceManager, Result.Context,
+ LineCountThreshold))
+ return;
+
diag(ParamVar->getLocation(), ErrorMessage)
<< 3 << ParamVar << MinimumParameterNameLength;
}
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.h
index 3adaf50bc57a1..2a59788260f8c 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierLengthCheck.h
@@ -42,6 +42,8 @@ class IdentifierLengthCheck : public ClangTidyCheck {
std::string IgnoredParameterNamesInput;
llvm::Regex IgnoredParameterNames;
+
+ const unsigned LineCountThreshold;
};
} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 95ed0061d654c..777ff064c08b7 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -498,6 +498,11 @@ Changes in existing checks
now uses separate note diagnostics for each uninitialized enumerator, making
it easier to see which specific enumerators need explicit initialization.
+- Improved :doc:`readability-identifier-length
+ <clang-tidy/checks/readability/identifier-length>` check by adding a new
+ option, named `LineCountThreshold`, to silence warnings for short-lived
+ variables, based on distance between declaration and last use.
+
- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check:
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-length.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-length.rst
index c4d39a28a4cb8..fce9592e699fb 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-length.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-length.rst
@@ -18,6 +18,7 @@ The following options are described below:
- :option:`MinimumLoopCounterNameLength`, :option:`IgnoredLoopCounterNames`
- :option:`MinimumExceptionNameLength`,
:option:`IgnoredExceptionVariableNames`
+ - :option:`LineCountThreshold`
.. option:: MinimumVariableNameLength
@@ -121,3 +122,20 @@ The following options are described below:
catch (const std::exception& e) {
// ...
}
+
+.. option:: LineCountThreshold
+
+ Defines the minimum number of lines required between declaration and last
+ use for a diagnostic to be issued. The default value for this option is 0,
+ which corresponds to all variables being flagged. This option only affects
+ the behavior regarding local variables: a warning is always issued when a
+ global variable has a short name, because globals can potentially be used
+ across multiple files.
+
+ .. code-block:: c++
+
+ // In this example, a warning will be issued if LineCountThreshold < N
+ int a = 0; // First line (declaration line)
+ a = 1; // Second line
+ // ...
+ last_use_of(a); // N-th line
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-length-line-count-threshold.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-length-line-count-threshold.cpp
new file mode 100644
index 0000000000000..17e75053eabfd
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-length-line-count-threshold.cpp
@@ -0,0 +1,85 @@
+// RUN: %check_clang_tidy %s readability-identifier-length %t \
+// RUN: -config='{CheckOptions: \
+// RUN: {readability-identifier-length.LineCountThreshold: 3}}' \
+// RUN: -- -fexceptions
+
+struct myexcept {
+ int val;
+};
+
+template<typename... Ts>
+void doIt(Ts...);
+
+#define MY_MACRO(arg) doIt(arg, arg)
+
+int g = 0;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable name 'g' is too short, expected at least 3 characters [readability-identifier-length]
+
+void shouldWarn(int z)
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: parameter name 'z' is too short, expected at least 3 characters [readability-identifier-length]
+{
+ int i = 5;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable name 'i' is too short, expected at least 3 characters [readability-identifier-length]
+ ++i;
+
+ for (int m = 0; m < 5; ++m)
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: loop variable name 'm' is too short, expected at least 2 characters [readability-identifier-length]
+ {
+ doIt(i);
+ doIt(m);
+ }
+
+ try {
+ doIt(z);
+ } catch (const myexcept &x)
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: exception variable name 'x' is too short, expected at least 2 characters [readability-identifier-length]
+ {
+ doIt(x);
+ }
+
+ int a = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable name 'a' is too short, expected at least 3 characters [readability-identifier-length]
+ ++a;
+ MY_MACRO(a);
+
+ int b = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable name 'b' is too short, expected at least 3 characters [readability-identifier-length]
+ [&](){
+ doIt(b);
+ }();
+
+ int c = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable name 'c' is too short, expected at least 3 characters [readability-identifier-length]
+ [=](){
+ doIt(c);
+ }();
+}
+
+void shouldNotWarn(int m)
+{
+ doIt(m);
+
+ int v = 5;
+ ++v;
+ doIt(v);
+
+ for (int a = 0; a < 42; ++a)
+ {
+ doIt(a);
+ }
+
+ try {
+ doIt();
+ } catch (const myexcept &x) {
+ doIt(x);
+ }
+
+ int a = 0;
+ MY_MACRO(a);
+
+ int b = 0;
+ [&](){ doIt(b); }();
+
+ int c = 0;
+ [=](){ doIt(c); }();
+}
More information about the cfe-commits
mailing list