https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/205655
>From e28e9e234b6165b49884cf254e1fb4efe44fe756 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sat, 6 Sep 2025 17:35:36 -0700 Subject: [PATCH 01/11] [clang-tidy] make `misc-const-correctness` work with `auto` variables and lambdas --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 12 ++------ clang-tools-extra/docs/ReleaseNotes.rst | 4 +++ .../const-correctness-pointer-as-values.cpp | 6 +++- .../misc/const-correctness-templates.cpp | 6 ++++ .../const-correctness-transform-values.cpp | 6 ---- .../misc/const-correctness-values.cpp | 28 ++++++++++++++++--- 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index b32507d66cbac..390046873f2d7 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -98,17 +98,13 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))), hasType(referenceType(pointee(substTemplateTypeParmType())))); - auto AllowedTypeDecl = namedDecl( + const auto AllowedTypeDecl = namedDecl( anyOf(matchers::matchesAnyListedName(AllowedTypes), usingShadowDecl())); const auto AllowedType = hasType(qualType( anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl), pointerType(pointee(hasDeclaration(AllowedTypeDecl)))))); - const auto AutoTemplateType = varDecl( - anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))), - hasType(pointerType(pointee(autoType()))))); - const auto FunctionPointerRef = hasType(hasCanonicalType(referenceType(pointee(functionType())))); @@ -117,10 +113,8 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { const auto LocalValDecl = varDecl( isLocal(), hasInitializer(anything()), unless(anyOf(ConstType, ConstReference, TemplateType, - hasInitializer(isInstantiationDependent()), AutoTemplateType, - RValueReference, FunctionPointerRef, - hasType(cxxRecordDecl(isLambda())), isImplicit(), - AllowedType))); + hasInitializer(isInstantiationDependent()), RValueReference, + FunctionPointerRef, isImplicit(), AllowedType))); // Match the function scope for which the analysis of all local variables // shall be run. diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 780e5b3fc21cf..1ae709c9a2744 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -185,6 +185,10 @@ Changes in existing checks adding an option to allow pointer arithmetic via prefix/postfix increment or decrement operators. +- Improved :doc:`misc-const-correctness + <clang-tidy/checks/misc/const-correctness>` check to diagnose + variables declared with ``auto``. + - Improved :doc:`misc-header-include-cycle <clang-tidy/checks/misc/header-include-cycle>` check performance. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp index 74be3dccc9daa..c675482513819 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp @@ -33,7 +33,9 @@ void range_for() { int *p_local2[2] = {nullptr, nullptr}; // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'int *[2]' can be declared 'const' // CHECK-FIXES: int *const p_local2[2] - for (const auto *con_ptr : p_local2) { + for (const auto *p_local3 : p_local2) { + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local3' of type 'const int *' can be declared 'const' + // CHECK-FIXES: for (const auto *const p_local3 : p_local2) } } @@ -62,6 +64,8 @@ void EmitProtocolMethodList(T &&Methods) { // CHECK-FIXES: SmallVector<const int *> const p_local0 SmallVector<const int *> np_local0; for (const auto *I : Methods) { + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'I' of type 'const int *' can be declared 'const' + // CHECK-FIXES: for (const auto *const I : Methods) if (I == nullptr) np_local0.push_back(I); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index 5a890f212a603..323d687cc4f24 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -10,6 +10,12 @@ template <typename T> void type_dependent_variables() { T value = 42; auto &ref = value; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'ref' of type 'int &' can be declared 'const' + // CHECK-FIXES: auto const&ref = value; + // + // FIXME: This is a false positive, the reference points to a template type + // and needs to be excluded from analysis. See the 'more_template_locals()' + // test in 'const-correctness-values.cpp' for more examples of the problem. T &templateRef = value; int value_int = 42; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp index 190d8ecec4c59..46ecb521b74cf 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp @@ -14,12 +14,6 @@ int scoped; float np_scoped = 1; // namespace variables are like globals } // namespace foo -// Lambdas should be ignored, because they do not follow the normal variable -// semantic (e.g. the type is only known to the compiler). -void lambdas() { - auto Lambda = [](int i) { return i < 0; }; -} - void some_function(double, wchar_t); void some_function(double np_arg0, wchar_t np_arg1) { diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index 17dcf12e2536c..726caa5ffc531 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -27,10 +27,19 @@ int np_anonymous_global; int p_anonymous_global = 43; } // namespace -// Lambdas should be ignored, because they do not follow the normal variable -// semantic (e.g. the type is only known to the compiler). void lambdas() { auto Lambda = [](int i) { return i < 0; }; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'Lambda' of type '{{.*}}' can be declared 'const' + // CHECK-FIXES: auto const Lambda + + auto LambdaWithMutableCallOperator = []() mutable {}; + LambdaWithMutableCallOperator(); + + int x = 0; + auto LambdaModifyingCapture = [&x] { ++x; }; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'LambdaModifyingCapture' of type '{{.*}}' can be declared 'const' + // CHECK-FIXES: auto const LambdaModifyingCapture + LambdaModifyingCapture(); } void some_function(double, wchar_t); @@ -965,14 +974,23 @@ template <typename T> T *return_ptr() { return &return_ref<T>(); } void auto_usage_variants() { + auto auto_int = int{}; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_int' of type 'int' can be declared 'const' + // CHECK-FIXES: auto const auto_int + auto auto_val0 = int{}; // CHECK-FIXES-NOT: auto const auto_val0 auto &auto_val1 = auto_val0; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_val1' of type 'int &' can be declared 'const' + // CHECK-FIXES: auto const&auto_val1 auto *auto_val2 = &auto_val0; auto auto_ref0 = return_ref<int>(); - // CHECK-FIXES-NOT: auto const auto_ref0 - auto &auto_ref1 = return_ref<int>(); // Bad + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_ref0' of type 'int' can be declared 'const' + // CHECK-FIXES: auto const auto_ref0 + auto &auto_ref1 = return_ref<int>(); + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_ref1' of type 'int &' can be declared 'const' + // CHECK-FIXES: auto const&auto_ref1 auto *auto_ref2 = return_ptr<int>(); auto auto_ptr0 = return_ptr<int>(); @@ -984,6 +1002,8 @@ void auto_usage_variants() { auto auto_td0 = MyTypedef{}; // CHECK-FIXES-NOT: auto const auto_td0 auto &auto_td1 = auto_td0; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_td1' of type 'MyTypedef &' (aka 'int &') can be declared 'const' + // CHECK-FIXES: auto const&auto_td1 auto *auto_td2 = &auto_td0; } >From 07a85c1fd3e0fd62651b2772cd0a7c51b0d6492b Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sun, 7 Sep 2025 12:47:48 -0700 Subject: [PATCH 02/11] make comment less confusing --- .../clang-tidy/checkers/misc/const-correctness-templates.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index 323d687cc4f24..98bf27033c3b5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -9,14 +9,14 @@ template <typename T> void type_dependent_variables() { T value = 42; + T &templateRef = value; + auto &ref = value; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'ref' of type 'int &' can be declared 'const' // CHECK-FIXES: auto const&ref = value; - // // FIXME: This is a false positive, the reference points to a template type // and needs to be excluded from analysis. See the 'more_template_locals()' // test in 'const-correctness-values.cpp' for more examples of the problem. - T &templateRef = value; int value_int = 42; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'value_int' of type 'int' can be declared 'const' >From c8043f835d746e435c45521c4cf8acc2f7714597 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sun, 7 Sep 2025 13:07:11 -0700 Subject: [PATCH 03/11] add tests ensuring rvalue references aren't diagnosed --- .../clang-tidy/checkers/misc/const-correctness-values.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index 726caa5ffc531..c0020a2b3c182 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -94,6 +94,11 @@ void ignore_reference_to_pointers() { int *&np_local1 = np_local0; } +void ignore_rvalue_references() { + int &&np_local0 = 42; + auto &&np_local1 = 42; +} + void some_lambda_environment_capture_all_by_reference(double np_arg0) { int np_local0 = 0; int p_local0 = 1; >From ef1540510ba6f360f2e2329bb559814562dc7562 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sat, 20 Dec 2025 11:45:46 -0700 Subject: [PATCH 04/11] Add options to control analyzing auto variables and lambdas --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 14 +++++++++++++- .../clang-tidy/misc/ConstCorrectnessCheck.h | 2 ++ .../clang-tidy/checks/misc/const-correctness.rst | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 27f4b7447f1f4..358189e238c7b 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -33,6 +33,10 @@ AST_MATCHER(ReferenceType, isSpelledAsLValue) { return Node.isSpelledAsLValue(); } AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); } + +AST_MATCHER(TypeLoc, hasContainedAutoType) { + return !TypeLoc.getContainedAutoTypeLoc().isNull(); +} } // namespace ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name, @@ -41,6 +45,8 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name, AnalyzePointers(Options.get("AnalyzePointers", true)), AnalyzeReferences(Options.get("AnalyzeReferences", true)), AnalyzeValues(Options.get("AnalyzeValues", true)), + AnalyzeAutoVariables(Options.get("AnalyzeAutoVariables", true)), + AnalyzeLambdas(Options.get("AnalyzeLambdas", true)), WarnPointersAsPointers(Options.get("WarnPointersAsPointers", true)), WarnPointersAsValues(Options.get("WarnPointersAsValues", false)), @@ -66,6 +72,8 @@ void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "AnalyzePointers", AnalyzePointers); Options.store(Opts, "AnalyzeReferences", AnalyzeReferences); Options.store(Opts, "AnalyzeValues", AnalyzeValues); + Options.store(Opts, "AnalyzeAutoVariables", AnalyzeAutoVariables); + Options.store(Opts, "AnalyzeLambdas", AnalyzeLambdas); Options.store(Opts, "WarnPointersAsPointers", WarnPointersAsPointers); Options.store(Opts, "WarnPointersAsValues", WarnPointersAsValues); @@ -114,7 +122,11 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { isLocal(), hasInitializer(anything()), unless(anyOf(ConstType, ConstReference, TemplateType, hasInitializer(isInstantiationDependent()), RValueReference, - FunctionPointerRef, isImplicit(), AllowedType))); + FunctionPointerRef, isImplicit(), AllowedType)), + AnalyzeLambdas ? TypeMatcher(anything()) + : unless(hasType(cxxRecordDecl(isLambda()))), + AnalyzeAutoVariables ? TypeLocMatcher(anything()) + : unless(hasTypeLoc(hasContainedAutoType()))); // Match the function scope for which the analysis of all local variables // shall be run. diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h index fafcac407e029..449aaaf93c0c0 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h @@ -41,6 +41,8 @@ class ConstCorrectnessCheck : public ClangTidyCheck { const bool AnalyzePointers; const bool AnalyzeReferences; const bool AnalyzeValues; + const bool AnalyzeAutoVariables; + const bool AnalyzeLambdas; const bool WarnPointersAsPointers; const bool WarnPointersAsValues; diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst index 01f3b75fc1ffa..9caa5d2478472 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst @@ -104,6 +104,18 @@ Options :option:`WarnPointersAsValues` and :option:`WarnPointersAsPointers`. Default is `true`. +.. option:: AnalyzeAutoVariables + + Enable or disable the analysis of variables declared with ``auto``, + such as ``auto i = 10;`` or ``auto *ptr = &i``. Default is `true`. + +.. option:: AnalyzeLambdas + + Enable or disable the analysis of lambda variables, like + ``auto f = [] { return 10; };``. For this option to have any + effect, `AnalyzeAutoVariables` must be `true` as well. + Default is `true`. + .. option:: WarnPointersAsValues This option enables the suggestion for ``const`` of the pointer itself. >From 1d3539c6da53d63fee7eac3e72c0d6c26d8aabf6 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sat, 20 Dec 2025 11:58:16 -0700 Subject: [PATCH 05/11] Fix build --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 13 +++++++----- .../checkers/misc/const-correctness-auto.cpp | 20 +++++++++++++++++++ .../misc/const-correctness-lambdas.cpp | 14 +++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 358189e238c7b..ee71464ade695 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -16,6 +16,7 @@ #include <cassert> using namespace clang::ast_matchers; +using namespace clang::ast_matchers::internal; namespace clang::tidy::misc { @@ -35,7 +36,7 @@ AST_MATCHER(ReferenceType, isSpelledAsLValue) { AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); } AST_MATCHER(TypeLoc, hasContainedAutoType) { - return !TypeLoc.getContainedAutoTypeLoc().isNull(); + return !Node.getContainedAutoTypeLoc().isNull(); } } // namespace @@ -123,10 +124,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { unless(anyOf(ConstType, ConstReference, TemplateType, hasInitializer(isInstantiationDependent()), RValueReference, FunctionPointerRef, isImplicit(), AllowedType)), - AnalyzeLambdas ? TypeMatcher(anything()) - : unless(hasType(cxxRecordDecl(isLambda()))), - AnalyzeAutoVariables ? TypeLocMatcher(anything()) - : unless(hasTypeLoc(hasContainedAutoType()))); + AnalyzeLambdas + ? Matcher<VarDecl>(anything()) + : Matcher<VarDecl>(unless(hasType(cxxRecordDecl(isLambda())))), + AnalyzeAutoVariables + ? Matcher<VarDecl>(anything()) + : Matcher<VarDecl>(unless(hasTypeLoc(hasContainedAutoType())))); // Match the function scope for which the analysis of all local variables // shall be run. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp new file mode 100644 index 0000000000000..243686435d8f4 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp @@ -0,0 +1,20 @@ +// RUN: %check_clang_tidy %s misc-const-correctness %t \ +// RUN: -config='{CheckOptions: \ +// RUN: {misc-const-correctness.AnalyzeAutoVariables: false,\ +// RUN: misc-const-correctness.AnalyzeValues: true,\ +// RUN: misc-const-correctness.AnalyzeLambdas: true,\ +// RUN: misc-const-correctness.WarnPointersAsValues: true,\ +// RUN: misc-const-correctness.WarnPointersAsPointers: true,\ +// RUN: misc-const-correctness.TransformPointersAsValues: true}}' \ +// RUN: -- -fno-delayed-template-parsing + +void auto_types_ignored() { + int i = 0; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'i' of type 'int' can be declared 'const' + // CHECK-FIXES: int const i = 0; + + auto auto_i = 0; + auto& auto_ref = auto_i; + auto auto_lambda = [] {}; + auto *auto_ptr = nullptr; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp new file mode 100644 index 0000000000000..b037a8d6a113a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s misc-const-correctness %t \ +// RUN: -config='{CheckOptions: \ +// RUN: {misc-const-correctness.AnalyzeAutoVariables: true,\ +// RUN: misc-const-correctness.AnalyzeValues: true,\ +// RUN: misc-const-correctness.AnalyzeLambdas: false}}' \ +// RUN: -- -fno-delayed-template-parsing + +void lambdas_ignored_but_other_auto_variables_diagnosed() { + auto i = 0; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'i' of type 'int' can be declared 'const' + // CHECK-FIXES: auto const i = 0; + + auto lambda = [] {}; +} >From b63136185f1928ca16ce22bb3cce3839c4015558 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin <[email protected]> Date: Sun, 11 Jan 2026 12:36:30 -0800 Subject: [PATCH 06/11] Fix tests; add test with reassigned lambda --- .../checkers/misc/const-correctness-auto.cpp | 2 +- .../const-correctness-pointer-as-values.cpp | 4 +-- .../misc/const-correctness-values.cpp | 25 +++++++++++-------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp index 243686435d8f4..210e8ae74d806 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp @@ -16,5 +16,5 @@ void auto_types_ignored() { auto auto_i = 0; auto& auto_ref = auto_i; auto auto_lambda = [] {}; - auto *auto_ptr = nullptr; + auto *auto_ptr = static_cast<void *>(nullptr); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp index e048fd1f66b3d..1c5e37053aa12 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp @@ -35,7 +35,7 @@ void range_for() { // CHECK-FIXES: int *const p_local2[2] = {nullptr, nullptr}; for (const auto *p_local3 : p_local2) { // CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local3' of type 'const int *' can be declared 'const' - // CHECK-FIXES: for (const auto *const p_local3 : p_local2) + // CHECK-FIXES: for (const auto *const p_local3 : p_local2) { } } @@ -64,7 +64,7 @@ void EmitProtocolMethodList(T &&Methods) { SmallVector<const int *> np_local0; for (const auto *I : Methods) { // CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'I' of type 'const int *' can be declared 'const' - // CHECK-FIXES: for (const auto *const I : Methods) + // CHECK-FIXES: for (const auto *const I : Methods) { if (I == nullptr) np_local0.push_back(I); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index 2f11464885a4f..6b993d43c96b6 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -30,15 +30,20 @@ int p_anonymous_global = 43; void lambdas() { auto Lambda = [](int i) { return i < 0; }; // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'Lambda' of type '{{.*}}' can be declared 'const' - // CHECK-FIXES: auto const Lambda + // CHECK-FIXES: auto const Lambda = [](int i) { return i < 0; }; auto LambdaWithMutableCallOperator = []() mutable {}; LambdaWithMutableCallOperator(); +#if __cplusplus >= 202002L + auto ReassignedLambda = [] {}; + ReassignedLambda = {}; +#endif + int x = 0; auto LambdaModifyingCapture = [&x] { ++x; }; // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'LambdaModifyingCapture' of type '{{.*}}' can be declared 'const' - // CHECK-FIXES: auto const LambdaModifyingCapture + // CHECK-FIXES: auto const LambdaModifyingCapture = [&x] { ++x; }; LambdaModifyingCapture(); } @@ -981,34 +986,34 @@ T *return_ptr() { return &return_ref<T>(); } void auto_usage_variants() { auto auto_int = int{}; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_int' of type 'int' can be declared 'const' - // CHECK-FIXES: auto const auto_int + // CHECK-FIXES: auto const auto_int = int{}; auto auto_val0 = int{}; - // CHECK-FIXES-NOT: auto const auto_val0 + // CHECK-FIXES-NOT: auto const auto_val0 = int{}; auto &auto_val1 = auto_val0; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_val1' of type 'int &' can be declared 'const' - // CHECK-FIXES: auto const&auto_val1 + // CHECK-FIXES: auto const&auto_val1 = auto_val0; auto *auto_val2 = &auto_val0; auto auto_ref0 = return_ref<int>(); // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_ref0' of type 'int' can be declared 'const' - // CHECK-FIXES: auto const auto_ref0 + // CHECK-FIXES: auto const auto_ref0 = return_ref<int>(); auto &auto_ref1 = return_ref<int>(); // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_ref1' of type 'int &' can be declared 'const' - // CHECK-FIXES: auto const&auto_ref1 + // CHECK-FIXES: auto const&auto_ref1 = return_ref<int>(); auto *auto_ref2 = return_ptr<int>(); auto auto_ptr0 = return_ptr<int>(); - // CHECK-FIXES-NOT: auto const auto_ptr0 + // CHECK-FIXES-NOT: auto const auto_ptr0 = return_ptr<int>(); auto &auto_ptr1 = auto_ptr0; auto *auto_ptr2 = return_ptr<int>(); using MyTypedef = int; auto auto_td0 = MyTypedef{}; - // CHECK-FIXES-NOT: auto const auto_td0 + // CHECK-FIXES-NOT: auto const auto_td0 = MyTypedef{}; auto &auto_td1 = auto_td0; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'auto_td1' of type 'MyTypedef &' (aka 'int &') can be declared 'const' - // CHECK-FIXES: auto const&auto_td1 + // CHECK-FIXES: auto const&auto_td1 = auto_td0; auto *auto_td2 = &auto_td0; } >From c23e77bf892a2fb86ae6fe8e82f26ddfff39f3cb Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Wed, 24 Jun 2026 22:39:48 +0300 Subject: [PATCH 07/11] ~ --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 4ed0946c54055..7d8497f4754c3 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -139,16 +139,13 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { const auto CommonExcludeTypes = anyOf(ConstType, ConstReference, RValueReference, TemplateType, - FunctionPointerRef, hasType(cxxRecordDecl(isLambda())), - AutoTemplateType, isImplicit(), AllowedType); + FunctionPointerRef, isImplicit(), AllowedType); // Match local variables which could be 'const' if not modified later. // Example: `int i = 10` would match `int i`. const auto LocalValDecl = varDecl( - isLocal(), hasInitializer(anything()), - unless(anyOf(ConstType, ConstReference, TemplateType, - hasInitializer(isInstantiationDependent()), RValueReference, - FunctionPointerRef, isImplicit(), AllowedType)), + isLocal(), hasInitializer(unless(isInstantiationDependent())), + unless(CommonExcludeTypes), AnalyzeLambdas ? Matcher<VarDecl>(anything()) : Matcher<VarDecl>(unless(hasType(cxxRecordDecl(isLambda())))), @@ -171,7 +168,9 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { if (AnalyzeParameters) { const auto ParamMatcher = - parmVarDecl(unless(CommonExcludeTypes), unless(isUnnamed()), + parmVarDecl(unless(CommonExcludeTypes), unless(AutoTemplateType), + unless(hasType(cxxRecordDecl(isLambda()))), + unless(isUnnamed()), anyOf(hasType(referenceType()), hasType(pointerType()))) .bind("value"); >From d32e8ef11d3f2220a1b83fb0d5d29c0c0ebd36ca Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Wed, 24 Jun 2026 23:01:24 +0300 Subject: [PATCH 08/11] ~ --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 7 ++++++- .../docs/clang-tidy/checks/misc/const-correctness.rst | 4 ++-- .../clang-tidy/checkers/misc/const-correctness-auto.cpp | 2 ++ .../clang-tidy/checkers/misc/const-correctness-lambdas.cpp | 4 ++++ .../clang-tidy/checkers/misc/const-correctness-values.cpp | 4 ++-- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 7d8497f4754c3..2c6968858c646 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -16,7 +16,7 @@ #include <cassert> using namespace clang::ast_matchers; -using namespace clang::ast_matchers::internal; +using clang::ast_matchers::internal::Matcher; namespace clang::tidy::misc { @@ -82,6 +82,11 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name, "The check 'misc-const-correctness' will not " "perform any analysis because 'AnalyzeValues', " "'AnalyzeReferences' and 'AnalyzePointers' are false."); + + if (AnalyzeLambdas && !AnalyzeAutoVariables) + this->configurationDiag("The check 'misc-const-correctness' will not " + "analyze lambdas because 'AnalyzeLambdas' has no " + "effect while 'AnalyzeAutoVariables' is false."); } void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst index ce699e06b9276..9317610ec3e56 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst @@ -109,13 +109,13 @@ Options .. option:: AnalyzeAutoVariables Enable or disable the analysis of variables declared with ``auto``, - such as ``auto i = 10;`` or ``auto *ptr = &i``. Default is `true`. + such as ``auto i = 10;`` or ``auto *ptr = &i;``. Default is `true`. .. option:: AnalyzeLambdas Enable or disable the analysis of lambda variables, like ``auto f = [] { return 10; };``. For this option to have any - effect, `AnalyzeAutoVariables` must be `true` as well. + effect, :option:`AnalyzeAutoVariables` must be `true` as well. Default is `true`. .. option:: AnalyzeParameters diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp index 210e8ae74d806..8e0c503c78c09 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp @@ -8,6 +8,8 @@ // RUN: misc-const-correctness.TransformPointersAsValues: true}}' \ // RUN: -- -fno-delayed-template-parsing +// CHECK-MESSAGES: warning: The check 'misc-const-correctness' will not analyze lambdas because 'AnalyzeLambdas' has no effect while 'AnalyzeAutoVariables' is false. [clang-tidy-config] + void auto_types_ignored() { int i = 0; // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'i' of type 'int' can be declared 'const' diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp index b037a8d6a113a..da3b69b4ff1a7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp @@ -11,4 +11,8 @@ void lambdas_ignored_but_other_auto_variables_diagnosed() { // CHECK-FIXES: auto const i = 0; auto lambda = [] {}; + + int x = 0; + auto lambda_modifying_capture = [&x] { ++x; }; + lambda_modifying_capture(); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index 314eeb350bf9f..f95676f475f9d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -30,7 +30,7 @@ int p_anonymous_global = 43; void lambdas() { auto Lambda = [](int i) { return i < 0; }; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'Lambda' of type '{{.*}}' can be declared 'const' + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'Lambda' of type '(lambda at {{.*}})' can be declared 'const' // CHECK-FIXES: auto const Lambda = [](int i) { return i < 0; }; auto LambdaWithMutableCallOperator = []() mutable {}; @@ -43,7 +43,7 @@ void lambdas() { int x = 0; auto LambdaModifyingCapture = [&x] { ++x; }; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'LambdaModifyingCapture' of type '{{.*}}' can be declared 'const' + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'LambdaModifyingCapture' of type '(lambda at {{.*}})' can be declared 'const' // CHECK-FIXES: auto const LambdaModifyingCapture = [&x] { ++x; }; LambdaModifyingCapture(); } >From b7d0d797380c29b3afa29914e76ebe18862bd721 Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Wed, 24 Jun 2026 23:24:29 +0300 Subject: [PATCH 09/11] ~ --- clang-tools-extra/docs/ReleaseNotes.rst | 5 --- .../const-correctness-auto-parameters.cpp | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8c587f35bdf71..8add70931d86a 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -562,11 +562,6 @@ Changes in existing checks - Fixed false positives when pointers were later passed or bound through ``const``-qualified pointer references. -- Improved :doc:`misc-coroutine-hostile-raii - <clang-tidy/checks/misc/coroutine-hostile-raii>` check by adding the option - `AllowedCallees`, that allows exempting safely awaitable callees from the - check. - - Improved :doc:`misc-multiple-inheritance <clang-tidy/checks/misc/multiple-inheritance>` by avoiding false positives when virtual inheritance causes concrete bases to be counted more than once. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp new file mode 100644 index 0000000000000..7d33de3fb8ec0 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp @@ -0,0 +1,34 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s misc-const-correctness %t -- \ +// RUN: -config="{CheckOptions: {\ +// RUN: misc-const-correctness.AnalyzeParameters: true, \ +// RUN: misc-const-correctness.WarnPointersAsValues: true, \ +// RUN: misc-const-correctness.TransformPointersAsValues: true \ +// RUN: }}" -- -fno-delayed-template-parsing + +struct Bar { + void const_method() const; +}; + +void abbreviated_template_ref_param(auto& b) { + b.const_method(); +} + +void abbreviated_template_ptr_param(auto* b) { + b->const_method(); +} + +void abbreviated_template_decl(const auto& b); + +void plain_ref_param(Bar& b) { + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: variable 'b' of type 'Bar &' can be declared 'const' + // CHECK-FIXES: void plain_ref_param(Bar const& b) { + b.const_method(); +} + +void generic_lambda_param() { + auto generic = [](auto& b) { b.const_method(); }; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'generic' of type '(lambda at {{.*}})' can be declared 'const' + // CHECK-FIXES: auto const generic = [](auto& b) { b.const_method(); }; + Bar bar; + generic(bar); +} >From 429d14a00b52943edea68053a3b9347f7d62c524 Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Thu, 25 Jun 2026 00:00:02 +0300 Subject: [PATCH 10/11] ~ --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 2 + .../misc/const-correctness-templates.cpp | 53 +++++++++++++++++-- .../misc/const-correctness-values.cpp | 12 ++--- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 2c6968858c646..7201266c46783 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -151,6 +151,8 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { const auto LocalValDecl = varDecl( isLocal(), hasInitializer(unless(isInstantiationDependent())), unless(CommonExcludeTypes), + unless( + allOf(hasType(referenceType(pointee(autoType()))), isInstantiated())), AnalyzeLambdas ? Matcher<VarDecl>(anything()) : Matcher<VarDecl>(unless(hasType(cxxRecordDecl(isLambda())))), diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index 98bf27033c3b5..fc9e67a944607 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -11,22 +11,65 @@ void type_dependent_variables() { T value = 42; T &templateRef = value; + // 'auto &ref' deduces to a dependent type, so the variable is not analyzed + // inside the template instantiation (the deduced type may differ between + // instantiations). auto &ref = value; - // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'ref' of type 'int &' can be declared 'const' - // CHECK-FIXES: auto const&ref = value; - // FIXME: This is a false positive, the reference points to a template type - // and needs to be excluded from analysis. See the 'more_template_locals()' - // test in 'const-correctness-values.cpp' for more examples of the problem. int value_int = 42; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'value_int' of type 'int' can be declared 'const' // CHECK-FIXES: int const value_int = 42; + + // Only dependent 'auto &' references are excluded. 'auto' variables deduced + // from a non-dependent initializer are still analyzed (here via the + // uninstantiated pattern), as are 'auto' values and 'auto' pointers. + int concrete = 42; + auto &ref_concrete = concrete; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'ref_concrete' of type 'int &' can be declared 'const' + // CHECK-FIXES: auto const&ref_concrete = concrete; + auto val_concrete = concrete; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'val_concrete' of type 'int' can be declared 'const' + // CHECK-FIXES: auto const val_concrete = concrete; + auto *ptr_concrete = &concrete; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: pointee of variable 'ptr_concrete' of type 'int *' can be declared 'const' + // CHECK-FIXES: auto const*ptr_concrete = &concrete; } void instantiate_template_cases() { type_dependent_variables<int>(); type_dependent_variables<float>(); } +// Class template member functions are instantiations too: a dependent +// 'auto &' reference must be excluded there as well, while non-dependent +// 'auto' variables stay analyzed. +template <typename T> +struct ClassTemplate { + int method() { + T value{}; + auto &dependent_ref = value; + + int c1 = 42; + // CHECK-MESSAGES:[[@LINE-1]]:5: warning: variable 'c1' of type 'int' can be declared 'const' + // CHECK-FIXES: int const c1 = 42; + auto &concrete_ref = c1; + // CHECK-MESSAGES:[[@LINE-1]]:5: warning: variable 'concrete_ref' of type 'int &' can be declared 'const' + // CHECK-FIXES: auto const&concrete_ref = c1; + int c2 = 42; + // CHECK-MESSAGES:[[@LINE-1]]:5: warning: variable 'c2' of type 'int' can be declared 'const' + // CHECK-FIXES: int const c2 = 42; + auto concrete_val = c2; + // CHECK-MESSAGES:[[@LINE-1]]:5: warning: variable 'concrete_val' of type 'int' can be declared 'const' + // CHECK-FIXES: auto const concrete_val = c2; + int c3 = 42; + auto *concrete_ptr = &c3; + // CHECK-MESSAGES:[[@LINE-1]]:5: warning: pointee of variable 'concrete_ptr' of type 'int *' can be declared 'const' + // CHECK-FIXES: auto const*concrete_ptr = &c3; + return concrete_ref + concrete_val + *concrete_ptr + + static_cast<int>(sizeof(dependent_ref)); + } +}; +template struct ClassTemplate<int>; + namespace gh57297{ // The expression to check may not be the dependent operand in a dependent // operator. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index f95676f475f9d..fe16cee947b6e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -269,8 +269,6 @@ void more_template_locals() { T *np_local_ptr = &np_local1; const auto np_local3 = T{}; - // FIXME: False positive, the reference points to a template type and needs - // to be excluded from analysis, but somehow isn't (matchers don't work) auto &np_local4 = np_local3; const auto *np_local5 = &np_local3; @@ -278,16 +276,18 @@ void more_template_locals() { using TypedefToTemplate = T; TypedefToTemplate np_local7{}; - // FIXME: False positive, the reference points to a template type and needs - // to be excluded from analysis, but somehow isn't (matchers don't work) - // auto &np_local8 = np_local7; + // 'auto' variables deduced from a dependent type are excluded inside template + // instantiations, so this reference is no longer a false positive. + auto &np_local8 = np_local7; const auto &np_local9 = np_local7; auto np_local10 = np_local7; auto *np_local11 = &np_local10; const auto *const np_local12 = &np_local10; // FIXME: False positive, the reference points to a template type and needs - // to be excluded from analysis, but somehow isn't (matchers don't work) + // to be excluded from analysis. Unlike the 'auto &' cases above, the type is + // spelled through a typedef to a template parameter, so 'auto' deduction does + // not kick in and the dedicated exclusion does not apply. // TypedefToTemplate &np_local13 = np_local7; TypedefToTemplate *np_local14 = &np_local7; } >From 590a22ce656487fbc033e62b47df1adf2014407a Mon Sep 17 00:00:00 2001 From: Victor Baranov <[email protected]> Date: Thu, 25 Jun 2026 08:50:24 +0300 Subject: [PATCH 11/11] + --- .../clang-tidy/misc/ConstCorrectnessCheck.cpp | 36 +++++++++++++++++-- .../misc/const-correctness-templates.cpp | 23 ++++++++++++ .../misc/const-correctness-values.cpp | 10 +++--- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 7201266c46783..24183002d9b00 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -45,6 +45,25 @@ AST_MATCHER(TypeLoc, hasContainedAutoType) { return !Node.getContainedAutoTypeLoc().isNull(); } +// Matches a type whose sugar contains a substituted template parameter, such as +// a typedef to a template parameter (`using U = T; U &r = ...;`). After +// instantiation the canonical type is concrete, so the type-based matchers can +// no longer tell that it derives from a template parameter; unlike 'auto', the +// substitution is still visible in the type sugar and can be found here. +AST_MATCHER(Type, hasSubstTemplateTypeParmInSugar) { + const ASTContext &Context = Finder->getASTContext(); + QualType QT(&Node, 0); + while (!QT.isNull()) { + if (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) + return true; + const QualType Desugared = QT.getSingleStepDesugaredType(Context); + if (Desugared == QT) + return false; + QT = Desugared; + } + return false; +} + AST_MATCHER(FunctionDecl, isTemplate) { return Node.getDescribedFunctionTemplate() != nullptr; } @@ -151,8 +170,21 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { const auto LocalValDecl = varDecl( isLocal(), hasInitializer(unless(isInstantiationDependent())), unless(CommonExcludeTypes), - unless( - allOf(hasType(referenceType(pointee(autoType()))), isInstantiated())), + // 'TemplateType' above excludes variables whose type is a template + // parameter, as a value (`hasType(substTemplateTypeParmType())`) or a + // reference (`referenceType(pointee(substTemplateTypeParmType()))`), + // because their constness can differ between instantiations. That + // exclusion has gaps inside template instantiations that are closed here: + // - a typedef to a template parameter hides the substituted parameter + // behind typedef sugar, so `using U = T; U v;` / `U &r;` slip through; + // - an 'auto &' reference erases the dependent type during deduction. + // Pointers stay analyzed (like the existing `T *p` case): the suggestion + // there concerns the pointer/pointee spelling, not member constness. + unless(allOf(hasType(qualType(anyOf( + hasSubstTemplateTypeParmInSugar(), + referenceType(pointee(anyOf( + autoType(), hasSubstTemplateTypeParmInSugar())))))), + isInstantiated())), AnalyzeLambdas ? Matcher<VarDecl>(anything()) : Matcher<VarDecl>(unless(hasType(cxxRecordDecl(isLambda())))), diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index fc9e67a944607..33c6a68b97ba9 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -33,6 +33,29 @@ void type_dependent_variables() { auto *ptr_concrete = &concrete; // CHECK-MESSAGES:[[@LINE-1]]:3: warning: pointee of variable 'ptr_concrete' of type 'int *' can be declared 'const' // CHECK-FIXES: auto const*ptr_concrete = &concrete; + + // A typedef to a template parameter keeps the substituted parameter visible + // in the type sugar, so it is excluded both as a reference and as a value: + // the constness of such a variable can differ between instantiations. + using TypedefToTemplate = T; + TypedefToTemplate &td_ref = value; + TypedefToTemplate td_val = value; + (void)td_val; + + // Pointers are still analyzed even when the pointee derives from a template + // parameter: the suggestion concerns the pointer/pointee spelling, like the + // bare 'T *' case, not member constness. + TypedefToTemplate *td_ptr = &value; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: pointee of variable 'td_ptr' of type 'TypedefToTemplate *' (aka 'int *') can be declared 'const' + // CHECK-FIXES: TypedefToTemplate const*td_ptr = &value; + (void)*td_ptr; + + // A typedef to a concrete type is not template-derived, so such references + // are still analyzed. + using ConcreteAlias = int; + ConcreteAlias &concrete_alias_ref = concrete; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'concrete_alias_ref' of type 'ConcreteAlias &' (aka 'int &') can be declared 'const' + // CHECK-FIXES: ConcreteAlias const&concrete_alias_ref = concrete; } void instantiate_template_cases() { type_dependent_variables<int>(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index fe16cee947b6e..d3c000d89360e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -284,11 +284,11 @@ void more_template_locals() { auto *np_local11 = &np_local10; const auto *const np_local12 = &np_local10; - // FIXME: False positive, the reference points to a template type and needs - // to be excluded from analysis. Unlike the 'auto &' cases above, the type is - // spelled through a typedef to a template parameter, so 'auto' deduction does - // not kick in and the dedicated exclusion does not apply. - // TypedefToTemplate &np_local13 = np_local7; + // A reference spelled through a typedef to a template parameter is also + // excluded inside the instantiation: even though 'auto' deduction does not + // apply here, the substituted template parameter is still visible in the + // type sugar and is detected. + TypedefToTemplate &np_local13 = np_local7; TypedefToTemplate *np_local14 = &np_local7; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
