Author: Moonn
Date: 2026-10-13T13:29:44+08:00
New Revision: f1136210323eb4cf169ea544c48fa650275a8c79

URL: 
https://github.com/llvm/llvm-project/commit/f1136210323eb4cf169ea544c48fa650275a8c79
DIFF: 
https://github.com/llvm/llvm-project/commit/f1136210323eb4cf169ea544c48fa650275a8c79.diff

LOG: [clang-tidy] Fix false positive in performance-no-automatic-move for 
captured variables (#231359)

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.

AI Usage: an AI assistant was used while working on this fix, and I went
through and tested every change myself.

Fixes #227240

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp
    clang-tools-extra/docs/ReleaseNotes.md
    clang-tools-extra/test/clang-tidy/checkers/performance/no-automatic-move.cpp

Removed: 
    


################################################################################
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..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();
@@ -115,3 +115,32 @@ 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]
+  };
+}
+
+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]
+  };
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to