[clang-tools-extra] [clang-tidy] Fix false positive in performance-no-automatic-move for captured variables (PR #231359)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 12 21:13:56 PDT 2026
https://github.com/your-moon updated https://github.com/llvm/llvm-project/pull/231359
>From 0d32d9f3e60e7035f44bd6e6283c0fa822fd4318 Mon Sep 17 00:00:00 2001
From: Munkherdene <muunuu960 at gmail.com>
Date: Tue, 13 Oct 2026 10:52:40 +0800
Subject: [PATCH 1/2] [clang-tidy] Fix false positive in
performance-no-automatic-move for captured variables
performance-no-automatic-move matched any `return x` where `x` is a const local
variable, without checking whether `x` is actually local to the function doing
the return. Inside a lambda, a captured variable still points at the enclosing
function's VarDecl, so `[&x] { return x; }` was diagnosed. But captured variables
are never implicitly moved from (Sema bails on refersToEnclosingVariableOrCapture()
before it looks at constness), so dropping the const still gives a copy.
Now we skip DeclRefExprs that refer to a capture, same as UseStdMoveCheck. A const
local declared inside the lambda body is still diagnosed.
Fixes #227240
---
.../performance/NoAutomaticMoveCheck.cpp | 11 +++++++++--
clang-tools-extra/docs/ReleaseNotes.md | 5 +++++
.../checkers/performance/no-automatic-move.cpp | 16 ++++++++++++++++
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
index ca23488be204a..3722d33cf54cb 100644
--- a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
@@ -20,6 +20,10 @@ namespace {
AST_MATCHER(VarDecl, isNRVOVariable) { return Node.isNRVOVariable(); }
+AST_MATCHER(DeclRefExpr, refersToEnclosingVariableOrCapture) {
+ return Node.refersToEnclosingVariableOrCapture();
+}
+
} // namespace
NoAutomaticMoveCheck::NoAutomaticMoveCheck(StringRef Name,
@@ -62,8 +66,11 @@ void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
ignoringElidableConstructorCall(ignoringParenImpCasts(
cxxConstructExpr(
hasDeclaration(anyOf(LValueRefCtor, ConstRefRefCtor)),
- hasArgument(0, ignoringParenImpCasts(declRefExpr(
- to(NonNrvoConstLocalVariable)))))
+ hasArgument(
+ 0,
+ ignoringParenImpCasts(declRefExpr(
+ to(NonNrvoConstLocalVariable),
+ unless(refersToEnclosingVariableOrCapture())))))
.bind("ctor_call")))))),
this);
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index a6d8c764728b4..1448005253556 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -314,6 +314,11 @@ infrastructure are described first, followed by tool-specific sections.
{option}`ForRangeLoopClasses` to configure container classes that can be used
as sources in range-based `for` loops.
+- Improved {doc}`performance-no-automatic-move
+ <clang-tidy/checks/performance/no-automatic-move>` check by no longer
+ warning when returning a variable captured by a lambda or a block, which is
+ never implicitly moved from.
+
- Improved {doc}`performance-prefer-single-char-overloads
<clang-tidy/checks/performance/prefer-single-char-overloads>` check to
avoid offering fix-its for string literals originating from macro
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
index 1ef7d16e38393..f0a8fd641bd2b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -115,3 +115,19 @@ FromConstRefOnly FromConstRefOnly() {
const Obj obj = Make<Obj>();
return obj;
}
+
+void Captures() {
+ const Obj obj = Make<Obj>();
+ auto ByRef = [&obj]() -> NonTemplate { return obj; };
+ auto ByCopy = [obj]() -> NonTemplate { return obj; };
+ auto DefaultByRef = [&]() -> NonTemplate { return obj; };
+ auto DefaultByCopy = [=]() -> NonTemplate { return obj; };
+}
+
+void LambdaLocal() {
+ auto L = []() -> NonTemplate {
+ const Obj obj = Make<Obj>();
+ return obj;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+ };
+}
>From 26215b04760ce12e188206e3fbaad2171295bb07 Mon Sep 17 00:00:00 2001
From: Munkherdene <muunuu960 at gmail.com>
Date: Tue, 13 Oct 2026 12:13:41 +0800
Subject: [PATCH 2/2] [clang-tidy] Test performance-no-automatic-move with
block captures
The release note says captured variables in lambdas and blocks are no
longer diagnosed, but only lambdas were tested. Run the test with -fblocks
and cover a variable captured by a block and a const local declared inside
one.
---
.../checkers/performance/no-automatic-move.cpp | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
index f0a8fd641bd2b..70b017b936148 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++11-or-later %s performance-no-automatic-move %t
+// RUN: %check_clang_tidy -std=c++11-or-later %s performance-no-automatic-move %t -- -- -fblocks
struct Obj {
Obj();
@@ -131,3 +131,16 @@ void LambdaLocal() {
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
};
}
+
+void BlockCaptures() {
+ const Obj obj = Make<Obj>();
+ auto B = ^NonTemplate { return obj; };
+}
+
+void BlockLocal() {
+ auto B = ^NonTemplate {
+ const Obj obj = Make<Obj>();
+ return obj;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: constness of 'obj' prevents automatic move [performance-no-automatic-move]
+ };
+}
More information about the cfe-commits
mailing list