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

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

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 +++++++++++++++++++
 clang/test/SemaCXX/warn-shadow-in-lambdas.cpp |  5 +-
 3 files changed, 79 insertions(+), 3 deletions(-)
 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
diff --git a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp 
b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
index d54b394df4eb8..2221c3f6d1049 100644
--- a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
+++ b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
@@ -258,9 +258,8 @@ struct S {
 };
 
 int foo() {
-  auto [a] = S{0}; // expected-note {{previous}} \
-                   // cxx14-warning {{decomposition declarations are a C++17 
extension}}
-  [a = a] () { // expected-warning {{declaration shadows a structured binding}}
+  auto [a] = S{0}; // cxx14-warning {{decomposition declarations are a C++17 
extension}}
+  [a = a] () { // No warning - consistent with regular parameter captures
   }();
 }
 

>From c0723fefc075729f5f1526115c81e42a34c9c51b Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Wed, 10 Sep 2025 10:48:52 +0100
Subject: [PATCH 2/8] [Sema] Fix inconsistent shadow warnings for lambda
 capture of structured bindings

Lambda captures that shadow structured bindings were incorrectly classified
as regular shadow warnings (shown with -Wshadow) while regular parameter
captures were classified as uncaptured-local warnings (shown only with
-Wshadow-all). This created inconsistent behavior between semantically
equivalent code patterns.

This change extends the existing lambda capture classification logic to
handle BindingDecl consistently with VarDecl:
- Lambda init captures of structured bindings now show as uncaptured-local
- Regular variable declarations inside lambda bodies still show as shadow
- All existing shadow warning functionality is preserved

The fix ensures consistent behavior between:
  void func(std::pair<int,int> val) { [val = val](){}; }     // no -Wshadow, 
warns with -Wshadow-all
  void func(std::pair<int,int> val) { auto [a,b] = val; [a = a](){}; }  // no 
-Wshadow, warns with -Wshadow-all

Previously the structured binding case incorrectly produced warnings with
basic -Wshadow, preventing structured bindings from being used in lambda
captures consistently with regular parameters.
---
 clang/lib/Sema/SemaDecl.cpp    | 42 ++++++++++++++++++-----
 clang/test/SemaCXX/PR68605.cpp | 63 +++++++++++++++++-----------------
 2 files changed, 65 insertions(+), 40 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 311105f31e3e3..ce2b3136e5ff4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8411,11 +8411,6 @@ NamedDecl *Sema::getShadowedDeclaration(const VarDecl *D,
 
   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;
 }
@@ -8485,6 +8480,7 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
   if (isa<VarDecl>(D) && NewDC && isa<CXXMethodDecl>(NewDC)) {
     if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
       if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
+        // Handle lambda capture logic for both VarDecl and BindingDecl
         if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
           const auto *LSI = cast<LambdaScopeInfo>(getCurFunction());
           if (RD->getLambdaCaptureDefault() == LCD_None) {
@@ -8502,6 +8498,21 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
                 ->ShadowingDecls.push_back({D, VD});
             return;
           }
+        } else if (isa<BindingDecl>(ShadowedDecl)) {
+          // Apply lambda capture logic only when D is actually a lambda 
capture
+          if (isa<VarDecl>(D) && cast<VarDecl>(D)->isInitCapture()) {
+            if (RD->getLambdaCaptureDefault() == LCD_None) {
+              // BindingDecls cannot be explicitly captured, so always treat as
+              // uncaptured
+              WarningDiag = diag::warn_decl_shadow_uncaptured_local;
+            } else {
+              // Same deferred handling as VarDecl
+              cast<LambdaScopeInfo>(getCurFunction())
+                  ->ShadowingDecls.push_back({D, ShadowedDecl});
+              return;
+            }
+          }
+          // For non-init-capture cases, fall through to regular shadow logic
         }
         if (isa<FieldDecl>(ShadowedDecl)) {
           // If lambda can capture this, then emit default shadowing warning,
@@ -8514,10 +8525,17 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
           return;
         }
       }
