[clang-tools-extra] [clang-tidy] Add AnalyzeParameters option to misc-const-correctness (PR #171215)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 16 00:45:55 PST 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/171215
>From 897a2e75233b8c0bcfc8aee4cf1af19bf10128f7 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Tue, 9 Dec 2025 01:15:44 +0300
Subject: [PATCH 1/7] [clang-tidy] Add AnalyzeParameters option to
misc-const-correctness
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 145 ++++-
.../clang-tidy/misc/ConstCorrectnessCheck.h | 4 +
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../checks/misc/const-correctness.rst | 26 +-
.../const-correctness/correctness-fixed.h | 11 +
.../Inputs/const-correctness/correctness.h | 11 +
.../const-correctness-parameters-header.cpp | 27 +
.../misc/const-correctness-parameters.cpp | 495 ++++++++++++++++++
.../misc/const-correctness-values.cpp | 1 +
9 files changed, 691 insertions(+), 35 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness-fixed.h
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness.h
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters-header.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 762144ea6deea..55e8f9c226234 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -33,6 +33,14 @@ AST_MATCHER(ReferenceType, isSpelledAsLValue) {
return Node.isSpelledAsLValue();
}
AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
+
+AST_MATCHER(FunctionDecl, isTemplate) {
+ return Node.getDescribedFunctionTemplate() != nullptr;
+}
+
+AST_MATCHER(FunctionDecl, isFunctionTemplateSpecialization) {
+ return Node.isFunctionTemplateSpecialization();
+}
} // namespace
ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
@@ -41,6 +49,7 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
AnalyzePointers(Options.get("AnalyzePointers", true)),
AnalyzeReferences(Options.get("AnalyzeReferences", true)),
AnalyzeValues(Options.get("AnalyzeValues", true)),
+ AnalyzeParameters(Options.get("AnalyzeParameters", true)),
WarnPointersAsPointers(Options.get("WarnPointersAsPointers", true)),
WarnPointersAsValues(Options.get("WarnPointersAsValues", false)),
@@ -66,6 +75,7 @@ void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AnalyzePointers", AnalyzePointers);
Options.store(Opts, "AnalyzeReferences", AnalyzeReferences);
Options.store(Opts, "AnalyzeValues", AnalyzeValues);
+ Options.store(Opts, "AnalyzeParameters", AnalyzeParameters);
Options.store(Opts, "WarnPointersAsPointers", WarnPointersAsPointers);
Options.store(Opts, "WarnPointersAsValues", WarnPointersAsValues);
@@ -112,15 +122,17 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
const auto FunctionPointerRef =
hasType(hasCanonicalType(referenceType(pointee(functionType()))));
+ const auto CommonExcludeTypes =
+ anyOf(ConstType, ConstReference, RValueReference, TemplateType,
+ FunctionPointerRef, hasType(cxxRecordDecl(isLambda())),
+ AutoTemplateType, 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()), AutoTemplateType,
- RValueReference, FunctionPointerRef,
- hasType(cxxRecordDecl(isLambda())), isImplicit(),
- AllowedType)));
+ const auto LocalValDecl =
+ varDecl(isLocal(), hasInitializer(anything()),
+ unless(anyOf(CommonExcludeTypes,
+ hasInitializer(isInstantiationDependent()))));
// Match the function scope for which the analysis of all local variables
// shall be run.
@@ -135,6 +147,49 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
.bind("function-decl");
Finder->addMatcher(FunctionScope, this);
+
+ if (AnalyzeParameters) {
+ const auto ParamMatcher =
+ parmVarDecl(unless(CommonExcludeTypes),
+ anyOf(hasType(referenceType()), hasType(pointerType())))
+ .bind("param-var");
+
+ // Match function parameters which could be 'const' if not modified later.
+ // Example: `void foo(int* ptr)` would match `int* ptr`.
+ const auto FunctionWithParams =
+ functionDecl(
+ hasBody(stmt().bind("scope-params")),
+ has(typeLoc(forEach(ParamMatcher))), unless(cxxMethodDecl()),
+ unless(isFunctionTemplateSpecialization()), unless(isTemplate()))
+ .bind("function-params");
+
+ Finder->addMatcher(FunctionWithParams, this);
+ }
+}
+
+static void addConstFixits(DiagnosticBuilder &Diag, const VarDecl *Variable,
+ const FunctionDecl *Function, ASTContext &Context,
+ Qualifiers::TQ Qualifier,
+ utils::fixit::QualifierTarget Target,
+ utils::fixit::QualifierPolicy Policy) {
+ Diag << addQualifierToVarDecl(*Variable, Context, Qualifier, Target, Policy);
+
+ // If this is a parameter, also add fixits for corresponding parameters in
+ // function declarations
+ if (const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
+ const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
+
+ for (const FunctionDecl *Redecl : Function->redecls()) {
+ if (Redecl == Function)
+ continue;
+
+ if (ParamIdx < Redecl->getNumParams()) {
+ const ParmVarDecl *RedeclParam = Redecl->getParamDecl(ParamIdx);
+ Diag << addQualifierToVarDecl(*RedeclParam, Context, Qualifier, Target,
+ Policy);
+ }
+ }
+ }
}
namespace {
@@ -145,14 +200,34 @@ enum class VariableCategory { Value, Reference, Pointer };
} // namespace
void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
- const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("local-value");
- const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
- const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
+ const Stmt *LocalScope = nullptr;
+ const VarDecl *Variable = nullptr;
+ const FunctionDecl *Function = nullptr;
+ const DeclStmt *VarDeclStmt = nullptr;
+
+ if (const auto *LocalVariable =
+ Result.Nodes.getNodeAs<VarDecl>("local-value")) {
+ Variable = LocalVariable;
+ LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
+ Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
+ VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
+ } else if (const auto *Parameter =
+ Result.Nodes.getNodeAs<ParmVarDecl>("param-var")) {
+ Variable = Parameter;
+ LocalScope = Result.Nodes.getNodeAs<Stmt>("scope-params");
+ Function = Result.Nodes.getNodeAs<FunctionDecl>("function-params");
+ } else {
+ llvm_unreachable("didn't match local-value or param-var");
+ }
+
+ assert(Variable && LocalScope && Function);
+
// It can not be guaranteed that the variable is declared isolated,
// therefore a transformation might effect the other variables as well and
- // be incorrect.
- const bool CanBeFixIt = VarDeclStmt != nullptr && VarDeclStmt->isSingleDecl();
+ // be incorrect. Parameters don't need this check - they receive values from
+ // callers
+ const bool CanBeFixIt = isa<ParmVarDecl>(Variable) ||
+ (VarDeclStmt && VarDeclStmt->isSingleDecl());
/// If the variable was declared in a template it might be analyzed multiple
/// times. Only one of those instantiations shall emit a warning. NOTE: This
@@ -176,11 +251,8 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
}
auto CheckValue = [&]() {
- // The scope is only registered if the analysis shall be run.
- registerScope(LocalScope, Result.Context);
-
// Offload const-analysis to utility function.
- if (ScopesCache[LocalScope]->isMutated(Variable))
+ if (isMutated(Variable, LocalScope, Function, Result.Context))
return;
auto Diag = diag(Variable->getBeginLoc(),
@@ -191,26 +263,27 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
if (!CanBeFixIt)
return;
using namespace utils::fixit;
+
if (VC == VariableCategory::Value && TransformValues) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- Qualifiers::Const, QualifierTarget::Value,
- QualifierPolicy::Right);
+ addConstFixits(Diag, Variable, Function, *Result.Context,
+ Qualifiers::Const, QualifierTarget::Value,
+ QualifierPolicy::Right);
// FIXME: Add '{}' for default initialization if no user-defined default
// constructor exists and there is no initializer.
return;
}
if (VC == VariableCategory::Reference && TransformReferences) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- Qualifiers::Const, QualifierTarget::Value,
- QualifierPolicy::Right);
+ addConstFixits(Diag, Variable, Function, *Result.Context,
+ Qualifiers::Const, QualifierTarget::Value,
+ QualifierPolicy::Right);
return;
}
if (VC == VariableCategory::Pointer && TransformPointersAsValues) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- Qualifiers::Const, QualifierTarget::Value,
- QualifierPolicy::Right);
+ addConstFixits(Diag, Variable, Function, *Result.Context,
+ Qualifiers::Const, QualifierTarget::Value,
+ QualifierPolicy::Right);
return;
}
};
@@ -230,9 +303,9 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
return;
using namespace utils::fixit;
if (TransformPointersAsPointers) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- Qualifiers::Const, QualifierTarget::Pointee,
- QualifierPolicy::Right);
+ addConstFixits(Diag, Variable, Function, *Result.Context,
+ Qualifiers::Const, QualifierTarget::Pointee,
+ QualifierPolicy::Right);
}
};
@@ -275,4 +348,18 @@ void ConstCorrectnessCheck::registerScope(const Stmt *LocalScope,
Analyzer = std::make_unique<ExprMutationAnalyzer>(*LocalScope, *Context);
}
+bool ConstCorrectnessCheck::isMutated(const VarDecl *Variable,
+ const Stmt *Scope,
+ const FunctionDecl *Func,
+ ASTContext *Context) {
+ if (const auto *Param = dyn_cast<ParmVarDecl>(Variable)) {
+ return FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(
+ *Func, *Context, ParamMutationAnalyzerMemoized)
+ ->isMutated(Param);
+ }
+
+ registerScope(Scope, Context);
+ return ScopesCache[Scope]->isMutated(Variable);
+}
+
} // namespace clang::tidy::misc
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
index fafcac407e029..9f4b2f37e0787 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
@@ -34,13 +34,17 @@ class ConstCorrectnessCheck : public ClangTidyCheck {
private:
void registerScope(const Stmt *LocalScope, ASTContext *Context);
+ bool isMutated(const VarDecl *Variable, const Stmt *Scope,
+ const FunctionDecl *Func, ASTContext *Context);
using MutationAnalyzer = std::unique_ptr<ExprMutationAnalyzer>;
llvm::DenseMap<const Stmt *, MutationAnalyzer> ScopesCache;
llvm::DenseSet<SourceLocation> TemplateDiagnosticsCache;
+ ExprMutationAnalyzer::Memoized ParamMutationAnalyzerMemoized;
const bool AnalyzePointers;
const bool AnalyzeReferences;
const bool AnalyzeValues;
+ const bool AnalyzeParameters;
const bool WarnPointersAsPointers;
const bool WarnPointersAsValues;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 54f5eed5306e4..dbf8d2ab0d1c4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -107,6 +107,12 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Improved :doc:`misc-const-correctness
+ <clang-tidy/checks/misc/const-correctness>` check:
+
+ - Added support for analyzing function parameters with the `AnalyzeParameters`
+ option.
+
Removed checks
^^^^^^^^^^^^^^
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..dcae96cf860d6 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
@@ -3,9 +3,9 @@
misc-const-correctness
======================
-This check implements detection of local variables which could be declared as
-``const`` but are not. Declaring variables as ``const`` is required or
-recommended by many coding guidelines, such as:
+This check implements detection of local variables and function parameters
+which could be declared as ``const`` but are not. Declaring variables as
+``const`` is required or recommended by many coding guidelines, such as:
`ES.25 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on>`_
from the C++ Core Guidelines.
@@ -58,9 +58,9 @@ Limitations
The check does not run on `C` code.
-The check will not analyze templated variables or variables that are
-instantiation dependent. Different instantiations can result in
-different ``const`` correctness properties and in general it is not
+The check will not analyze templated variables and template functions or
+variables that are instantiation dependent. Different instantiations can result
+in different ``const`` correctness properties and in general it is not
possible to find all instantiations of a template. The template might
be used differently in an independent translation unit.
@@ -104,6 +104,20 @@ Options
:option:`WarnPointersAsValues` and :option:`WarnPointersAsPointers`.
Default is `true`.
+.. option:: AnalyzeParameters
+
+ Enable or disable the analysis of function parameters, like
+ ``void foo(int* ptr)``. Only reference and pointer parameters are analyzed.
+ As of current implementation, member function (including constructors) and
+ lambdas are excluded from the analysis. Default is `true`.
+
+ .. code-block:: c++
+
+ // Warning
+ void function(int& param) {}
+ // No warning
+ void function(const int& param) {}
+
.. option:: WarnPointersAsValues
This option enables the suggestion for ``const`` of the pointer itself.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness-fixed.h b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness-fixed.h
new file mode 100644
index 0000000000000..24f191c448e06
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness-fixed.h
@@ -0,0 +1,11 @@
+struct S {
+ void method() const;
+ int value;
+};
+
+void func_with_ref_param(S const& s);
+
+void func_mixed_params(int x, S const& readonly, S& mutated);
+
+void multi_decl_func(S const& s);
+void multi_decl_func(S const& s);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness.h b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness.h
new file mode 100644
index 0000000000000..7d1a63c563779
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/const-correctness/correctness.h
@@ -0,0 +1,11 @@
+struct S {
+ void method() const;
+ int value;
+};
+
+void func_with_ref_param(S& s);
+
+void func_mixed_params(int x, S& readonly, S& mutated);
+
+void multi_decl_func(S& s);
+void multi_decl_func(S& s);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters-header.cpp
new file mode 100644
index 0000000000000..bd0d3dbf7068f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters-header.cpp
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: cp %S/Inputs/const-correctness/correctness.h %t/correctness.h
+// RUN: %check_clang_tidy %s misc-const-correctness %t/temp -- \
+// RUN: -- -I%t -fno-delayed-template-parsing
+// RUN: diff %t/correctness.h %S/Inputs/const-correctness/correctness-fixed.h
+
+#include "correctness.h"
+
+// CHECK-MESSAGES: :[[@LINE+1]]:26: warning: variable 's' of type 'S &' can be declared 'const'
+void func_with_ref_param(S& s) {
+ // CHECK-FIXES: void func_with_ref_param(S const& s) {
+ s.method();
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:31: warning: variable 'readonly' of type 'S &' can be declared 'const'
+void func_mixed_params(int x, S& readonly, S& mutated) {
+ // CHECK-FIXES: void func_mixed_params(int x, S const& readonly, S& mutated) {
+ readonly.method();
+ mutated.value = x;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: variable 's' of type 'S &' can be declared 'const'
+void multi_decl_func(S& s) {
+ // CHECK-FIXES: void multi_decl_func(S const& s) {
+ s.method();
+}
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
new file mode 100644
index 0000000000000..dbc2f3e25ca95
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp
@@ -0,0 +1,495 @@
+// RUN: %check_clang_tidy %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: }}" -- -I %S/Inputs/const-correctness -fno-delayed-template-parsing
+
+struct Bar {
+ void buz() const;
+ void mutate();
+ int value;
+};
+
+struct Foo {
+ void method() const;
+ void mutating_method();
+};
+
+void ref_param_const(Bar& b);
+// CHECK-FIXES: void ref_param_const(Bar const& b);
+
+void ref_param_const(Bar& b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:22: warning: variable 'b' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void ref_param_const(Bar const& b) {
+ b.buz();
+}
+
+void ref_param_already_const(const Foo& f) {
+ f.method();
+}
+
+void ref_param_mutated(Foo& f) {
+ f.mutating_method();
+}
+
+void ref_param_member_modified(Bar& b) {
+ b.value = 42;
+}
+
+void pointer_param_read_only(Bar* b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:30: warning: pointee of variable 'b' of type 'Bar *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:30: warning: variable 'b' of type 'Bar *' can be declared 'const'
+ // CHECK-FIXES: void pointer_param_read_only(Bar const* const b) {
+ b->buz();
+}
+
+void pointer_param_mutated_pointee(Bar* b) {
+ b->mutate();
+}
+
+void pointer_param_mutated_pointer(Bar* b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:36: warning: pointee of variable 'b' of type 'Bar *' can be declared 'const'
+ // CHECK-FIXES: void pointer_param_mutated_pointer(Bar const* b) {
+ b = nullptr;
+}
+
+void value_param_int(int x) {
+ int y = x + 1;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const y = x + 1;
+}
+
+void value_param_struct(Bar b) {
+ b.buz();
+}
+
+void multiple_params_mixed(int x, Bar& b, Foo& f) {
+ // CHECK-MESSAGES: [[@LINE-1]]:35: warning: variable 'b' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void multiple_params_mixed(int x, Bar const& b, Foo& f) {
+ int y = x;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const y = x;
+ b.buz();
+ f.mutating_method();
+}
+
+void rvalue_ref_param(Bar&& b) {
+ b.buz();
+}
+
+void pass_to_const_ref(const Bar& b);
+void pass_to_nonconst_ref(Bar& b);
+
+void param_passed_to_const_ref(Bar& b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:32: warning: variable 'b' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void param_passed_to_const_ref(Bar const& b) {
+ pass_to_const_ref(b);
+}
+
+void param_passed_to_nonconst_ref(Bar& b) {
+ pass_to_nonconst_ref(b);
+}
+
+template<typename T>
+void template_param(T& t) {
+ t.buz();
+}
+
+void instantiate_template() {
+ Bar b;
+ template_param(b);
+}
+
+template<typename T>
+void forwarding_ref(T&& t) {
+ t.method();
+}
+
+template<typename T>
+void specialized_func(T& t) {
+ t.buz();
+}
+
+template<>
+void specialized_func<Bar>(Bar& b) {
+ b.buz();
+}
+
+template<int N>
+void non_type_template_param(Bar& b) {
+ b.buz();
+}
+
+template<typename... Args>
+void variadic_template(Args&... args) {
+ (args.buz(), ...);
+}
+
+template<typename First, typename... Rest>
+void variadic_first_param(First& first, Rest&... rest) {
+ first.buz();
+}
+
+template<typename T>
+struct is_bar {
+ static constexpr bool value = false;
+};
+
+template<>
+struct is_bar<Bar> {
+ static constexpr bool value = true;
+};
+
+template<typename T, typename = void>
+struct enable_if {};
+
+template<typename T>
+struct enable_if<T, typename T::type> {
+ using type = typename T::type;
+};
+
+struct true_type { using type = void; };
+struct false_type {};
+
+template<bool B>
+struct bool_constant : false_type {};
+
+template<>
+struct bool_constant<true> : true_type {};
+
+template<typename T>
+void sfinae_func(T& t, typename enable_if<bool_constant<is_bar<T>::value>>::type* = nullptr) {
+ t.buz();
+}
+
+template<typename T>
+struct TemplateClass {
+ void non_template_method(Bar& b) {
+ b.buz();
+ }
+
+ template<typename U>
+ void template_method(U& u) {
+ u.buz();
+ }
+
+ static void static_method(Bar& b) {
+ b.buz();
+ }
+};
+
+template struct TemplateClass<int>;
+
+struct NonTemplateClass {
+ template<typename T>
+ void template_method(T& t) {
+ t.buz();
+ }
+
+ template<typename T>
+ static void static_template_method(T& t) {
+ t.buz();
+ }
+};
+
+template<typename T = Bar>
+void default_template_arg(T& t) {
+ t.buz();
+}
+
+template<typename Outer>
+struct OuterTemplate {
+ template<typename Inner>
+ static void nested_template_func(Outer& o, Inner& i) {
+ o.buz();
+ i.buz();
+ }
+};
+
+template<typename T>
+using RefAlias = T&;
+
+template<typename T>
+void alias_template_param(RefAlias<T> t) {
+ t.buz();
+}
+
+template<typename T>
+void requires_buz(T& t) {
+}
+
+void use_requires_buz() {
+ Bar b;
+ requires_buz(b);
+}
+
+void lambda_params() {
+ auto lambda = [](Bar& b) {
+ b.buz();
+ };
+}
+
+struct Container {
+ Container(int* x) {}
+ void member_func(Bar& b) {
+ b.buz();
+ }
+};
+
+void builtin_int_ref(int& x) {
+ // CHECK-MESSAGES: [[@LINE-1]]:22: warning: variable 'x' of type 'int &' can be declared 'const'
+ // CHECK-FIXES: void builtin_int_ref(int const& x) {
+ int y = x;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const y = x;
+}
+
+void builtin_int_ref_mutated(int& x) {
+ x = 42;
+}
+
+void decl_multiple(Bar& b);
+// CHECK-FIXES: void decl_multiple(Bar const& b);
+void decl_multiple(Bar& b);
+// CHECK-FIXES: void decl_multiple(Bar const& b);
+
+void decl_multiple(Bar& b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: variable 'b' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void decl_multiple(Bar const& b) {
+ b.buz();
+}
+
+void decl_multi_params(int& x, Bar& b, Foo& f);
+// CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Foo& f);
+
+void decl_multi_params(int& x, Bar& b, Foo& f) {
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'x' of type 'int &' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:32: warning: variable 'b' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Foo& f) {
+ int y = x;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const y = x;
+ b.buz();
+ f.mutating_method(); // f is mutated
+}
+
+namespace ns {
+ void namespaced_func(Foo& f);
+ // CHECK-FIXES: void namespaced_func(Foo const& f);
+
+ void namespaced_func(Foo& f) {
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'f' of type 'Foo &' can be declared 'const'
+ // CHECK-FIXES: void namespaced_func(Foo const& f) {
+ f.method();
+ }
+}
+
+void func_ptr_param(void (*fp)(int&)) {
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: variable 'fp' of type 'void (*)(int &)' can be declared 'const'
+ // CHECK-FIXES: void func_ptr_param(void (*const fp)(int&)) {
+ int x = 5;
+ fp(x);
+}
+
+void func_ptr_param_reassigned(void (*fp)(int&)) {
+ int x = 5;
+ fp(x);
+ fp = nullptr;
+}
+
+struct Block {
+ void render(int& x) const { x = 0; }
+};
+
+void member_ptr_param(void (Block::*mp)(int&) const) {
+}
+
+namespace std {
+ template<typename T> class function;
+ template<typename R, typename... Args>
+ class function<R(Args...)> {
+ public:
+ template<typename F> function(F&&) {}
+ R operator()(Args...) const { return R(); }
+ };
+}
+
+struct Decl {};
+
+void std_function_param(std::function<void(Decl*)> callback) {
+ Decl d;
+ callback(&d);
+ std::function<void(Decl*)> const cb = nullptr;
+}
+
+void std_function_ref_param(std::function<void(int&)> callback) {
+ int x = 5;
+ callback(x);
+}
+
+void double_ptr_inner_not_modified(int** pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:36: warning: pointee of variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:36: warning: variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-FIXES: void double_ptr_inner_not_modified(int* const* const pp) {
+ int* p = *pp;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: int const* const p = *pp;
+}
+
+void double_ptr_inner_modified(int** pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:32: warning: variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-FIXES: void double_ptr_inner_modified(int** const pp) {
+ *pp = nullptr;
+}
+
+void double_ptr_outer_not_modified(int** pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:36: warning: pointee of variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:36: warning: variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-FIXES: void double_ptr_outer_not_modified(int* const* const pp) {
+ int* local = *pp;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'local' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'local' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: int const* const local = *pp;
+}
+
+void triple_ptr_read_only(int*** ppp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: pointee of variable 'ppp' of type 'int ***' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:27: warning: variable 'ppp' of type 'int ***' can be declared 'const'
+ // CHECK-FIXES: void triple_ptr_read_only(int** const* const ppp) {
+ int** pp = *ppp;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-FIXES: int* const* const pp = *ppp;
+}
+
+void triple_ptr_middle_modified(int*** ppp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:33: warning: pointee of variable 'ppp' of type 'int ***' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:33: warning: variable 'ppp' of type 'int ***' can be declared 'const'
+ // CHECK-FIXES: void triple_ptr_middle_modified(int** const* const ppp) {
+ **ppp = nullptr;
+}
+
+void ref_to_ptr_both_readonly(int*& p) {
+ // CHECK-MESSAGES: [[@LINE-1]]:31: warning: variable 'p' of type 'int *&' can be declared 'const'
+ // CHECK-FIXES: void ref_to_ptr_both_readonly(int* const& p) {
+ int val = *p;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'val' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const val = *p;
+}
+
+void ref_to_ptr_pointer_modified(int*& p) {
+ p = nullptr;
+}
+
+void ref_to_ptr_pointee_modified(int*& p) {
+ // CHECK-MESSAGES: [[@LINE-1]]:34: warning: variable 'p' of type 'int *&' can be declared 'const'
+ // CHECK-FIXES: void ref_to_ptr_pointee_modified(int* const& p) {
+ *p = 42;
+}
+
+void ref_to_const_ptr(int* const& p) {
+ int val = *p;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'val' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const val = *p;
+}
+
+void const_ref_to_ptr(int* const& p) {
+ *p = 42; // Pointee modification is allowed
+}
+
+void ref_to_ptr_to_const(const int*& p) {
+ // CHECK-MESSAGES: [[@LINE-1]]:26: warning: variable 'p' of type 'const int *&' can be declared 'const'
+ // CHECK-FIXES: void ref_to_ptr_to_const(const int* const& p) {
+ int val = *p;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'val' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const val = *p;
+}
+
+void ref_to_ptr_to_const_modified(const int*& p) {
+ p = nullptr;
+}
+
+void double_ptr_const_inner(const int** pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: pointee of variable 'pp' of type 'const int **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:29: warning: variable 'pp' of type 'const int **' can be declared 'const'
+ // CHECK-FIXES: void double_ptr_const_inner(const int* const* const pp) {
+ const int* p = *pp;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p' of type 'const int *' can be declared 'const'
+ // CHECK-FIXES: const int* const p = *pp;
+}
+
+void double_ptr_modify_value(int** pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:30: warning: pointee of variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:30: warning: variable 'pp' of type 'int **' can be declared 'const'
+ // CHECK-FIXES: void double_ptr_modify_value(int* const* const pp) {
+ **pp = 42;
+}
+
+void ref_to_double_ptr(int**& pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'pp' of type 'int **&' can be declared 'const'
+ // CHECK-FIXES: void ref_to_double_ptr(int** const& pp) {
+ int* p = *pp;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: int const* const p = *pp;
+}
+
+void ref_to_double_ptr_outer_modified(int**& pp) {
+ pp = nullptr;
+}
+
+void ref_to_double_ptr_inner_modified(int**& pp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:39: warning: variable 'pp' of type 'int **&' can be declared 'const'
+ // CHECK-FIXES: void ref_to_double_ptr_inner_modified(int** const& pp) {
+ *pp = nullptr;
+}
+
+void array_of_ptrs(int* arr[]) {
+ int* p = arr[0];
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: int const* const p = arr[0];
+}
+
+void ptr_to_array(int (*arr)[10]) {
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: pointee of variable 'arr' of type 'int (*)[10]' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:19: warning: variable 'arr' of type 'int (*)[10]' can be declared 'const'
+ // CHECK-FIXES: void ptr_to_array(int const(*const arr)[10]) {
+ int val = (*arr)[0];
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'val' of type 'int' can be declared 'const'
+ // CHECK-FIXES: int const val = (*arr)[0];
+}
+
+void ptr_to_array_modified(int (*arr)[10]) {
+ // CHECK-MESSAGES: [[@LINE-1]]:28: warning: variable 'arr' of type 'int (*)[10]' can be declared 'const'
+ // CHECK-FIXES: void ptr_to_array_modified(int (*const arr)[10]) {
+ (*arr)[0] = 42;
+}
+
+void ref_to_ptr_array(int* (&arr)[5]) {
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: variable 'arr' of type 'int *(&)[5]' can be declared 'const'
+ // CHECK-FIXES: void ref_to_ptr_array(int* const(&arr)[5]) {
+ int* p = arr[0];
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:3: warning: variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: int const* const p = arr[0];
+}
+
+void ref_to_ptr_array_modified(int* (&arr)[5]) {
+ arr[0] = nullptr;
+}
+
+void struct_ptr_param(Bar** bp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: pointee of variable 'bp' of type 'Bar **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:23: warning: variable 'bp' of type 'Bar **' can be declared 'const'
+ // CHECK-FIXES: void struct_ptr_param(Bar* const* const bp) {
+ (*bp)->buz();
+}
+
+void struct_ptr_param_modified(Bar** bp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:32: warning: variable 'bp' of type 'Bar **' can be declared 'const'
+ // CHECK-FIXES: void struct_ptr_param_modified(Bar** const bp) {
+ (*bp)->mutate();
+}
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 4cef7f4ce116e..ced07501c01d8 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
@@ -1,5 +1,6 @@
// RUN: %check_clang_tidy %s misc-const-correctness %t -- \
// RUN: -config="{CheckOptions: {\
+// RUN: misc-const-correctness.AnalyzeParameters: false, \
// RUN: misc-const-correctness.TransformValues: true, \
// RUN: misc-const-correctness.WarnPointersAsValues: false, \
// RUN: misc-const-correctness.WarnPointersAsPointers: false, \
>From 4fd9bc368f3dc85fa5f1982d6de2a8e0aa937677 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Wed, 10 Dec 2025 20:26:38 +0300
Subject: [PATCH 2/7] fix review
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 38 ++++++-------------
1 file changed, 11 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 55e8f9c226234..55ac1b87e420e 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -130,9 +130,8 @@ 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(anything()),
- unless(anyOf(CommonExcludeTypes,
- hasInitializer(isInstantiationDependent()))));
+ varDecl(isLocal(), hasInitializer(unless(isInstantiationDependent())),
+ unless(CommonExcludeTypes));
// Match the function scope for which the analysis of all local variables
// shall be run.
@@ -140,7 +139,7 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
functionDecl(
hasBody(stmt(forEachDescendant(
declStmt(containsAnyDeclaration(
- LocalValDecl.bind("local-value")),
+ LocalValDecl.bind("value")),
unless(has(decompositionDecl())))
.bind("decl-stmt")))
.bind("scope")))
@@ -152,16 +151,16 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
const auto ParamMatcher =
parmVarDecl(unless(CommonExcludeTypes),
anyOf(hasType(referenceType()), hasType(pointerType())))
- .bind("param-var");
+ .bind("value");
// Match function parameters which could be 'const' if not modified later.
// Example: `void foo(int* ptr)` would match `int* ptr`.
const auto FunctionWithParams =
functionDecl(
- hasBody(stmt().bind("scope-params")),
+ hasBody(stmt().bind("scope")),
has(typeLoc(forEach(ParamMatcher))), unless(cxxMethodDecl()),
unless(isFunctionTemplateSpecialization()), unless(isTemplate()))
- .bind("function-params");
+ .bind("function-decl");
Finder->addMatcher(FunctionWithParams, this);
}
@@ -200,32 +199,17 @@ enum class VariableCategory { Value, Reference, Pointer };
} // namespace
void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
- const Stmt *LocalScope = nullptr;
- const VarDecl *Variable = nullptr;
- const FunctionDecl *Function = nullptr;
- const DeclStmt *VarDeclStmt = nullptr;
-
- if (const auto *LocalVariable =
- Result.Nodes.getNodeAs<VarDecl>("local-value")) {
- Variable = LocalVariable;
- LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
- Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
- VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
- } else if (const auto *Parameter =
- Result.Nodes.getNodeAs<ParmVarDecl>("param-var")) {
- Variable = Parameter;
- LocalScope = Result.Nodes.getNodeAs<Stmt>("scope-params");
- Function = Result.Nodes.getNodeAs<FunctionDecl>("function-params");
- } else {
- llvm_unreachable("didn't match local-value or param-var");
- }
+ const auto* Variable = Result.Nodes.getNodeAs<VarDecl>("value");
+ const auto* LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
+ const auto* Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
+ const auto* VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
assert(Variable && LocalScope && Function);
// It can not be guaranteed that the variable is declared isolated,
// therefore a transformation might effect the other variables as well and
// be incorrect. Parameters don't need this check - they receive values from
- // callers
+ // callers.
const bool CanBeFixIt = isa<ParmVarDecl>(Variable) ||
(VarDeclStmt && VarDeclStmt->isSingleDecl());
>From 6cff3d8b385a86597764f5aa8c1c15e12838e6e9 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Wed, 10 Dec 2025 20:28:02 +0300
Subject: [PATCH 3/7] format
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 27 +++++++++----------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 55ac1b87e420e..e62fcf0615a23 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -136,13 +136,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
// Match the function scope for which the analysis of all local variables
// shall be run.
const auto FunctionScope =
- functionDecl(
- hasBody(stmt(forEachDescendant(
- declStmt(containsAnyDeclaration(
- LocalValDecl.bind("value")),
- unless(has(decompositionDecl())))
- .bind("decl-stmt")))
- .bind("scope")))
+ functionDecl(hasBody(stmt(forEachDescendant(
+ declStmt(containsAnyDeclaration(
+ LocalValDecl.bind("value")),
+ unless(has(decompositionDecl())))
+ .bind("decl-stmt")))
+ .bind("scope")))
.bind("function-decl");
Finder->addMatcher(FunctionScope, this);
@@ -157,9 +156,9 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
// Example: `void foo(int* ptr)` would match `int* ptr`.
const auto FunctionWithParams =
functionDecl(
- hasBody(stmt().bind("scope")),
- has(typeLoc(forEach(ParamMatcher))), unless(cxxMethodDecl()),
- unless(isFunctionTemplateSpecialization()), unless(isTemplate()))
+ hasBody(stmt().bind("scope")), has(typeLoc(forEach(ParamMatcher))),
+ unless(cxxMethodDecl()), unless(isFunctionTemplateSpecialization()),
+ unless(isTemplate()))
.bind("function-decl");
Finder->addMatcher(FunctionWithParams, this);
@@ -199,10 +198,10 @@ enum class VariableCategory { Value, Reference, Pointer };
} // namespace
void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
- const auto* Variable = Result.Nodes.getNodeAs<VarDecl>("value");
- const auto* LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
- const auto* Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
- const auto* VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
+ const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
+ const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("value");
+ const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");
+ const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
assert(Variable && LocalScope && Function);
>From 83b3b67a42b64099c2097957d1d8655d79bc50fd Mon Sep 17 00:00:00 2001
From: Baranov Victor <bar.victor.2002 at gmail.com>
Date: Wed, 17 Dec 2025 00:41:56 +0300
Subject: [PATCH 4/7] fix review
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 16 ++--
.../checks/misc/const-correctness.rst | 4 +-
.../misc/const-correctness-parameters.cpp | 73 +++++++++----------
3 files changed, 41 insertions(+), 52 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index e62fcf0615a23..5d1d988493cdf 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -170,23 +170,17 @@ static void addConstFixits(DiagnosticBuilder &Diag, const VarDecl *Variable,
Qualifiers::TQ Qualifier,
utils::fixit::QualifierTarget Target,
utils::fixit::QualifierPolicy Policy) {
- Diag << addQualifierToVarDecl(*Variable, Context, Qualifier, Target, Policy);
-
// If this is a parameter, also add fixits for corresponding parameters in
// function declarations
if (const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
-
for (const FunctionDecl *Redecl : Function->redecls()) {
- if (Redecl == Function)
- continue;
-
- if (ParamIdx < Redecl->getNumParams()) {
- const ParmVarDecl *RedeclParam = Redecl->getParamDecl(ParamIdx);
- Diag << addQualifierToVarDecl(*RedeclParam, Context, Qualifier, Target,
- Policy);
- }
+ Diag << addQualifierToVarDecl(*Redecl->getParamDecl(ParamIdx), Context,
+ Qualifier, Target, Policy);
}
+ } else {
+ Diag << addQualifierToVarDecl(*Variable, Context, Qualifier, Target,
+ Policy);
}
}
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 dcae96cf860d6..96541a5c437ad 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
@@ -108,8 +108,8 @@ Options
Enable or disable the analysis of function parameters, like
``void foo(int* ptr)``. Only reference and pointer parameters are analyzed.
- As of current implementation, member function (including constructors) and
- lambdas are excluded from the analysis. Default is `true`.
+ Currently, member functions (including constructors) and lambdas are excluded
+ from the analysis. Default is `true`.
.. code-block:: c++
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 dbc2f3e25ca95..f234c305bf54b 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
@@ -6,14 +6,9 @@
// RUN: }}" -- -I %S/Inputs/const-correctness -fno-delayed-template-parsing
struct Bar {
- void buz() const;
- void mutate();
- int value;
-};
-
-struct Foo {
- void method() const;
+ void const_method() const;
void mutating_method();
+ int value;
};
void ref_param_const(Bar& b);
@@ -22,14 +17,14 @@ void ref_param_const(Bar& b);
void ref_param_const(Bar& b) {
// CHECK-MESSAGES: [[@LINE-1]]:22: warning: variable 'b' of type 'Bar &' can be declared 'const'
// CHECK-FIXES: void ref_param_const(Bar const& b) {
- b.buz();
+ b.const_method();
}
-void ref_param_already_const(const Foo& f) {
- f.method();
+void ref_param_already_const(const Bar& f) {
+ f.const_method();
}
-void ref_param_mutated(Foo& f) {
+void ref_param_mutated(Bar& f) {
f.mutating_method();
}
@@ -41,11 +36,11 @@ void pointer_param_read_only(Bar* b) {
// CHECK-MESSAGES: [[@LINE-1]]:30: warning: pointee of variable 'b' of type 'Bar *' can be declared 'const'
// CHECK-MESSAGES: [[@LINE-2]]:30: warning: variable 'b' of type 'Bar *' can be declared 'const'
// CHECK-FIXES: void pointer_param_read_only(Bar const* const b) {
- b->buz();
+ b->const_method();
}
void pointer_param_mutated_pointee(Bar* b) {
- b->mutate();
+ b->mutating_method();
}
void pointer_param_mutated_pointer(Bar* b) {
@@ -61,21 +56,21 @@ void value_param_int(int x) {
}
void value_param_struct(Bar b) {
- b.buz();
+ b.const_method();
}
-void multiple_params_mixed(int x, Bar& b, Foo& f) {
+void multiple_params_mixed(int x, Bar& b, Bar& f) {
// CHECK-MESSAGES: [[@LINE-1]]:35: warning: variable 'b' of type 'Bar &' can be declared 'const'
- // CHECK-FIXES: void multiple_params_mixed(int x, Bar const& b, Foo& f) {
+ // CHECK-FIXES: void multiple_params_mixed(int x, Bar const& b, Bar& f) {
int y = x;
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
// CHECK-FIXES: int const y = x;
- b.buz();
+ b.const_method();
f.mutating_method();
}
void rvalue_ref_param(Bar&& b) {
- b.buz();
+ b.const_method();
}
void pass_to_const_ref(const Bar& b);
@@ -93,7 +88,7 @@ void param_passed_to_nonconst_ref(Bar& b) {
template<typename T>
void template_param(T& t) {
- t.buz();
+ t.const_method();
}
void instantiate_template() {
@@ -113,12 +108,12 @@ void specialized_func(T& t) {
template<>
void specialized_func<Bar>(Bar& b) {
- b.buz();
+ b.const_method();
}
template<int N>
void non_type_template_param(Bar& b) {
- b.buz();
+ b.const_method();
}
template<typename... Args>
@@ -166,7 +161,7 @@ void sfinae_func(T& t, typename enable_if<bool_constant<is_bar<T>::value>>::type
template<typename T>
struct TemplateClass {
void non_template_method(Bar& b) {
- b.buz();
+ b.const_method();
}
template<typename U>
@@ -175,7 +170,7 @@ struct TemplateClass {
}
static void static_method(Bar& b) {
- b.buz();
+ b.const_method();
}
};
@@ -226,14 +221,14 @@ void use_requires_buz() {
void lambda_params() {
auto lambda = [](Bar& b) {
- b.buz();
+ b.const_method();
};
}
struct Container {
Container(int* x) {}
void member_func(Bar& b) {
- b.buz();
+ b.const_method();
}
};
@@ -257,31 +252,31 @@ void decl_multiple(Bar& b);
void decl_multiple(Bar& b) {
// CHECK-MESSAGES: [[@LINE-1]]:20: warning: variable 'b' of type 'Bar &' can be declared 'const'
// CHECK-FIXES: void decl_multiple(Bar const& b) {
- b.buz();
+ b.const_method();
}
-void decl_multi_params(int& x, Bar& b, Foo& f);
-// CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Foo& f);
+void decl_multi_params(int& x, Bar& b, Bar& f);
+// CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Bar& f);
-void decl_multi_params(int& x, Bar& b, Foo& f) {
+void decl_multi_params(int& x, Bar& b, Bar& f) {
// CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'x' of type 'int &' can be declared 'const'
// CHECK-MESSAGES: [[@LINE-2]]:32: warning: variable 'b' of type 'Bar &' can be declared 'const'
- // CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Foo& f) {
+ // CHECK-FIXES: void decl_multi_params(int const& x, Bar const& b, Bar& f) {
int y = x;
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'y' of type 'int' can be declared 'const'
// CHECK-FIXES: int const y = x;
- b.buz();
+ b.const_method();
f.mutating_method(); // f is mutated
}
namespace ns {
- void namespaced_func(Foo& f);
- // CHECK-FIXES: void namespaced_func(Foo const& f);
+ void namespaced_func(Bar& f);
+ // CHECK-FIXES: void namespaced_func(Bar const& f);
- void namespaced_func(Foo& f) {
- // CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'f' of type 'Foo &' can be declared 'const'
- // CHECK-FIXES: void namespaced_func(Foo const& f) {
- f.method();
+ void namespaced_func(Bar& f) {
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: variable 'f' of type 'Bar &' can be declared 'const'
+ // CHECK-FIXES: void namespaced_func(Bar const& f) {
+ f.const_method();
}
}
@@ -485,11 +480,11 @@ void struct_ptr_param(Bar** bp) {
// CHECK-MESSAGES: [[@LINE-1]]:23: warning: pointee of variable 'bp' of type 'Bar **' can be declared 'const'
// CHECK-MESSAGES: [[@LINE-2]]:23: warning: variable 'bp' of type 'Bar **' can be declared 'const'
// CHECK-FIXES: void struct_ptr_param(Bar* const* const bp) {
- (*bp)->buz();
+ (*bp)->const_method();
}
void struct_ptr_param_modified(Bar** bp) {
// CHECK-MESSAGES: [[@LINE-1]]:32: warning: variable 'bp' of type 'Bar **' can be declared 'const'
// CHECK-FIXES: void struct_ptr_param_modified(Bar** const bp) {
- (*bp)->mutate();
+ (*bp)->mutating_method();
}
>From af24b116ed54323cb26ec3771930e0ad04a09251 Mon Sep 17 00:00:00 2001
From: Baranov Victor <bar.victor.2002 at gmail.com>
Date: Wed, 17 Dec 2025 00:55:58 +0300
Subject: [PATCH 5/7] improve tests a little
---
.../misc/const-correctness-parameters.cpp | 60 ++++++++-----------
1 file changed, 26 insertions(+), 34 deletions(-)
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 f234c305bf54b..b988b7985463e 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
@@ -91,19 +91,14 @@ void template_param(T& t) {
t.const_method();
}
-void instantiate_template() {
- Bar b;
- template_param(b);
-}
-
template<typename T>
void forwarding_ref(T&& t) {
- t.method();
+ t.mutating_method();
}
template<typename T>
void specialized_func(T& t) {
- t.buz();
+ t.const_method();
}
template<>
@@ -118,12 +113,12 @@ void non_type_template_param(Bar& b) {
template<typename... Args>
void variadic_template(Args&... args) {
- (args.buz(), ...);
+ (args.const_method(), ...);
}
template<typename First, typename... Rest>
void variadic_first_param(First& first, Rest&... rest) {
- first.buz();
+ first.const_method();
}
template<typename T>
@@ -155,9 +150,23 @@ struct bool_constant<true> : true_type {};
template<typename T>
void sfinae_func(T& t, typename enable_if<bool_constant<is_bar<T>::value>>::type* = nullptr) {
- t.buz();
+ t.const_method();
+}
+
+void instantiate() {
+ int a = 42;
+ Bar b;
+ template_param(b);
+ forwarding_ref(b);
+ specialized_func(b);
+ non_type_template_param<2>(b);
+ variadic_template(b, b);
+ variadic_first_param(b, a);
+ sfinae_func(b);
}
+// Leave this for futher reference if const-correctness is implemented on template functions/methods
+
template<typename T>
struct TemplateClass {
void non_template_method(Bar& b) {
@@ -166,7 +175,7 @@ struct TemplateClass {
template<typename U>
void template_method(U& u) {
- u.buz();
+ u.const_method();
}
static void static_method(Bar& b) {
@@ -176,29 +185,12 @@ struct TemplateClass {
template struct TemplateClass<int>;
-struct NonTemplateClass {
- template<typename T>
- void template_method(T& t) {
- t.buz();
- }
-
- template<typename T>
- static void static_template_method(T& t) {
- t.buz();
- }
-};
-
-template<typename T = Bar>
-void default_template_arg(T& t) {
- t.buz();
-}
-
template<typename Outer>
struct OuterTemplate {
template<typename Inner>
static void nested_template_func(Outer& o, Inner& i) {
- o.buz();
- i.buz();
+ o.const_method();
+ i.const_method();
}
};
@@ -207,16 +199,16 @@ using RefAlias = T&;
template<typename T>
void alias_template_param(RefAlias<T> t) {
- t.buz();
+ t.const_method();
}
template<typename T>
-void requires_buz(T& t) {
+void requires_const_method(T& t) {
}
-void use_requires_buz() {
+void use_requires_const_method() {
Bar b;
- requires_buz(b);
+ requires_const_method(b);
}
void lambda_params() {
>From b9335901ce1821fa9c3b5efd113384d07401afec Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Fri, 2 Jan 2026 20:48:30 +0300
Subject: [PATCH 6/7] fix review
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 8 +++++
.../checks/misc/const-correctness.rst | 8 +++--
.../misc/const-correctness-parameters.cpp | 31 ++++++++++++++++++-
3 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 5d1d988493cdf..6eb371a58ed1a 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -174,6 +174,14 @@ static void addConstFixits(DiagnosticBuilder &Diag, const VarDecl *Variable,
// function declarations
if (const auto *ParamDecl = dyn_cast<ParmVarDecl>(Variable)) {
const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
+ // Skip if all fix-its can not be applied properly due to 'using'/'typedef'
+ if (llvm::any_of(
+ Function->redecls(), [ParamIdx](const FunctionDecl *Redecl) {
+ const QualType Type = Redecl->getParamDecl(ParamIdx)->getType();
+ return Type->isTypedefNameType() || Type->getAs<UsingType>();
+ }))
+ return;
+
for (const FunctionDecl *Redecl : Function->redecls()) {
Diag << addQualifierToVarDecl(*Redecl->getParamDecl(ParamIdx), Context,
Qualifier, Target, Policy);
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 96541a5c437ad..b458dda8fd66e 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
@@ -3,9 +3,11 @@
misc-const-correctness
======================
-This check implements detection of local variables and function parameters
-which could be declared as ``const`` but are not. Declaring variables as
-``const`` is required or recommended by many coding guidelines, such as:
+Finds local variables and function parameters which could be declared as
+``const`` but are not.
+
+Declaring variables as ``const`` is required or recommended by many coding
+guidelines, such as:
`ES.25 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on>`_
from the C++ Core Guidelines.
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 b988b7985463e..96d54b8441700 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
@@ -165,7 +165,7 @@ void instantiate() {
sfinae_func(b);
}
-// Leave this for futher reference if const-correctness is implemented on template functions/methods
+// Leave this for further reference if const-correctness is implemented on template functions/methods
template<typename T>
struct TemplateClass {
@@ -272,6 +272,35 @@ namespace ns {
}
}
+using int_ptr = int*;
+using f_signature = void(int*);
+
+void decl_different_style(int_ptr);
+f_signature decl_different_style;
+void decl_different_style(int* p) {
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:27: warning: variable 'p' of type 'int *' can be declared 'const'
+ // No CHECK-FIXES - declaration uses 'using'
+}
+
+typedef int* int_ptr_typedef;
+void typedef_decl(int_ptr_typedef);
+void typedef_decl(int* p) {
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:19: warning: variable 'p' of type 'int *' can be declared 'const'
+ // No CHECK-FIXES - declaration uses 'typedef'
+}
+
+void multi_param_one_alias(int_ptr, int*);
+void multi_param_one_alias(int* p, int* q) {
+ // CHECK-MESSAGES: [[@LINE-1]]:28: warning: pointee of variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:28: warning: variable 'p' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-3]]:36: warning: pointee of variable 'q' of type 'int *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-4]]:36: warning: variable 'q' of type 'int *' can be declared 'const'
+ // CHECK-FIXES: void multi_param_one_alias(int_ptr, int const*const );
+ // CHECK-FIXES-NEXT: void multi_param_one_alias(int* p, int const* const q) {
+}
+
void func_ptr_param(void (*fp)(int&)) {
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: variable 'fp' of type 'void (*)(int &)' can be declared 'const'
// CHECK-FIXES: void func_ptr_param(void (*const fp)(int&)) {
>From df2501373f113e278dc34eebeeb0fcd6aa1ce988 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Fri, 9 Jan 2026 09:25:30 +0800
Subject: [PATCH 7/7] fix wording
---
.../docs/clang-tidy/checks/misc/const-correctness.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 b458dda8fd66e..0365df2aa4504 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
@@ -60,8 +60,8 @@ Limitations
The check does not run on `C` code.
-The check will not analyze templated variables and template functions or
-variables that are instantiation dependent. Different instantiations can result
+The check will not analyze templated variables, template functions or variables
+that are instantiation dependent. Different instantiations can result
in different ``const`` correctness properties and in general it is not
possible to find all instantiations of a template. The template might
be used differently in an independent translation unit.
More information about the cfe-commits
mailing list