diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 4d05e16491b0..1ee243155ab2 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4916,7 +4916,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { } if (D.getCXXScopeSpec().isValid()) { - if (Actions.ShouldEnterDeclaratorScope(getCurScope(), D.getCXXScopeSpec())) + if (Actions.ShouldEnterDeclaratorScope(getCurScope(), + D.getCXXScopeSpec())) // Change the declaration context for name lookup, until this function // is exited (and the declarator has been parsed). DeclScopeObj.EnterDeclaratorScope(); @@ -4968,6 +4969,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { AllowConstructorName = (D.getContext() == Declarator::MemberContext); SourceLocation TemplateKWLoc; + bool HadScope = D.getCXXScopeSpec().isValid(); if (ParseUnqualifiedId(D.getCXXScopeSpec(), /*EnteringContext=*/true, /*AllowDestructorName=*/true, @@ -4981,6 +4983,13 @@ void Parser::ParseDirectDeclarator(Declarator &D) { D.SetIdentifier(nullptr, Tok.getLocation()); D.setInvalidType(true); } else { + // ParseUnqualifiedId might have parsed a scope specifier during error + // recovery. If it did so, enter that scope. + if (!HadScope && D.getCXXScopeSpec().isValid() && + Actions.ShouldEnterDeclaratorScope(getCurScope(), + D.getCXXScopeSpec())) + DeclScopeObj.EnterDeclaratorScope(); + // Parsed the unqualified-id; update range information and move along. if (D.getSourceRange().getBegin().isInvalid()) D.SetRangeBegin(D.getName().getSourceRange().getBegin()); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 355503caa9be..751a90ae71d3 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2504,6 +2504,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, } // If the user wrote ~T::T, correct it to T::~T. + DeclaratorScopeObj DeclScopeObj(*this, SS); if (!TemplateSpecified && NextToken().is(tok::coloncolon)) { if (SS.isSet()) { AnnotateScopeToken(SS, /*NewAnnotation*/true); @@ -2520,6 +2521,10 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, Diag(TildeLoc, diag::err_destructor_tilde_scope) << FixItHint::CreateRemoval(TildeLoc) << FixItHint::CreateInsertion(Tok.getLocation(), "~"); + + // Temporarily enter the scope for the rest of this function. + if (Actions.ShouldEnterDeclaratorScope(getCurScope(), SS)) + DeclScopeObj.EnterDeclaratorScope(); } // Parse the class-name (or template-name in a simple-template-id). diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 422398ebeb1e..4de25c63b766 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -113,6 +113,9 @@ ParsedType Sema::getDestructorName(SourceLocation TildeLoc, bool isDependent = false; bool LookInScope = false; + if (SS.isInvalid()) + return ParsedType(); + // If we have an object type, it's because we are in a // pseudo-destructor-expression or a member access expression, and // we know what type we're looking for. diff --git a/clang/test/Parser/cxx-class.cpp b/clang/test/Parser/cxx-class.cpp index 0e9a3b9d54d3..9d68cd96b084 100644 --- a/clang/test/Parser/cxx-class.cpp +++ b/clang/test/Parser/cxx-class.cpp @@ -140,8 +140,8 @@ namespace CtorErrors { } namespace DtorErrors { - struct A { ~A(); } a; - ~A::A() {} // expected-error {{'~' in destructor name should be after nested name specifier}} expected-note {{previous}} + struct A { ~A(); int n; } a; + ~A::A() { n = 0; } // expected-error {{'~' in destructor name should be after nested name specifier}} expected-note {{previous}} A::~A() {} // expected-error {{redefinition}} struct B { ~B(); } *b; @@ -151,6 +151,12 @@ namespace DtorErrors { a.~A::A(); // expected-error {{'~' in destructor name should be after nested name specifier}} b->~DtorErrors::~B::B(); // expected-error {{'~' in destructor name should be after nested name specifier}} } + + struct C; // expected-note {{forward decl}} + ~C::C() {} // expected-error {{incomplete}} expected-error {{'~' in destructor name should be after nested name specifier}} + + struct D { struct X {}; ~D() throw(X); }; + ~D::D() throw(X) {} // expected-error {{'~' in destructor name should be after nested name specifier}} } namespace BadFriend { diff --git a/clang/test/SemaTemplate/explicit-specialization-member.cpp b/clang/test/SemaTemplate/explicit-specialization-member.cpp index e8dc4d219acb..f302836c7e4b 100644 --- a/clang/test/SemaTemplate/explicit-specialization-member.cpp +++ b/clang/test/SemaTemplate/explicit-specialization-member.cpp @@ -17,7 +17,7 @@ namespace PR6161 { { static locale::id id; // expected-error{{use of undeclared identifier}} }; - numpunct::~numpunct(); // expected-error{{expected the class name after '~' to name a destructor}} + numpunct::~numpunct(); } namespace PR12331 {