-      if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl);
-          VD && VD->hasLocalStorage()) {
-        // A variable can't shadow a local variable in an enclosing scope, if
-        // they are separated by a non-capturing declaration context.
+      // Apply scoping logic to both VarDecl and BindingDecl
+      bool shouldApplyScopingLogic = false;
+      if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
+        shouldApplyScopingLogic = VD->hasLocalStorage();
+      } else if (isa<BindingDecl>(ShadowedDecl)) {
+        shouldApplyScopingLogic = true;
+      }
+
+      if (shouldApplyScopingLogic) {
+        // A variable can't shadow a local variable or binding in an enclosing
+        // scope, if they are separated by a non-capturing declaration context.
         for (DeclContext *ParentDC = NewDC;
              ParentDC && !ParentDC->Equals(OldDC);
              ParentDC = getLambdaAwareParentOfDeclContext(ParentDC)) {
@@ -8583,6 +8601,12 @@ void Sema::DiagnoseShadowingLambdaDecls(const 
LambdaScopeInfo *LSI) {
         Diag(CaptureLoc, diag::note_var_explicitly_captured_here)
             << Shadow.VD->getDeclName() << /*explicitly*/ 0;
       Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
+    } else if (isa<BindingDecl>(ShadowedDecl)) {
+      // BindingDecls cannot be explicitly captured, so always uncaptured-local
+      Diag(Shadow.VD->getLocation(), diag::warn_decl_shadow_uncaptured_local)
+          << Shadow.VD->getDeclName()
+          << computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC;
+      Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
     } else if (isa<FieldDecl>(ShadowedDecl)) {
       Diag(Shadow.VD->getLocation(),
            LSI->isCXXThisCaptured() ? diag::warn_decl_shadow
diff --git a/clang/test/SemaCXX/PR68605.cpp b/clang/test/SemaCXX/PR68605.cpp
index 90e4876b3e394..b981f6c4eecce 100644
--- a/clang/test/SemaCXX/PR68605.cpp
+++ b/clang/test/SemaCXX/PR68605.cpp
@@ -1,12 +1,15 @@
 // RUN: %clang_cc1 -verify -fsyntax-only -std=c++20 -Wshadow %s
+// RUN: %clang_cc1 -verify=all -fsyntax-only -std=c++20 -Wshadow-all %s
 
-// Test for issue #68605: False positive warning with `-Wshadow` when using 
-// structured binding and lambda capture.
+// Test for issue #68605: Inconsistent shadow warnings for lambda capture of 
structured bindings.
 // 
-// 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.
+// The issue was that structured binding lambda captures were incorrectly 
classified
+// as regular shadow warnings (shown with -Wshadow) while regular parameter 
captures 
+// were classified as uncaptured-local warnings (shown only with -Wshadow-all).
+//
+// This test validates that both VarDecl and BindingDecl lambda captures now 
+// behave consistently: no warnings with -Wshadow, but uncaptured-local 
warnings 
+// with -Wshadow-all.
 
 namespace std {
   template<typename T> T&& move(T&& t) { return static_cast<T&&>(t); }
@@ -21,50 +24,48 @@ struct Pair {
   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 1: Regular parameter - consistent behavior
+void foo1(Pair val) { // all-note {{previous declaration is here}}
+  [val = std::move(val)](){}(); // all-warning {{declaration shadows a local 
variable}}
 }
 
-// Test case 2: Structured binding - should NOT produce warning
+// Test case 2: Structured binding - now consistent with regular parameter
 void foo2(Pair val) {
-  auto [a,b] = val;
-  [a = std::move(a)](){}(); // No warning - consistent with regular parameter 
behavior
+  auto [a,b] = val; // all-note {{previous declaration is here}}
+  [a = std::move(a)](){}(); // all-warning {{declaration shadows a structured 
binding}}
 }
 
-// Test case 3: More complex example with multiple captures
+// Test case 3: Multiple captures showing consistent behavior
 void foo3() {
   Pair data{42, 100};
-  auto [id, value] = data;
+  auto [id, value] = data; // all-note 2{{previous declaration is here}}
   
-  // Both of these should NOT produce warnings
-  auto lambda1 = [id = id](){ return id; }; // No warning
-  auto lambda2 = [value = value](){ return value; }; // No warning
+  // Both show consistent uncaptured-local warnings with -Wshadow-all
+  auto lambda1 = [id = id](){ return id; }; // all-warning {{declaration 
shadows a structured binding}}
+  auto lambda2 = [value = value](){ return value; }; // all-warning 
{{declaration shadows a structured binding}}
 }
 
