[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