[clang-tools-extra] [clang-tidy] Don't suggest const on `std::moved` locals in templates … (PR #226577)
Daniel Petrovic via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 25 12:46:11 PDT 2026
https://github.com/daniel-petrovic created https://github.com/llvm/llvm-project/pull/226577
…(#225395)
Skip diagnostics in template instantiations for locals that are used as arguments of std::move. Reason: for some instantiations the move may be converted to copy so the variable may look unmutated and flagged, whereas for other template instantiations the real move semantics may apply.
Fixes: #225395
>From 1669c325e106b865461c603a7e984091fbea2196 Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <daniel-dev at hotmail.de>
Date: Wed, 23 Sep 2026 22:09:58 +0200
Subject: [PATCH] [clang-tidy] Don't suggest const on `std::moved` locals in
templates (#225395)
Skip diagnostics in template instantiations for locals that are used
as arguments of std::move. Reason: for some instantiations the move
may be converted to copy so the variable may look unmutated and flagged,
whereas for other template instantiations the real move semantics may
apply.
Fixes: #225395
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 29 +++++++++++++++++--
.../misc/const-correctness-templates.cpp | 24 +++++++++++++++
2 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 57c2bda6c3fd5..3ca76ba915c70 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -199,6 +199,24 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
}
}
+static bool isStdMoveOperand(const VarDecl *Variable, const FunctionDecl *Func,
+ ASTContext &Context) {
+ if (Func->isTemplateInstantiation())
+ if (const Stmt *Body = Func->getBody()) {
+ // Match on the instantiated body where `std::move` is resolved to a
+ // concrete function and the argument references the instantiated
+ // variable.
+ const auto Moves =
+ match(stmt(forEachDescendant(
+ callExpr(callee(functionDecl(hasName("std::move"))),
+ hasArgument(0, ignoringImpCasts(declRefExpr(
+ to(equalsNode(Variable)))))))),
+ *Body, Context);
+ return !Moves.empty();
+ }
+ return false;
+}
+
static void addConstFixits(const DiagnosticBuilder &Diag,
const VarDecl *Variable,
const FunctionDecl *Function,
@@ -255,9 +273,6 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
/// dependent. Variables like 'int x = 42;' in a template that can become
/// const emit multiple warnings otherwise.
bool IsNormalVariableInTemplate = Function->isTemplateInstantiation();
- if (IsNormalVariableInTemplate &&
- TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
- return;
VariableCategory VC = VariableCategory::Value;
const QualType VT = Variable->getType();
@@ -269,6 +284,14 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
ArrayT && ArrayT->getElementType()->isPointerType())
VC = VariableCategory::Pointer;
+ if (IsNormalVariableInTemplate && VC != VariableCategory::Pointer &&
+ isStdMoveOperand(Variable, Function, *Result.Context))
+ return;
+
+ if (IsNormalVariableInTemplate &&
+ TemplateDiagnosticsCache.contains(Variable->getBeginLoc()))
+ return;
+
const auto CheckValue = [&]() {
// Offload const-analysis to utility function.
if (isMutated(Variable, LocalScope, Function, Result.Context))
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..c952dce7b99c5 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
@@ -6,6 +6,9 @@
// RUN: misc-const-correctness.TransformPointersAsValues: false} \
// RUN: }" -- -fno-delayed-template-parsing
+#include <string>
+#include <utility>
+
template <typename T>
void type_dependent_variables() {
T value = 42;
@@ -27,6 +30,27 @@ void instantiate_template_cases() {
type_dependent_variables<float>();
}
+namespace gh225395 {
+// The variable 'tmp' is passed to std::move in the template definition, so it
+// may be moved-from for some instantiations (e.g. std::string) but std::move
+// degenerates into a copy for others (e.g. int). No warning may be emitted
+// since the fix-it would apply to the template code and break the move for
+// types with real move semantics.
+template <typename T> void my_swap(T &t1, T &t2) {
+ auto tmp = std::move(t1);
+ t1 = std::move(t2);
+ t2 = std::move(tmp);
+}
+void instantiate() {
+ std::string a;
+ std::string b;
+ my_swap(a, b);
+ int x = 0;
+ int y = 0;
+ my_swap(x, y);
+}
+} // namespace gh225395
+
namespace gh57297{
// The expression to check may not be the dependent operand in a dependent
// operator.
More information about the cfe-commits
mailing list