-// Test case 4: Mixed scenario with regular var and structured binding
+// Test case 4: Mixed scenario showing consistent behavior
 void foo4() {
-  int regular_var = 10;
+  int regular_var = 10; // all-note {{previous declaration is here}}
   Pair pair_data{1, 2};
-  auto [x, y] = pair_data;
+  auto [x, y] = pair_data; // all-note 2{{previous declaration is here}}
   
-  // 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
+  // All captures now show consistent uncaptured-local warnings with 
-Wshadow-all
+  auto lambda1 = [regular_var = regular_var](){}; // all-warning {{declaration 
shadows a local variable}}
+  auto lambda2 = [x = x](){}; // all-warning {{declaration shadows a 
structured binding}}
+  auto lambda3 = [y = y](){}; // all-warning {{declaration shadows a 
structured binding}}
 }
 
 // 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}}
+  int outer = 5; // expected-note {{previous declaration is here}} all-note 
{{previous declaration is here}}
+  auto [a, b] = Pair{1, 2}; // expected-note {{previous declaration is here}} 
all-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}}
+  auto lambda = [outer, a](){ // expected-note {{variable 'outer' is 
explicitly captured here}} all-note {{variable 'outer' is explicitly captured 
here}}
+    int outer = 10; // expected-warning {{declaration shadows a local 
variable}} all-warning {{declaration shadows a local variable}}
+    int a = 20;     // expected-warning {{declaration shadows a structured 
binding}} all-warning {{declaration shadows a structured binding}}
   };
 }
 

>From 74a2ff78c5bf82666696d8cf08c07f6e932ee07b Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Wed, 10 Sep 2025 13:03:04 +0100
Subject: [PATCH 3/8] Fix warn-shadow-in-lambdas test for structured bindings

---
 clang/test/SemaCXX/warn-shadow-in-lambdas.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp 
b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
index 2221c3f6d1049..2388c5f16e4ca 100644
--- a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
+++ b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
@@ -258,9 +258,15 @@ struct S {
 };
 
 int foo() {
+#ifdef AVOID
   auto [a] = S{0}; // cxx14-warning {{decomposition declarations are a C++17 
extension}}
-  [a = a] () { // No warning - consistent with regular parameter captures
+  [a = a] () { // No warning with basic -Wshadow due to uncaptured-local 
classification
+  }();
+#else
+  auto [a] = S{0}; // cxx14-warning {{decomposition declarations are a C++17 
extension}} expected-note {{previous declaration is here}}
+  [a = a] () { // expected-warning {{declaration shadows a structured binding}}
   }();
+#endif
 }
 
 }

>From 27fc9b7095e6ab993782e121e1ff1a3776b2ab0a Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Wed, 10 Sep 2025 15:43:36 +0100
Subject: [PATCH 4/8] [Sema] Consolidate VarDecl and BindingDecl shadow
 detection using ValueDecl

