[clang] [clang][Sema] Fix false positive -Wshadow with structured binding captures (PR #157667)

Ivan Murashko via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 9 06:08:09 PDT 2025


https://github.com/ivanmurashko created https://github.com/llvm/llvm-project/pull/157667

Fixes #68605.

Previously, lambda init captures that captured structured bindings would incorrectly emit shadow warnings, even though regular parameter captures don't emit such warnings. This created inconsistent behavior:

```cpp
void foo1(std::pair<int, int> val) {
  [val = std::move(val)](){}();  // No warning (correct)
}

void foo2(std::pair<int, int> val) {
  auto [a, b] = val;
  [a = std::move(a)](){}();      // Warning (incorrect)
}
```

The fix modifies `getShadowedDeclaration()` for `VarDecl` to return `nullptr` when a lambda init capture would shadow a `BindingDecl`, ensuring consistent behavior between regular captures and structured binding captures.

>From 1e8d8847364b881440b70b1bf9c4a6864b0ec228 Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.murashko at gmail.com>
Date: Tue, 9 Sep 2025 13:42:46 +0100
Subject: [PATCH] [clang][Sema] Fix false positive -Wshadow with structured
 binding captures

Fixes #68605.

Previously, lambda init captures that captured structured bindings would
incorrectly emit shadow warnings, even though regular parameter captures
don't emit such warnings. This created inconsistent behavior:

```cpp
void foo1(std::pair<int, int> val) {
  [val = val](){}();  // No warning (correct)
}

void foo2(std::pair<int, int> val) {
  auto [a, b] = val;
  [a = a](){}();      // Warning (incorrect)
}
```

The fix modifies getShadowedDeclaration() for VarDecl to return nullptr
when a lambda init capture would shadow a BindingDecl, ensuring consistent
behavior between regular captures and structured binding captures.
---
 clang/lib/Sema/SemaDecl.cpp    |  6 +++
 clang/test/SemaCXX/PR68605.cpp | 71 ++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 clang/test/SemaCXX/PR68605.cpp

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 365ebb63b1559..311105f31e3e3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8410,6 +8410,12 @@ NamedDecl *Sema::getShadowedDeclaration(const VarDecl *D,
     return nullptr;
 
   NamedDecl *ShadowedDecl = R.getFoundDecl();
+
+  // Don't warn when lambda captures shadow structured bindings.
+  // This ensures consistency with regular parameter captures.
+  if (isa<BindingDecl>(ShadowedDecl) && D->isInitCapture())
+    return nullptr;
+
   return isa<VarDecl, FieldDecl, BindingDecl>(ShadowedDecl) ? ShadowedDecl
                                                             : nullptr;
 }
diff --git a/clang/test/SemaCXX/PR68605.cpp b/clang/test/SemaCXX/PR68605.cpp
new file mode 100644
index 0000000000000..90e4876b3e394
--- /dev/null
+++ b/clang/test/SemaCXX/PR68605.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -std=c++20 -Wshadow %s
+
+// Test for issue #68605: False positive warning with `-Wshadow` when using 
+// structured binding and lambda capture.
+// 
+// The issue is that structured bindings should behave consistently with 
+// regular variables when used in lambda captures - no shadow warning should
+// be emitted when a lambda capture variable has the same name as the captured
+// structured binding, just like with regular parameters.
+
+namespace std {
+  template<typename T> T&& move(T&& t) { return static_cast<T&&>(t); }
+}
+
+namespace issue_68605 {
+
+// Simple pair-like struct for testing
+struct Pair {
+  int first;
+  int second;
+  Pair(int f, int s) : first(f), second(s) {}
+};
+
+// Test case 1: Regular parameter - should NOT produce warning (baseline)
+void foo1(Pair val) {
+  [val = std::move(val)](){}(); // No warning expected
+}
+
+// Test case 2: Structured binding - should NOT produce warning
+void foo2(Pair val) {
+  auto [a,b] = val;
+  [a = std::move(a)](){}(); // No warning - consistent with regular parameter behavior
+}
+
+// Test case 3: More complex example with multiple captures
+void foo3() {
+  Pair data{42, 100};
+  auto [id, value] = data;
+  
+  // Both of these should NOT produce warnings
+  auto lambda1 = [id = id](){ return id; }; // No warning
+  auto lambda2 = [value = value](){ return value; }; // No warning
+}
+
+// Test case 4: Mixed scenario with regular var and structured binding
+void foo4() {
+  int regular_var = 10;
+  Pair pair_data{1, 2};
+  auto [x, y] = pair_data;
+  
+  // Regular variable capture - no warning expected (current behavior)
+  auto lambda1 = [regular_var = regular_var](){};
+  
+  // Structured binding captures - should be consistent
+  auto lambda2 = [x = x](){}; // No warning - consistent behavior
+  auto lambda3 = [y = y](){}; // No warning - consistent behavior
+}
+
+// Test case 5: Ensure we don't break existing shadow detection for actual shadowing
+void foo5() {
+  int outer = 5; // expected-note {{previous declaration is here}}
+  auto [a, b] = Pair{1, 2}; // expected-note {{previous declaration is here}}
+  
+  // This SHOULD still warn - it's actual shadowing within the lambda body
+  auto lambda = [outer, a](){ // expected-note {{variable 'outer' is explicitly captured here}}
+    int outer = 10; // expected-warning {{declaration shadows a local variable}}
+    int a = 20;     // expected-warning {{declaration shadows a structured binding}}
+  };
+}
+
+} // namespace issue_68605
\ No newline at end of file



More information about the cfe-commits mailing list