Skip to content

Commit c90114c

Browse files
authored
[clang] Avoid -Wshadow warning when init-capture named same as class field (#74512)
Shadowing warning doesn't make much sense since field is not available in lambda's body without capturing this. Fixes #71976
1 parent 8a2a65f commit c90114c

File tree

4 files changed

+141
-31
lines changed

4 files changed

+141
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ Bug Fixes in This Version
165165
a member class template for an implicit instantiation of a class template.
166166

167167
- Fixed missing warnings when doing bool-like conversions in C23 (`#79435 <https://github.com/llvm/llvm-project/issues/79435>`_).
168+
- Clang's ``-Wshadow`` no longer warns when an init-capture is named the same as
169+
a class field unless the lambda can capture this.
170+
Fixes (`#71976 <https://github.com/llvm/llvm-project/issues/71976>`_)
168171

169172
- Clang now accepts qualified partial/explicit specializations of variable templates that
170173
are not nominable in the lookup context of the specialization.

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -942,8 +942,8 @@ class LambdaScopeInfo final :
942942
/// that were defined in parent contexts. Used to avoid warnings when the
943943
/// shadowed variables are uncaptured by this lambda.
944944
struct ShadowedOuterDecl {
945-
const VarDecl *VD;
946-
const VarDecl *ShadowedDecl;
945+
const NamedDecl *VD;
946+
const NamedDecl *ShadowedDecl;
947947
};
948948
llvm::SmallVector<ShadowedOuterDecl, 4> ShadowingDecls;
949949

clang/lib/Sema/SemaDecl.cpp

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8357,28 +8357,40 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
83578357

83588358
unsigned WarningDiag = diag::warn_decl_shadow;
83598359
SourceLocation CaptureLoc;
8360-
if (isa<VarDecl>(D) && isa<VarDecl>(ShadowedDecl) && NewDC &&
8361-
isa<CXXMethodDecl>(NewDC)) {
8360+
if (isa<VarDecl>(D) && NewDC && isa<CXXMethodDecl>(NewDC)) {
83628361
if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
83638362
if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
8364-
if (RD->getLambdaCaptureDefault() == LCD_None) {
8365-
// Try to avoid warnings for lambdas with an explicit capture list.
8363+
if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
83668364
const auto *LSI = cast<LambdaScopeInfo>(getCurFunction());
8367-
// Warn only when the lambda captures the shadowed decl explicitly.
8368-
CaptureLoc = getCaptureLocation(LSI, cast<VarDecl>(ShadowedDecl));
8369-
if (CaptureLoc.isInvalid())
8370-
WarningDiag = diag::warn_decl_shadow_uncaptured_local;
8371-
} else {
8372-
// Remember that this was shadowed so we can avoid the warning if the
8373-
// shadowed decl isn't captured and the warning settings allow it.
8365+
if (RD->getLambdaCaptureDefault() == LCD_None) {
8366+
// Try to avoid warnings for lambdas with an explicit capture
8367+
// list. Warn only when the lambda captures the shadowed decl
8368+
// explicitly.
8369+
CaptureLoc = getCaptureLocation(LSI, VD);
8370+
if (CaptureLoc.isInvalid())
8371+
WarningDiag = diag::warn_decl_shadow_uncaptured_local;
8372+
} else {
8373+
// Remember that this was shadowed so we can avoid the warning if
8374+
// the shadowed decl isn't captured and the warning settings allow
8375+
// it.
8376+
cast<LambdaScopeInfo>(getCurFunction())
8377+
->ShadowingDecls.push_back({D, VD});
8378+
return;
8379+
}
8380+
}
8381+
if (isa<FieldDecl>(ShadowedDecl)) {
8382+
// If lambda can capture this, then emit default shadowing warning,
8383+
// Otherwise it is not really a shadowing case since field is not
8384+
// available in lambda's body.
8385+
// At this point we don't know that lambda can capture this, so
8386+
// remember that this was shadowed and delay until we know.
83748387
cast<LambdaScopeInfo>(getCurFunction())
8375-
->ShadowingDecls.push_back(
8376-
{cast<VarDecl>(D), cast<VarDecl>(ShadowedDecl)});
8388+
->ShadowingDecls.push_back({D, ShadowedDecl});
83778389
return;
83788390
}
83798391
}
8380-
8381-
if (cast<VarDecl>(ShadowedDecl)->hasLocalStorage()) {
8392+
if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl);
8393+
VD && VD->hasLocalStorage()) {
83828394
// A variable can't shadow a local variable in an enclosing scope, if
83838395
// they are separated by a non-capturing declaration context.
83848396
for (DeclContext *ParentDC = NewDC;
@@ -8429,19 +8441,28 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
84298441
/// when these variables are captured by the lambda.
84308442
void Sema::DiagnoseShadowingLambdaDecls(const LambdaScopeInfo *LSI) {
84318443
for (const auto &Shadow : LSI->ShadowingDecls) {
8432-
const VarDecl *ShadowedDecl = Shadow.ShadowedDecl;
8444+
const NamedDecl *ShadowedDecl = Shadow.ShadowedDecl;
84338445
// Try to avoid the warning when the shadowed decl isn't captured.
8434-
SourceLocation CaptureLoc = getCaptureLocation(LSI, ShadowedDecl);
84358446
const DeclContext *OldDC = ShadowedDecl->getDeclContext();
8436-
Diag(Shadow.VD->getLocation(), CaptureLoc.isInvalid()
8437-
? diag::warn_decl_shadow_uncaptured_local
8438-
: diag::warn_decl_shadow)
8439-
<< Shadow.VD->getDeclName()
8440-
<< computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC;
8441-
if (!CaptureLoc.isInvalid())
8442-
Diag(CaptureLoc, diag::note_var_explicitly_captured_here)
8443-
<< Shadow.VD->getDeclName() << /*explicitly*/ 0;
8444-
Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
8447+
if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
8448+
SourceLocation CaptureLoc = getCaptureLocation(LSI, VD);
8449+
Diag(Shadow.VD->getLocation(),
8450+
CaptureLoc.isInvalid() ? diag::warn_decl_shadow_uncaptured_local
8451+
: diag::warn_decl_shadow)
8452+
<< Shadow.VD->getDeclName()
8453+
<< computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC;
8454+
if (CaptureLoc.isValid())
8455+
Diag(CaptureLoc, diag::note_var_explicitly_captured_here)
8456+
<< Shadow.VD->getDeclName() << /*explicitly*/ 0;
8457+
Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
8458+
} else if (isa<FieldDecl>(ShadowedDecl)) {
8459+
Diag(Shadow.VD->getLocation(),
8460+
LSI->isCXXThisCaptured() ? diag::warn_decl_shadow
8461+
: diag::warn_decl_shadow_uncaptured_local)
8462+
<< Shadow.VD->getDeclName()
8463+
<< computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC;
8464+
Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration);
8465+
}
84458466
}
84468467
}
84478468

clang/test/SemaCXX/warn-shadow-in-lambdas.cpp

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow -D AVOID %s
2-
// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow -Wshadow-uncaptured-local %s
3-
// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow-all %s
1+
// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx14 -fsyntax-only -Wshadow -D AVOID %s
2+
// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx14 -fsyntax-only -Wshadow -Wshadow-uncaptured-local %s
3+
// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx14 -fsyntax-only -Wshadow-all %s
44
// RUN: %clang_cc1 -std=c++17 -verify -fsyntax-only -Wshadow-all %s
55
// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only -Wshadow-all %s
66

@@ -179,3 +179,89 @@ void f() {
179179
#endif
180180
}
181181
}
182+
183+
namespace GH71976 {
184+
#ifdef AVOID
185+
struct A {
186+
int b = 5;
187+
int foo() {
188+
return [b = b]() { return b; }(); // no -Wshadow diagnostic, init-capture does not shadow b due to not capturing this
189+
}
190+
};
191+
192+
struct B {
193+
int a;
194+
void foo() {
195+
auto b = [a = this->a] {}; // no -Wshadow diagnostic, init-capture does not shadow a due to not capturing his
196+
}
197+
};
198+
199+
struct C {
200+
int b = 5;
201+
int foo() {
202+
return [a = b]() {
203+
return [=, b = a]() { // no -Wshadow diagnostic, init-capture does not shadow b due to outer lambda
204+
return b;
205+
}();
206+
}();
207+
}
208+
};
209+
210+
#else
211+
struct A {
212+
int b = 5; // expected-note {{previous}}
213+
int foo() {
214+
return [b = b]() { return b; }(); // expected-warning {{declaration shadows a field}}
215+
}
216+
};
217+
218+
struct B {
219+
int a; // expected-note {{previous}}
220+
void foo() {
221+
auto b = [a = this->a] {}; // expected-warning {{declaration shadows a field}}
222+
}
223+
};
224+
225+
struct C {
226+
int b = 5; // expected-note {{previous}}
227+
int foo() {
228+
return [a = b]() {
229+
return [=, b = a]() { // expected-warning {{declaration shadows a field}}
230+
return b;
231+
}();
232+
}();
233+
}
234+
};
235+
236+
struct D {
237+
int b = 5; // expected-note {{previous}}
238+
int foo() {
239+
return [b = b, this]() { return b; }(); // expected-warning {{declaration shadows a field}}
240+
}
241+
};
242+
243+
struct E {
244+
int b = 5;
245+
int foo() {
246+
return [a = b]() { // expected-note {{previous}}
247+
return [=, a = a]() { // expected-warning {{shadows a local}}
248+
return a;
249+
}();
250+
}();
251+
}
252+
};
253+
254+
#endif
255+
256+
struct S {
257+
int a ;
258+
};
259+
260+
int foo() {
261+
auto [a] = S{0}; // expected-note {{previous}} \
262+
// cxx14-warning {{decomposition declarations are a C++17 extension}}
263+
[a = a] () { // expected-warning {{declaration shadows a structured binding}}
264+
}();
265+
}
266+
267+
}

0 commit comments

Comments
 (0)