Consolidate the handling of VarDecl and BindingDecl in shadow detection
by using their common base class ValueDecl.
---
 clang/lib/Sema/SemaDecl.cpp    | 79 +++++++++++++++-------------------
 clang/test/SemaCXX/PR68605.cpp |  2 +-
 2 files changed, 35 insertions(+), 46 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index ce2b3136e5ff4..193db2726273a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8382,7 +8382,7 @@ static ShadowedDeclKind computeShadowedDeclKind(const 
NamedDecl *ShadowedDecl,
 /// Return the location of the capture if the given lambda captures the given
 /// variable \p VD, or an invalid source location otherwise.
 static SourceLocation getCaptureLocation(const LambdaScopeInfo *LSI,
-                                         const VarDecl *VD) {
+                                         const ValueDecl *VD) {
   for (const Capture &Capture : LSI->Captures) {
     if (Capture.isVariableCapture() && Capture.getVariable() == VD)
       return Capture.getLocation();
@@ -8410,7 +8410,6 @@ NamedDecl *Sema::getShadowedDeclaration(const VarDecl *D,
     return nullptr;
 
   NamedDecl *ShadowedDecl = R.getFoundDecl();
-
   return isa<VarDecl, FieldDecl, BindingDecl>(ShadowedDecl) ? ShadowedDecl
                                                             : nullptr;
 }
@@ -8480,9 +8479,11 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
   if (isa<VarDecl>(D) && NewDC && isa<CXXMethodDecl>(NewDC)) {
     if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
       if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
-        // Handle lambda capture logic for both VarDecl and BindingDecl
-        if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
+        // Handle both VarDecl and BindingDecl in lambda contexts
+        if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
+          const auto *VD = cast<ValueDecl>(ShadowedDecl);
           const auto *LSI = cast<LambdaScopeInfo>(getCurFunction());
+
           if (RD->getLambdaCaptureDefault() == LCD_None) {
             // Try to avoid warnings for lambdas with an explicit capture
             // list. Warn only when the lambda captures the shadowed decl
@@ -8498,21 +8499,6 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
                 ->ShadowingDecls.push_back({D, VD});
             return;
           }
-        } else if (isa<BindingDecl>(ShadowedDecl)) {
-          // Apply lambda capture logic only when D is actually a lambda 
capture
-          if (isa<VarDecl>(D) && cast<VarDecl>(D)->isInitCapture()) {
-            if (RD->getLambdaCaptureDefault() == LCD_None) {
-              // BindingDecls cannot be explicitly captured, so always treat as
-              // uncaptured
-              WarningDiag = diag::warn_decl_shadow_uncaptured_local;
-            } else {
-              // Same deferred handling as VarDecl
-              cast<LambdaScopeInfo>(getCurFunction())
-                  ->ShadowingDecls.push_back({D, ShadowedDecl});
-              return;
-            }
-          }
-          // For non-init-capture cases, fall through to regular shadow logic
         }
         if (isa<FieldDecl>(ShadowedDecl)) {
           // If lambda can capture this, then emit default shadowing warning,
@@ -8525,25 +8511,32 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
           return;
         }
       }
-      // Apply scoping logic to both VarDecl and BindingDecl
-      bool shouldApplyScopingLogic = false;
-      if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
-        shouldApplyScopingLogic = VD->hasLocalStorage();
-      } else if (isa<BindingDecl>(ShadowedDecl)) {
-        shouldApplyScopingLogic = true;
-      }
-
-      if (shouldApplyScopingLogic) {
-        // A variable can't shadow a local variable or binding in an enclosing
-        // scope, if they are separated by a non-capturing declaration context.
-        for (DeclContext *ParentDC = NewDC;
-             ParentDC && !ParentDC->Equals(OldDC);
-             ParentDC = getLambdaAwareParentOfDeclContext(ParentDC)) {
-          // Only block literals, captured statements, and lambda expressions
-          // can capture; other scopes don't.
-          if (!isa<BlockDecl>(ParentDC) && !isa<CapturedDecl>(ParentDC) &&
-              !isLambdaCallOperator(ParentDC)) {
-            return;
+      // Apply scoping logic to both VarDecl and BindingDecl with local storage
+      if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
+        bool hasLocalStorage = false;
+        if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
+          hasLocalStorage = VD->hasLocalStorage();
+        } else {
+          // For BindingDecl, apply the same logic as
+          // VarDecl::hasLocalStorage(): local storage means not at file 
context
+          hasLocalStorage = !ShadowedDecl->getLexicalDeclContext()
+                                 ->getRedeclContext()
+                                 ->isFileContext();
+        }
+
+        if (hasLocalStorage) {
+          // A variable can't shadow a local variable or binding in an 
enclosing
+          // scope, if they are separated by a non-capturing declaration
+          // context.
+          for (DeclContext *ParentDC = NewDC;
+               ParentDC && !ParentDC->Equals(OldDC);
+               ParentDC = getLambdaAwareParentOfDeclContext(ParentDC)) {
+            // Only block literals, captured statements, and lambda expressions
+            // can capture; other scopes don't.
+            if (!isa<BlockDecl>(ParentDC) && !isa<CapturedDecl>(ParentDC) &&
+                !isLambdaCallOperator(ParentDC)) {
+              return;
+            }
           }
         }
       }
@@ -8590,8 +8583,10 @@ void Sema::DiagnoseShadowingLambdaDecls(const 
LambdaScopeInfo *LSI) {
     const NamedDecl *ShadowedDecl = Shadow.ShadowedDecl;
     // Try to avoid the warning when the shadowed decl isn't captured.
     const DeclContext *OldDC = ShadowedDecl->getDeclContext();
-    if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
+    if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
+      const auto *VD = cast<ValueDecl>(ShadowedDecl);
       SourceLocation CaptureLoc = getCaptureLocation(LSI, VD);
+
       Diag(Shadow.VD->getLocation(),
            CaptureLoc.isInvalid() ? diag::warn_decl_shadow_uncaptured_local
                                   : diag::warn_decl_shadow)
@@ -8601,12 +8596,6 @@ void Sema::DiagnoseShadowingLambdaDecls(const 
LambdaScopeInfo *LSI) {
         Diag(CaptureLoc, diag::note_var_explicitly_captured_here)
             << Shadow.VD->getDeclName() << /*explicitly*/ 0;
       Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
-    } else if (isa<BindingDecl>(ShadowedDecl)) {
-      // BindingDecls cannot be explicitly captured, so always uncaptured-local
-      Diag(Shadow.VD->getLocation(), diag::warn_decl_shadow_uncaptured_local)
-          << Shadow.VD->getDeclName()
-          << computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC;
-      Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
     } else if (isa<FieldDecl>(ShadowedDecl)) {
       Diag(Shadow.VD->getLocation(),
            LSI->isCXXThisCaptured() ? diag::warn_decl_shadow
diff --git a/clang/test/SemaCXX/PR68605.cpp b/clang/test/SemaCXX/PR68605.cpp
index b981f6c4eecce..12e8216d8eaa2 100644
--- a/clang/test/SemaCXX/PR68605.cpp
+++ b/clang/test/SemaCXX/PR68605.cpp
@@ -63,7 +63,7 @@ void foo5() {
   auto [a, b] = Pair{1, 2}; // expected-note {{previous declaration is here}} 
all-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}} all-note {{variable 'outer' is explicitly captured 
here}}
+  auto lambda = [outer, a](){ // expected-note {{variable 'outer' is 
explicitly captured here}} all-note {{variable 'outer' is explicitly captured 
here}} expected-note {{variable 'a' is explicitly captured here}} all-note 
{{variable 'a' is explicitly captured here}}
     int outer = 10; // expected-warning {{declaration shadows a local 
variable}} all-warning {{declaration shadows a local variable}}
     int a = 20;     // expected-warning {{declaration shadows a structured 
binding}} all-warning {{declaration shadows a structured binding}}
   };

