diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -150,6 +150,9 @@ } using ns::$explicit^Y;)cpp", "^Y x;"); + testWalk(R"cpp( + namespace ns { class Foo {}; } + )cpp", "using ns::$explicit^Foo; class ^Foo foo;"); } TEST(WalkAST, Namespaces) { diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -506,8 +506,16 @@ assert(isTypeRep((TST) TypeSpecType) && "DeclSpec does not store a type"); return TypeRep; } + // Returns the underlying decl, if any. Decl *getRepAsDecl() const { - assert(isDeclRep((TST) TypeSpecType) && "DeclSpec does not store a decl"); + auto *D = getRepAsFoundDecl(); + if (const auto *Using = dyn_cast_or_null(D)) + return Using->getTargetDecl(); + return D; + } + // Returns the originally found decl, if any. + Decl *getRepAsFoundDecl() const { + assert(isDeclRep((TST)TypeSpecType) && "DeclSpec does not store a decl"); return DeclRep; } Expr *getRepAsExpr() const { 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 @@ -3324,7 +3324,9 @@ SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, bool IsTypeSpecifier, bool IsTemplateParamOrArg, - OffsetOfKind OOK, SkipBodyInfo *SkipBody = nullptr); + OffsetOfKind OOK, + UsingShadowDecl*& FoundUsingShadow, + SkipBodyInfo *SkipBody = nullptr); DeclResult ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4965,6 +4965,7 @@ bool IsDependent = false; const char *PrevSpec = nullptr; unsigned DiagID; + UsingShadowDecl* FoundUsing = nullptr; Decl *TagDecl = Actions.ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name, NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(), @@ -4973,7 +4974,7 @@ BaseType, DSC == DeclSpecContext::DSC_type_specifier, DSC == DeclSpecContext::DSC_template_param || DSC == DeclSpecContext::DSC_template_type_arg, - OffsetOfState, &SkipBody).get(); + OffsetOfState, FoundUsing, &SkipBody).get(); if (SkipBody.ShouldSkip) { assert(TUK == Sema::TUK_Definition && "can only skip a definition"); @@ -4983,8 +4984,8 @@ T.skipToEnd(); if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, - NameLoc.isValid() ? NameLoc : StartLoc, - PrevSpec, DiagID, TagDecl, Owned, + NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, + DiagID, FoundUsing ? FoundUsing : TagDecl, Owned, Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; return; @@ -5038,8 +5039,8 @@ } if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, - NameLoc.isValid() ? NameLoc : StartLoc, - PrevSpec, DiagID, TagDecl, Owned, + NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, + DiagID, FoundUsing ? FoundUsing : TagDecl, Owned, Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1934,6 +1934,7 @@ // Create the tag portion of the class or class template. DeclResult TagOrTempResult = true; // invalid TypeResult TypeResult = true; // invalid + UsingShadowDecl *FoundUsing = nullptr; bool Owned = false; Sema::SkipBodyInfo SkipBody; @@ -2074,7 +2075,7 @@ DSC == DeclSpecContext::DSC_type_specifier, DSC == DeclSpecContext::DSC_template_param || DSC == DeclSpecContext::DSC_template_type_arg, - OffsetOfState, &SkipBody); + OffsetOfState, FoundUsing, &SkipBody); // If ActOnTag said the type was dependent, try again with the // less common call. @@ -2133,7 +2134,7 @@ } else if (!TagOrTempResult.isInvalid()) { Result = DS.SetTypeSpecType( TagType, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, - DiagID, TagOrTempResult.get(), Owned, Policy); + DiagID, FoundUsing ? FoundUsing : TagOrTempResult.get(), Owned, Policy); } else { DS.SetTypeSpecError(); return; 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 @@ -16592,7 +16592,8 @@ bool &IsDependent, SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, bool IsTypeSpecifier, bool IsTemplateParamOrArg, - OffsetOfKind OOK, SkipBodyInfo *SkipBody) { + OffsetOfKind OOK, UsingShadowDecl *&FoundUsingShadow, + SkipBodyInfo *SkipBody) { // If this is not a definition, it must have a name. IdentifierInfo *OrigName = Name; assert((Name != nullptr || TUK == TUK_Definition) && @@ -17027,6 +17028,7 @@ // redefinition if either context is within the other. if (auto *Shadow = dyn_cast(DirectPrevDecl)) { auto *OldTag = dyn_cast(PrevDecl); + FoundUsingShadow = Shadow; if (SS.isEmpty() && TUK != TUK_Reference && TUK != TUK_Friend && isDeclInScope(Shadow, SearchDC, S, isMemberSpecialization) && !(OldTag && isAcceptableTagRedeclContext( 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 @@ -16972,6 +16972,7 @@ if (SS.isEmpty()) { bool Owned = false; bool IsDependent = false; + UsingShadowDecl* FoundUsing = nullptr; return ActOnTag(S, TagSpec, TUK_Friend, TagLoc, SS, Name, NameLoc, Attr, AS_public, /*ModulePrivateLoc=*/SourceLocation(), @@ -16980,7 +16981,7 @@ /*ScopedEnumUsesClassTag=*/false, /*UnderlyingType=*/TypeResult(), /*IsTypeSpecifier=*/false, - /*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside); + /*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside, FoundUsing); } NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10181,11 +10181,14 @@ bool Owned = false; bool IsDependent = false; - Decl *TagD = ActOnTag(S, TagSpec, Sema::TUK_Reference, KWLoc, SS, Name, - NameLoc, Attr, AS_none, /*ModulePrivateLoc=*/SourceLocation(), + UsingShadowDecl* FoundUsing = nullptr; + Decl *TagD = + ActOnTag(S, TagSpec, Sema::TUK_Reference, KWLoc, SS, Name, NameLoc, Attr, + AS_none, /*ModulePrivateLoc=*/SourceLocation(), MultiTemplateParamsArg(), Owned, IsDependent, SourceLocation(), false, TypeResult(), /*IsTypeSpecifier*/ false, - /*IsTemplateParamOrArg*/ false, /*OOK=*/OOK_Outside).get(); + /*IsTemplateParamOrArg*/ false, /*OOK=*/OOK_Outside, FoundUsing) + .get(); assert(!IsDependent && "explicit instantiation of dependent name not yet handled"); if (!TagD) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1588,6 +1588,9 @@ // TypeQuals handled by caller. Result = Context.getTypeDeclType(D); + if (const auto *Using = + dyn_cast_or_null(DS.getRepAsFoundDecl())) + Result = Context.getUsingType(Using, Result); // In both C and C++, make an ElaboratedType. ElaboratedTypeKeyword Keyword diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -992,7 +992,7 @@ } struct B::V {}; // expected-error {{no struct named 'V'}} struct B::W {}; - struct B::X {}; // FIXME: ill-formed + struct B::X {}; // expected-error {{forward declaration of struct cannot have}} enum B::Y e; // ok per dr417 class B::Z z; // ok per dr417 @@ -1009,7 +1009,7 @@ }; struct D::V {}; // expected-error {{no struct named 'V'}} struct D::W {}; - struct D::X {}; // FIXME: ill-formed + struct D::X {}; // expected-error {{forward declaration of struct cannot have}} enum D::Y e2; // ok per dr417 class D::Z z2; // ok per dr417 } diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/clang/test/CXX/drs/dr4xx.cpp @@ -301,9 +301,8 @@ struct F; struct H; } - // FIXME: This is ill-formed. using N::D; - struct dr417::D {}; // expected-warning {{extra qualification}} + struct dr417::D {}; // expected-error {{forward declaration of struct cannot}} expected-warning {{extra qualification}} using namespace N; struct dr417::E {}; // expected-warning {{extra qualification}} expected-error {{no struct named 'E'}} struct N::F {};