diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -71,7 +71,7 @@ Modified Compiler Flags ----------------------- -- ... +- -Wshadow now also checks for shadowed structured bindings Removed Compiler Flags ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -425,7 +425,8 @@ "static data member of %2|" "field of %2|" "typedef in %2|" - "type alias in %2}1">, + "type alias in %2|" + "structured binding}1">, InGroup, DefaultIgnore; def warn_decl_shadow_uncaptured_local : Warning, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2598,6 +2598,8 @@ NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, const LookupResult &R); NamedDecl *getShadowedDeclaration(const VarDecl *D, const LookupResult &R); + NamedDecl *getShadowedDeclaration(const BindingDecl *D, + const LookupResult &R); void CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl, const LookupResult &R); void CheckShadow(Scope *S, VarDecl *D); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7491,7 +7491,8 @@ SDK_StaticMember, SDK_Field, SDK_Typedef, - SDK_Using + SDK_Using, + SDK_StructuredBinding }; /// Determine what kind of declaration we're shadowing. @@ -7501,6 +7502,8 @@ return SDK_Using; else if (isa(ShadowedDecl)) return SDK_Typedef; + else if (isa(ShadowedDecl)) + return SDK_StructuredBinding; else if (isa(OldDC)) return isa(ShadowedDecl) ? SDK_Field : SDK_StaticMember; @@ -7540,9 +7543,8 @@ return nullptr; NamedDecl *ShadowedDecl = R.getFoundDecl(); - return isa(ShadowedDecl) || isa(ShadowedDecl) - ? ShadowedDecl - : nullptr; + return isa(ShadowedDecl) ? ShadowedDecl + : nullptr; } /// Return the declaration shadowed by the given typedef \p D, or null @@ -7560,6 +7562,18 @@ return isa(ShadowedDecl) ? ShadowedDecl : nullptr; } +/// Return the declaration shadowed by the given variable \p D, or null +/// if it doesn't shadow any declaration or shadowing warnings are disabled. +NamedDecl *Sema::getShadowedDeclaration(const BindingDecl *D, + const LookupResult &R) { + if (!shouldWarnIfShadowedDecl(Diags, R)) + return nullptr; + + NamedDecl *ShadowedDecl = R.getFoundDecl(); + return isa(ShadowedDecl) ? ShadowedDecl + : nullptr; +} + /// Diagnose variable or built-in function shadowing. Implements /// -Wshadow. /// diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -857,17 +857,25 @@ Previous.clear(); } + auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name); + + // Find the shadowed declaration before filtering for scope. + NamedDecl *ShadowedDecl = D.getCXXScopeSpec().isEmpty() + ? getShadowedDeclaration(BD, Previous) + : nullptr; + bool ConsiderLinkage = DC->isFunctionOrMethod() && DS.getStorageClassSpec() == DeclSpec::SCS_extern; FilterLookupForScope(Previous, DC, S, ConsiderLinkage, /*AllowInlineNamespace*/false); + if (!Previous.empty()) { auto *Old = Previous.getRepresentativeDecl(); Diag(B.NameLoc, diag::err_redefinition) << B.Name; Diag(Old->getLocation(), diag::note_previous_definition); + } else if (ShadowedDecl && !D.isRedeclaration()) { + CheckShadow(BD, ShadowedDecl, Previous); } - - auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name); PushOnScopeChains(BD, S, true); Bindings.push_back(BD); ParsingInitForAutoVars.insert(BD); diff --git a/clang/test/SemaCXX/warn-shadow.cpp b/clang/test/SemaCXX/warn-shadow.cpp --- a/clang/test/SemaCXX/warn-shadow.cpp +++ b/clang/test/SemaCXX/warn-shadow.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -Wshadow-all %s +// RUN: %clang_cc1 -verify -fsyntax-only -std=c++17 -Wshadow-all %s namespace { int i; // expected-note {{previous declaration is here}} @@ -265,3 +265,44 @@ PR24718_1 // Does not shadow a type. }; }; + +namespace structured_binding_tests { +int x; // expected-note {{previous declaration is here}} +int y; // expected-note {{previous declaration is here}} +struct S { + int a, b; +}; + +void test1() { + const auto [x, y] = S(); // expected-warning 2 {{declaration shadows a variable in namespace 'structured_binding_tests'}} +} + +void test2() { + int a; // expected-note {{previous declaration is here}} + bool b; // expected-note {{previous declaration is here}} + { + auto [a, b] = S(); // expected-warning 2 {{declaration shadows a local variable}} + } +} + +class A +{ + int m_a; // expected-note {{previous declaration is here}} + int m_b; // expected-note {{previous declaration is here}} + + void test3() { + auto [m_a, m_b] = S(); // expected-warning 2 {{declaration shadows a field of 'structured_binding_tests::A'}} + } +}; + +void test4() { + const auto [a, b] = S(); // expected-note 3 {{previous declaration is here}} + { + int a = 4; // expected-warning {{declaration shadows a structured binding}} + } + { + const auto [a, b] = S(); // expected-warning 2 {{declaration shadows a structured binding}} + } +} + +}; // namespace structured_binding_tests \ No newline at end of file