>From 578e61b7aebbfa9213a28f3dabd29708112ce2a5 Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Wed, 10 Sep 2025 18:39:24 +0100
Subject: [PATCH 5/8] [Clang] Simplify BindingDecl scoping logic using
 getDecomposedDecl()->hasLocalStorage()

---
 clang/lib/Sema/SemaDecl.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 193db2726273a..256e57cea3b45 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8513,18 +8513,15 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
       }
       // Apply scoping logic to both VarDecl and BindingDecl with local storage
       if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
-        bool hasLocalStorage = false;
+        bool HasLocalStorage = false;
         if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
-          hasLocalStorage = VD->hasLocalStorage();
-        } else {
-          // For BindingDecl, apply the same logic as
-          // VarDecl::hasLocalStorage(): local storage means not at file 
context
-          hasLocalStorage = !ShadowedDecl->getLexicalDeclContext()
-                                 ->getRedeclContext()
-                                 ->isFileContext();
+          HasLocalStorage = VD->hasLocalStorage();
+        } else if (const auto *BD = dyn_cast<BindingDecl>(ShadowedDecl)) {
+          HasLocalStorage =
+              cast<VarDecl>(BD->getDecomposedDecl())->hasLocalStorage();
         }
 
-        if (hasLocalStorage) {
+        if (HasLocalStorage) {
           // A variable can't shadow a local variable or binding in an 
enclosing
           // scope, if they are separated by a non-capturing declaration
           // context.

>From 13a6db586a6187803dd3a066154c565ff05df3e6 Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Wed, 10 Sep 2025 21:55:52 +0100
Subject: [PATCH 6/8] [Clang] Add release note for consistent structured
 binding shadow warnings

---
 clang/docs/ReleaseNotes.rst | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a20b1ab298f9c..dbfdb79193377 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -308,6 +308,11 @@ Bug Fixes in This Version
 - Builtin elementwise operators now accept vector arguments that have different
   qualifiers on their elements. For example, vector of 4 ``const float`` values
   and vector of 4 ``float`` values. (#GH155405)
+- Fixed inconsistent shadow warnings for lambda capture of structured bindings.
+  Previously, ``[val = val]`` (regular parameter) produced no warnings with 
``-Wshadow``
+  while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 
2)``) 
+  incorrectly produced warnings. Both cases now consistently show no warnings 
with 
+  ``-Wshadow`` and show uncaptured-local warnings with ``-Wshadow-all``. 
(#GH68605)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>From e1c4dc206ee22006682d9e5a0f3f6da168b92f40 Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Thu, 11 Sep 2025 14:33:29 +0100
Subject: [PATCH 7/8] Apply suggestions from code review

Code style fixes applied

Co-authored-by: Mariya Podchishchaeva <mariya.podchishcha...@intel.com>
---
 clang/lib/Sema/SemaDecl.cpp | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 256e57cea3b45..441f7699ae49d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8483,7 +8483,6 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
         if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
           const auto *VD = cast<ValueDecl>(ShadowedDecl);
           const auto *LSI = cast<LambdaScopeInfo>(getCurFunction());
-
           if (RD->getLambdaCaptureDefault() == LCD_None) {
             // Try to avoid warnings for lambdas with an explicit capture
             // list. Warn only when the lambda captures the shadowed decl
@@ -8514,12 +8513,11 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
       // Apply scoping logic to both VarDecl and BindingDecl with local storage
       if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
         bool HasLocalStorage = false;
-        if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
+        if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl))
           HasLocalStorage = VD->hasLocalStorage();
-        } else if (const auto *BD = dyn_cast<BindingDecl>(ShadowedDecl)) {
+        else if (const auto *BD = dyn_cast<BindingDecl>(ShadowedDecl))
           HasLocalStorage =
               cast<VarDecl>(BD->getDecomposedDecl())->hasLocalStorage();
-        }
 
         if (HasLocalStorage) {
           // A variable can't shadow a local variable or binding in an 
enclosing
@@ -8531,9 +8529,8 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl 
*ShadowedDecl,
             // Only block literals, captured statements, and lambda expressions
             // can capture; other scopes don't.
             if (!isa<BlockDecl>(ParentDC) && !isa<CapturedDecl>(ParentDC) &&
-                !isLambdaCallOperator(ParentDC)) {
+                !isLambdaCallOperator(ParentDC))
               return;
-            }
           }
         }
       }
