Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6233,8 +6233,8 @@ def note_lambda_to_block_conv : Note< "implicit capture of lambda object due to conversion to block pointer " "here">; - def note_var_explicitly_captured_here : Note<"variable %0 is explicitly " - "captured here">; + def note_var_explicitly_captured_here : Note<"variable %0 is" + "%select{| explicitly}1 captured here">; // C++14 lambda init-captures. def warn_cxx11_compat_init_capture : Warning< Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -735,7 +735,16 @@ /// to local variables that are usable as constant expressions and /// do not involve an odr-use (they may still need to be captured /// if the enclosing full-expression is instantiation dependent). - llvm::SmallSet NonODRUsedCapturingExprs; + llvm::SmallSet NonODRUsedCapturingExprs; + + /// Contains all of the variables defined in this lambda that shadow variables + /// that were defined in parent contexts. Used to avoid warnings when the + /// shadowed variables are uncaptured by this lambda. + struct ShadowedOuterDecl { + const VarDecl *VD; + const VarDecl *ShadowedDecl; + }; + llvm::SmallVector ShadowingDecls; SourceLocation PotentialThisCaptureLocation; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1716,6 +1716,8 @@ /// to a shadowing declaration. void CheckShadowingDeclModification(Expr *E, SourceLocation Loc); + void DiagnoseShadowingLambdaDecls(const sema::LambdaScopeInfo *LSI); + private: /// Map of current shadowing declarations to shadowed declarations. Warn if /// it looks like the user is trying to modify the shadowing declaration. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -6655,14 +6655,21 @@ SourceLocation CaptureLoc; if (isa(ShadowedDecl) && NewDC && isa(NewDC)) { if (const auto *RD = dyn_cast(NewDC->getParent())) { - // Try to avoid warnings for lambdas with an explicit capture list. - if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent()) && - RD->getLambdaCaptureDefault() == LCD_None) { - const auto *LSI = cast(getCurFunction()); - // Warn only when the lambda captures the shadowed decl explicitly. - CaptureLoc = getCaptureLocation(LSI, cast(ShadowedDecl)); - if (CaptureLoc.isInvalid()) - WarningDiag = diag::warn_decl_shadow_uncaptured_local; + if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) { + if (RD->getLambdaCaptureDefault() == LCD_None) { + // Try to avoid warnings for lambdas with an explicit capture list. + const auto *LSI = cast(getCurFunction()); + // Warn only when the lambda captures the shadowed decl explicitly. + CaptureLoc = getCaptureLocation(LSI, cast(ShadowedDecl)); + if (CaptureLoc.isInvalid()) + WarningDiag = diag::warn_decl_shadow_uncaptured_local; + } else { + // Remember that this was shadowed so we can avoid the warning if the + // shadowed decl isn't captured and the warning settings allow it. + cast(getCurFunction()) + ->ShadowingDecls.push_back({D, cast(ShadowedDecl)}); + return; + } } } } @@ -6690,10 +6697,31 @@ ShadowedDeclKind Kind = computeShadowedDeclKind(ShadowedDecl, OldDC); Diag(R.getNameLoc(), WarningDiag) << Name << Kind << OldDC; if (!CaptureLoc.isInvalid()) - Diag(CaptureLoc, diag::note_var_explicitly_captured_here) << Name; + Diag(CaptureLoc, diag::note_var_explicitly_captured_here) + << Name << /*explicitly*/ 1; Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration); } +/// Diagnose shadowing for variables shadowed in the lambda record \p LambdaRD +/// when these variables are captured by the lambda. +void Sema::DiagnoseShadowingLambdaDecls(const LambdaScopeInfo *LSI) { + for (const auto &Shadow : LSI->ShadowingDecls) { + const VarDecl *ShadowedDecl = Shadow.ShadowedDecl; + // Try to avoid the warning when the shadowed decl isn't captured. + SourceLocation CaptureLoc = getCaptureLocation(LSI, ShadowedDecl); + const DeclContext *OldDC = ShadowedDecl->getDeclContext(); + Diag(Shadow.VD->getLocation(), CaptureLoc.isInvalid() + ? diag::warn_decl_shadow_uncaptured_local + : diag::warn_decl_shadow) + << Shadow.VD->getDeclName() + << computeShadowedDeclKind(ShadowedDecl, OldDC) << OldDC; + if (!CaptureLoc.isInvalid()) + Diag(CaptureLoc, diag::note_var_explicitly_captured_here) + << Shadow.VD->getDeclName() << /*explicitly*/ 0; + Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration); + } +} + /// \brief Check -Wshadow without the advantage of a previous lookup. void Sema::CheckShadow(Scope *S, VarDecl *D) { if (Diags.isIgnored(diag::warn_decl_shadow, D->getLocation())) Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1620,6 +1620,9 @@ CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); } + // Emit delayed shadowing warnings now that the full capture list is known. + DiagnoseShadowingLambdaDecls(LSI); + if (!CurContext->isDependentContext()) { switch (ExprEvalContexts.back().Context) { // C++11 [expr.prim.lambda]p2: Index: test/SemaCXX/warn-shadow-in-lambdas.cpp =================================================================== --- test/SemaCXX/warn-shadow-in-lambdas.cpp +++ test/SemaCXX/warn-shadow-in-lambdas.cpp @@ -5,12 +5,48 @@ void foo(int param) { // expected-note 1+ {{previous declaration is here}} int var = 0; // expected-note 1+ {{previous declaration is here}} - // Warn for lambdas with a default capture specifier. + // Avoid warnings for variables that aren't implicitly captured. { +#ifdef AVOID + auto f1 = [=] { int var = 1; }; // no warning + auto f2 = [&] { int var = 2; }; // no warning + auto f3 = [=] (int param) { ; }; // no warning + auto f4 = [&] (int param) { ; }; // no warning +#else auto f1 = [=] { int var = 1; }; // expected-warning {{declaration shadows a local variable}} auto f2 = [&] { int var = 2; }; // expected-warning {{declaration shadows a local variable}} auto f3 = [=] (int param) { ; }; // expected-warning {{declaration shadows a local variable}} auto f4 = [&] (int param) { ; }; // expected-warning {{declaration shadows a local variable}} +#endif + } + + // Warn for variables that are implicitly captured. + { + auto f1 = [=] () { + { + int var = 1; // expected-warning {{declaration shadows a local variable}} + } + int x = var; // expected-note {{variable 'var' is captured here}} + }; + auto f2 = [&] +#ifdef AVOID + (int param) { +#else + (int param) { // expected-warning {{declaration shadows a local variable}} +#endif + int x = var; // expected-note {{variable 'var' is captured here}} + int var = param; // expected-warning {{declaration shadows a local variable}} + }; + } + + // Warn for variables that are explicitly captured when a lambda has a default + // capture specifier. + { + auto f1 = [=, &var] () { // expected-note {{variable 'var' is captured here}} + int x = param; // expected-note {{variable 'param' is captured here}} + int var = 0; // expected-warning {{declaration shadows a local variable}} + int param = 0; // expected-warning {{declaration shadows a local variable}} + }; } // Warn normally inside of lambdas. @@ -72,20 +108,32 @@ }; #ifdef AVOID auto f1 = [] { int var = 1; }; // no warning + auto f2 = [=] { int var = 1; }; // no warning #else auto f1 = [] { int var = 1; }; // expected-warning {{declaration shadows a local variable}} -#endif auto f2 = [=] { int var = 1; }; // expected-warning {{declaration shadows a local variable}} +#endif auto f3 = [var] // expected-note {{variable 'var' is explicitly captured here}} { int var = 1; }; // expected-warning {{declaration shadows a local variable}} + auto f4 = [&] { + int x = var; // expected-note {{variable 'var' is captured here}} + int var = 2; // expected-warning {{declaration shadows a local variable}} + }; + }; + auto l6 = [&] { + auto f1 = [param] { // expected-note {{variable 'param' is explicitly captured here}} + int param = 0; // expected-warning {{declaration shadows a local variable}} + }; }; // Generic lambda arguments should work. #ifdef AVOID auto g1 = [](auto param) { ; }; // no warning + auto g2 = [=](auto param) { ; }; // no warning #else auto g1 = [](auto param) { ; }; // expected-warning {{declaration shadows a local variable}} + auto g2 = [=](auto param) { ; }; // expected-warning {{declaration shadows a local variable}} #endif - auto g2 = [param] // expected-note {{variable 'param' is explicitly captured here}} + auto g3 = [param] // expected-note {{variable 'param' is explicitly captured here}} (auto param) { ; }; // expected-warning {{declaration shadows a local variable}} }