Author: Victor Chernyakin Date: 2026-07-11T09:54:07+03:00 New Revision: 0730f680b6ac237ee1c81b2c55a741ba7065ee97
URL: https://github.com/llvm/llvm-project/commit/0730f680b6ac237ee1c81b2c55a741ba7065ee97 DIFF: https://github.com/llvm/llvm-project/commit/0730f680b6ac237ee1c81b2c55a741ba7065ee97.diff LOG: [clang-tidy] make `misc-const-correctness` work with `auto` variables and lambdas (#157319) Fixes #60789. Currently, the check will never make `auto` variables `const`. Here's the relevant bit of code: https://github.com/llvm/llvm-project/blob/6b200e21adec0e28407def6fcb2e6c7359fd881b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp#L108-L110 Notice how the matcher's name is `AutoTemplateType`, but it has nothing to do with templates. What it *was* intended to do, I'm not sure, but excluding all `auto` variables can't be right. For lambdas, this is the only justification I can find: https://github.com/llvm/llvm-project/blob/36627e1724504d783dc1cbc466666516d28260e4/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp#L30-L34 Which doesn't convince me. Looking at the test changes, I believe there's only one new false positive, and that one seems to be a symptom of an existing problem that was only being masked by `auto`, but we can absolutely discuss whether the now-diagnosed cases are correct. --------- Co-authored-by: Victor Baranov <[email protected]> Added: clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-lambdas.cpp Modified: clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 6964e1b3652ea..88ee9fd58fd72 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 { @@ -40,6 +41,10 @@ AST_MATCHER(ReferenceType, isSpelledAsLValue) { } AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); } +AST_MATCHER(TypeLoc, hasContainedAutoType) { + return !Node.getContainedAutoTypeLoc().isNull(); +} + AST_MATCHER(FunctionDecl, isTemplate) { return Node.getDescribedFunctionTemplate() != nullptr; } @@ -55,6 +60,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)), AnalyzeParameters(Options.get("AnalyzeParameters", true)), WarnPointersAsPointers(Options.get("WarnPointersAsPointers", true)), @@ -75,12 +82,19 @@ 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) { 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, "AnalyzeParameters", AnalyzeParameters); Options.store(Opts, "WarnPointersAsPointers", WarnPointersAsPointers); @@ -114,7 +128,7 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))), hasType(referenceType(pointee(substTemplateTypeParmType())))); - auto AllowedTypeDecl = namedDecl(anyOf( + const auto AllowedTypeDecl = namedDecl(anyOf( matchers::matchesAnyListedRegexName(AllowedTypes), usingShadowDecl())); const auto AllowedType = hasType(qualType( @@ -135,9 +149,17 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { // 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(unless(isInstantiationDependent())), - unless(CommonExcludeTypes)); + const auto LocalValDecl = varDecl( + isLocal(), hasInitializer(anything()), + unless(anyOf(ConstType, ConstReference, TemplateType, + hasInitializer(isInstantiationDependent()), RValueReference, + FunctionPointerRef, isImplicit(), AllowedType)), + 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/clang-tidy/misc/ConstCorrectnessCheck.h b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h index 9f4b2f37e0787..2e0e2fc67564b 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h @@ -44,6 +44,8 @@ class ConstCorrectnessCheck : public ClangTidyCheck { const bool AnalyzePointers; const bool AnalyzeReferences; const bool AnalyzeValues; + const bool AnalyzeAutoVariables; + const bool AnalyzeLambdas; const bool AnalyzeParameters; const bool WarnPointersAsPointers; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 5abcc053652ee..3891dbd21024a 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -577,6 +577,10 @@ Changes in existing checks - Fixed false positive where a pointer used with placement new was incorrectly diagnosed as allowing the pointee to be made ``const``. + - Enabled the check for variables declared with ``auto`` (configurable + via the `AnalyzeAutoVariables` option) and lambdas (configurable + via `AnalyzeLambdas` option). + - Fixed false positive where calling a non-const member function on a pointer was incorrectly treated as mutating the pointer, when it only mutates the pointee. 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 7a0c73e099494..ce699e06b9276 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 @@ -106,6 +106,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:: AnalyzeParameters Enable or disable the analysis of function parameters, like 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..5cd938d44a9d8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto-parameters.cpp @@ -0,0 +1,32 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s misc-const-correctness %t -- \ +// RUN: -config="{CheckOptions: {\ +// RUN: misc-const-correctness.AnalyzeParameters: 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 l = [](auto& b) { b.const_method(); }; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'l' of type '(lambda at {{.*}})' can be declared 'const' + // CHECK-FIXES: auto const l = [](auto& b) { b.const_method(); }; + Bar bar; + l(bar); +} 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..8e0c503c78c09 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-auto.cpp @@ -0,0 +1,22 @@ +// 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 + +// 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' + // CHECK-FIXES: int const i = 0; + + auto auto_i = 0; + auto& auto_ref = auto_i; + auto auto_lambda = [] {}; + auto *auto_ptr = static_cast<void *>(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 = [] {}; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp index a8d90f1b582bf..cbe63d878ccb6 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp @@ -215,6 +215,8 @@ void use_requires_const_method() { void lambda_params() { auto lambda = [](Bar& b) { + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'lambda' of type '(lambda at {{.*}})' can be declared 'const' + // CHECK-FIXES: auto const lambda = [](Bar& b) { b.const_method(); }; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp index f6bfb98c548c3..811d5e08cb219 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp @@ -206,3 +206,16 @@ void ignore_unique_ptr_emplace_sink() { std::vector<std::unique_ptr<UniquePtrData>> data; data.emplace_back(newdata); } + +void auto_pointee_to_const() { + int a[] = {1, 2}; + int *p_normal = &a[0]; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_normal' of type 'int *' can be declared 'const' + // CHECK-FIXES: int const*p_normal = &a[0]; + p_normal = &a[1]; + + // A bare 'auto' deduces to 'int *'; the pointee 'const' cannot be spelled through 'auto', + // so unlike the pointer above it is not diagnosed. + auto p_auto = &a[0]; + p_auto = &a[1]; +} 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 0875e79e22f17..b498c0b6d8d5a 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] = {nullptr, nullptr}; - 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) { } } @@ -61,6 +63,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..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,9 +9,15 @@ template <typename T> void type_dependent_variables() { T value = 42; - auto &ref = value; 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. + 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; 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 ced07501c01d8..314eeb350bf9f 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 @@ -28,10 +28,24 @@ 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 = [](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 = [&x] { ++x; }; + LambdaModifyingCapture(); } void some_function(double, wchar_t); @@ -86,6 +100,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; @@ -966,25 +985,36 @@ 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 = 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 = auto_val0; 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 = 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 = 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 = auto_td0; auto *auto_td2 = &auto_td0; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