@@ -8583,7 +8580,6 @@ void Sema::DiagnoseShadowingLambdaDecls(const 
LambdaScopeInfo *LSI) {
     if (isa<VarDecl, BindingDecl>(ShadowedDecl)) {
       const auto *VD = cast<ValueDecl>(ShadowedDecl);
       SourceLocation CaptureLoc = getCaptureLocation(LSI, VD);
-
       Diag(Shadow.VD->getLocation(),
            CaptureLoc.isInvalid() ? diag::warn_decl_shadow_uncaptured_local
                                   : diag::warn_decl_shadow)

>From ef25bd24d475fa75c038a743ff64b4c36d7f7241 Mon Sep 17 00:00:00 2001
From: Ivan Murashko <ivan.muras...@gmail.com>
Date: Thu, 11 Sep 2025 15:58:03 +0100
Subject: [PATCH 8/8] [Clang] Add missing newline at end of PR68605.cpp test
 file

---
 clang/test/SemaCXX/PR68605.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/PR68605.cpp b/clang/test/SemaCXX/PR68605.cpp
index 12e8216d8eaa2..97eb858b77246 100644
--- a/clang/test/SemaCXX/PR68605.cpp
+++ b/clang/test/SemaCXX/PR68605.cpp
@@ -69,4 +69,4 @@ void foo5() {
   };
 }
 
-} // namespace issue_68605
\ No newline at end of file
+} // namespace issue_68605

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to