sepavloff created this revision. sepavloff added reviewers: rsmith, doug.gregor. sepavloff added a subscriber: cfe-commits.
If a declaration references a function from global namespace by its qualified name and if that function return type is a class or enum, there is possible ambiguity. The declaration: ``` friend A::B::C(); ``` may be considered as a declaration of a function `::C()` that returns `A::B`, or a function `::B::C()` that returns `A`. With this change when the compiler sees 'A::B' while parsing decl-spec, it tries to find `B` within 'A'. If it finds, 'A::B' is treated as a part of qualified name. If it doesn't and the current declaration declares a function, '::B' is assumed to be a part of declarator. For non-function declarations 'B' can be searched for in the global namespace to improve diagnostics. This changes fixes https://llvm.org/bugs/show_bug.cgi?id=28422. https://reviews.llvm.org/D23684 Files: include/clang/Parse/Parser.h include/clang/Sema/Sema.h lib/Parse/ParseDecl.cpp lib/Parse/ParseDeclCXX.cpp lib/Parse/ParseExprCXX.cpp lib/Parse/Parser.cpp lib/Parse/RAIIObjectsForParser.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/TreeTransform.h test/CXX/drs/dr1xx.cpp test/CXX/drs/dr2xx.cpp test/Parser/cxx-decl.cpp test/SemaCXX/nested-name-spec2.cpp test/SemaCXX/pr18284-crash-on-invalid.cpp test/SemaCXX/typo-correction.cpp
Index: test/SemaCXX/typo-correction.cpp =================================================================== --- test/SemaCXX/typo-correction.cpp +++ test/SemaCXX/typo-correction.cpp @@ -478,22 +478,17 @@ } } -namespace PR18213 { // expected-note {{'PR18213' declared here}} +namespace PR18213 { struct WrapperInfo { int i; }; template <typename T> struct Wrappable { static WrapperInfo kWrapperInfo; }; -// Note the space before "::PR18213" is intended and needed, as it highlights -// the actual typo, which is the leading "::". -// TODO: Suggest removing the "::" from "::PR18213" (the right correction) -// instead of incorrectly suggesting dropping "PR18213::WrapperInfo::". template <> -PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 }; // expected-error {{no member named 'PR18213' in 'PR18213::WrapperInfo'; did you mean simply 'PR18213'?}} \ - // expected-error {{C++ requires a type specifier for all declarations}} +PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 }; } namespace PR18651 { Index: test/SemaCXX/pr18284-crash-on-invalid.cpp =================================================================== --- test/SemaCXX/pr18284-crash-on-invalid.cpp +++ test/SemaCXX/pr18284-crash-on-invalid.cpp @@ -5,7 +5,7 @@ class A { }; class C { A a; }; -A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}} +A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}} void f() { new C; @@ -16,7 +16,7 @@ class A { }; class C : public A { }; -A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}} +A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}} void f() { new C; Index: test/SemaCXX/nested-name-spec2.cpp =================================================================== --- /dev/null +++ test/SemaCXX/nested-name-spec2.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +enum E1 { ABCD }; + +struct C1 { + struct C2 {}; + enum E2 { XYZ }; +}; + +E1 func_01(); +E1 ::func_01(); // expected-warning{{extra qualification on member}} + +C1 func_02(); +C1 ::func_02(); // expected-warning{{extra qualification on member}} + +C1::C2 func_03(); +C1::C2 ::func_03(); // expected-warning{{extra qualification on member}} + +C1::E2 func_04(); +C1::E2 ::func_04(); // expected-warning{{extra qualification on member}} + +class C3 { + friend E1 ::func_01(); + friend C1 ::func_02(); + friend C1::C2 ::func_03(); + friend C1::E2 ::func_04(); +}; + + +namespace N1 { + class C3 { + friend ::E1 ::func_01(); + friend ::C1 ::func_02(); + friend ::C1::C2 ::func_03(); + friend ::C1::E2 ::func_04(); + }; +} + + +namespace N2 { + enum E1 { ABCD }; + + struct C1 { + struct C2 {}; + enum E2 { XYZ }; + }; + + E1 func_01(); + C1 func_02(); + C1::C2 func_03(); + C1::E2 func_04(); +} + +namespace N3 { + class C4 { + friend ::N2::E1 ::N2::func_01(); + friend ::N2::C1 ::N2::func_02(); + friend ::N2::C1::C2 ::N2::func_03(); + friend ::N2::C1::E2 ::N2::func_04(); + }; +} Index: test/Parser/cxx-decl.cpp =================================================================== --- test/Parser/cxx-decl.cpp +++ test/Parser/cxx-decl.cpp @@ -249,8 +249,11 @@ FooBar(); // expected-error {{missing return type for function 'FooBar'; did you mean the constructor name 'Foobar'?}} ~FooBar(); // expected-error {{expected the class name after '~' to name a destructor}} }; - FooBar::FooBar() {} // expected-error {{undeclared}} expected-error {{missing return type}} - FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error {{expected the class name}} + FooBar::FooBar() {} // expected-error {{unknown type name 'FooBar'; did you mean 'Foobar'?}} + // expected-error@-1{{definition or redeclaration of 'FooBar' cannot name the global scope}} + // expected-error@-2{{use of undeclared identifier 'FooBar'; did you mean 'Foobar'?}} + // expected-note@-3{{'FooBar' declared here}} + FooBar::~FooBar() {} // expected-error {{'FooBar' is not a class, namespace, or enumeration}} } namespace DuplicateFriend { Index: test/CXX/drs/dr2xx.cpp =================================================================== --- test/CXX/drs/dr2xx.cpp +++ test/CXX/drs/dr2xx.cpp @@ -1038,7 +1038,7 @@ struct B b; // expected-error {{refers to a typedef}} struct C c; // expected-error {{refers to a typedef}} - B::B() {} // expected-error {{requires a type specifier}} + B::B() {} // expected-error{{definition or redeclaration of 'B' cannot name the global scope}} B::A() {} // ok C::~C() {} // expected-error {{destructor cannot be declared using a typedef 'C' (aka 'const dr298::A') of the class name}} Index: test/CXX/drs/dr1xx.cpp =================================================================== --- test/CXX/drs/dr1xx.cpp +++ test/CXX/drs/dr1xx.cpp @@ -226,18 +226,14 @@ // dr124: dup 201 // dr125: yes -struct dr125_A { struct dr125_B {}; }; // expected-note {{here}} +struct dr125_A { struct dr125_B {}; }; dr125_A::dr125_B dr125_C(); namespace dr125_B { dr125_A dr125_C(); } namespace dr125 { struct X { friend dr125_A::dr125_B (::dr125_C)(); // ok friend dr125_A (::dr125_B::dr125_C)(); // ok - friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}} - // expected-error@-1 {{missing exception specification}} -#if __cplusplus >= 201103L - // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}} -#endif + friend dr125_A::dr125_B ::dr125_C(); }; } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3442,7 +3442,7 @@ Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(), Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType); if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false, - SS, FirstQualifierInScope, false)) + SS, FirstQualifierInScope, false) != Sema::NNSResult::Success) return NestedNameSpecifierLoc(); } break; Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -473,13 +473,13 @@ /// scope if it *knows* that the result is correct. It should not return in a /// dependent context, for example. Nor will it extend \p SS with the scope /// specifier. -bool Sema::BuildCXXNestedNameSpecifier(Scope *S, - NestedNameSpecInfo &IdInfo, - bool EnteringContext, - CXXScopeSpec &SS, - NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup, - bool *IsCorrectedToColon) { +Sema::NNSResult Sema::BuildCXXNestedNameSpecifier(Scope *S, + NestedNameSpecInfo &IdInfo, + bool EnteringContext, + CXXScopeSpec &SS, + NamedDecl *ScopeLookupResult, + bool ErrorRecoveryLookup, + bool *IsCorrectedToColon) { LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, LookupNestedNameSpecifierName); QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType); @@ -513,7 +513,7 @@ // The declaration context must be complete. if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS, LookupCtx)) - return true; + return NNSResult::Failure; LookupQualifiedName(Found, LookupCtx); @@ -551,7 +551,7 @@ } if (Found.isAmbiguous()) - return true; + return NNSResult::Failure; // If we performed lookup into a dependent context and did not find anything, // that's fine: just build a dependent nested-name-specifier. @@ -561,14 +561,14 @@ !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) { // Don't speculate if we're just trying to improve error recovery. if (ErrorRecoveryLookup) - return true; + return NNSResult::Failure; // We were not able to compute the declaration context for a dependent // base object type or prior nested-name-specifier, so this // nested-name-specifier refers to an unknown specialization. Just build // a dependent nested-name-specifier. SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc); - return false; + return NNSResult::Success; } if (Found.empty() && !ErrorRecoveryLookup) { @@ -591,15 +591,15 @@ << FixItHint::CreateReplacement(IdInfo.CCLoc, ":"); if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) Diag(ND->getLocation(), diag::note_declared_at); - return true; + return NNSResult::Failure; } // Replacement '::' -> ':' is not allowed, just issue respective error. Diag(R.getNameLoc(), diag::err_expected_class_or_namespace) << IdInfo.Identifier << getLangOpts().CPlusPlus; if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) Diag(ND->getLocation(), diag::note_entity_declared_at) << IdInfo.Identifier; - return true; + return NNSResult::Failure; } } @@ -641,10 +641,50 @@ Found.isSingleResult() ? Found.getRepresentativeDecl() : nullptr; bool IsExtension = false; bool AcceptSpec = isAcceptableNestedNameSpecifier(SD, &IsExtension); - if (!AcceptSpec && IsExtension) { + if (!AcceptSpec && IsExtension) AcceptSpec = true; - Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum); + if (AcceptSpec && IdInfo.InDeclSpec && IdInfo.NextIdentifier && + !IdInfo.ObjectType) { + // Need to resolve possible ambiguity in construct 'T::A' - whether 'T::A' + // is a part of nested-name-spec or '::A' is a part of declarator. + + NamedDecl *FoundDecl = SD; + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) { + QualType QT = TD->getUnderlyingType(); + if (const TagType *TT = QT->getAs<TagType>()) + if (TagDecl *TD = TT->getDecl()) + FoundDecl = TD; + } + + if (TagDecl *TD = dyn_cast<TagDecl>(FoundDecl)) { + // In the construct 'T::A' tokens 'T::' are part of nested-name-spec if A + // is a member of 'T'. + if (TagDecl *Def = TD->getDefinition()) { + LookupResult Member(*this, IdInfo.NextIdentifier, + IdInfo.NextIdentifierLoc, LookupMemberName); + LookupQualifiedName(Member, Def); + LookupResult::LookupResultKind Res = Member.getResultKind(); + Member.clear(); + if (Res == LookupResult::NotFound) { + if (IdInfo.FunctionExpected) + return NNSResult::Stop; + // Variable must be declare in its namespace, this declaration can be + // found by lookup. + LookupResult Global(*this, IdInfo.NextIdentifier, + IdInfo.NextIdentifierLoc, LookupOrdinaryName); + LookupQualifiedName(Global, Context.getTranslationUnitDecl()); + LookupResult::LookupResultKind Res = Global.getResultKind(); + Global.clear(); + if (Res != LookupResult::NotFound) + return NNSResult::Stop; + // Otherwise consider 'A' as a typo of a nested-name-spec. + } + } + } } + if (IsExtension) + Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum); + if (AcceptSpec) { if (!ObjectType.isNull() && !ObjectTypeSearchedInScope && !getLangOpts().CPlusPlus11) { @@ -674,7 +714,7 @@ Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)), Context.getTypeDeclType(cast<TypeDecl>(SD))))) { if (ErrorRecoveryLookup) - return true; + return NNSResult::Failure; Diag(IdInfo.IdentifierLoc, diag::err_nested_name_member_ref_lookup_ambiguous) @@ -694,19 +734,19 @@ // If we're just performing this lookup for error-recovery purposes, // don't extend the nested-name-specifier. Just return now. if (ErrorRecoveryLookup) - return false; + return NNSResult::Success; // The use of a nested name specifier may trigger deprecation warnings. DiagnoseUseOfDecl(SD, IdInfo.CCLoc); if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) { SS.Extend(Context, Namespace, IdInfo.IdentifierLoc, IdInfo.CCLoc); - return false; + return NNSResult::Success; } if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) { SS.Extend(Context, Alias, IdInfo.IdentifierLoc, IdInfo.CCLoc); - return false; + return NNSResult::Success; } QualType T = @@ -750,13 +790,13 @@ SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T), IdInfo.CCLoc); - return false; + return NNSResult::Success; } // Otherwise, we have an error case. If we don't want diagnostics, just // return an error now. if (ErrorRecoveryLookup) - return true; + return NNSResult::Failure; // If we didn't find anything during our lookup, try again with // ordinary name lookup, which can help us produce better error @@ -793,7 +833,7 @@ << IdInfo.Identifier << ContainingClass; SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc); - return false; + return NNSResult::Success; } } } @@ -816,17 +856,17 @@ Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use) << IdInfo.Identifier; - return true; + return NNSResult::Failure; } -bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, - NestedNameSpecInfo &IdInfo, - bool EnteringContext, - CXXScopeSpec &SS, - bool ErrorRecoveryLookup, - bool *IsCorrectedToColon) { +Sema::NNSResult Sema::ActOnCXXNestedNameSpecifier(Scope *S, + NestedNameSpecInfo &IdInfo, + bool EnteringContext, + CXXScopeSpec &SS, + bool ErrorRecoveryLookup, + bool *IsCorrectedToColon) { if (SS.isInvalid()) - return true; + return NNSResult::Failure; return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, @@ -869,8 +909,8 @@ if (SS.isInvalid()) return false; - return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ScopeLookupResult=*/nullptr, true); + return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, + /*ScopeLookupResult=*/nullptr, true) == NNSResult::Success; } bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, Index: lib/Parse/RAIIObjectsForParser.h =================================================================== --- lib/Parse/RAIIObjectsForParser.h +++ lib/Parse/RAIIObjectsForParser.h @@ -88,7 +88,7 @@ /// variable's initializer, but not when parsing the body of a /// class or function definition. class ParsingDeclRAIIObject { - Sema &Actions; + Parser &TheParser; sema::DelayedDiagnosticPool DiagnosticPool; Sema::ParsingDeclState State; bool Popped; @@ -99,22 +99,22 @@ public: enum NoParent_t { NoParent }; ParsingDeclRAIIObject(Parser &P, NoParent_t _) - : Actions(P.getActions()), DiagnosticPool(nullptr) { + : TheParser(P), DiagnosticPool(nullptr) { push(); } /// Creates a RAII object whose pool is optionally parented by another. ParsingDeclRAIIObject(Parser &P, const sema::DelayedDiagnosticPool *parentPool) - : Actions(P.getActions()), DiagnosticPool(parentPool) { + : TheParser(P), DiagnosticPool(parentPool) { push(); } /// Creates a RAII object and, optionally, initialize its /// diagnostics pool by stealing the diagnostics from another /// RAII object (which is assumed to be the current top pool). ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other) - : Actions(P.getActions()), + : TheParser(P), DiagnosticPool(other ? other->DiagnosticPool.getParent() : nullptr) { if (other) { DiagnosticPool.steal(other->DiagnosticPool); @@ -127,6 +127,8 @@ abort(); } + Parser &getParser() const { return TheParser; } + sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() { return DiagnosticPool; } @@ -159,13 +161,13 @@ private: void push() { - State = Actions.PushParsingDeclaration(DiagnosticPool); + State = TheParser.getActions().PushParsingDeclaration(DiagnosticPool); Popped = false; } void pop(Decl *D) { if (!Popped) { - Actions.PopParsingDeclaration(State, D); + TheParser.getActions().PopParsingDeclaration(State, D); Popped = true; } } @@ -178,10 +180,17 @@ public: ParsingDeclSpec(Parser &P) : DeclSpec(P.getAttrFactory()), - ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {} + ParsingRAII(P, ParsingDeclRAIIObject::NoParent) { + P.InDeclSpec = true; + } ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII) : DeclSpec(P.getAttrFactory()), - ParsingRAII(P, RAII) {} + ParsingRAII(P, RAII) { + P.InDeclSpec = true; + } + ~ParsingDeclSpec() { + leave(); + } const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const { return ParsingRAII.getDelayedDiagnosticPool(); @@ -191,6 +200,10 @@ ParsingRAII.complete(D); } + void leave() { + ParsingRAII.getParser().InDeclSpec = false; + } + void abort() { ParsingRAII.abort(); } Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -69,8 +69,8 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) : PP(pp), Actions(actions), Diags(PP.getDiagnostics()), - GreaterThanIsOperator(true), ColonIsSacred(false), - InMessageExpression(false), TemplateParameterDepth(0), + GreaterThanIsOperator(true), ColonIsSacred(false), + InMessageExpression(false), InDeclSpec(false), TemplateParameterDepth(0), ParsingInObjCContainer(false) { SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies; Tok.startToken(); @@ -853,6 +853,34 @@ Tok.is(tok::kw_try); // X() try { ... } } +/// \brief Checks if the current token finishes decl-spec and next '::' starts a +/// function declarator. +/// +/// This function is used to assist in parsing constructs 'A::B' where 'A' and +/// 'B' are identifiers. So it expects that the current token is 'A'. +/// FIXME: we must support all kinds of nested-name-specifiers. +/// +bool Parser::isFunctionDeclaratorInGlobalNamespace() { + assert(Tok.is(tok::identifier)); + assert(NextToken().isOneOf(tok::coloncolon, tok::colon)); + unsigned Displ = 2; + while (true) { + const Token CurTok = GetLookAheadToken(Displ); + if (CurTok.is(tok::identifier)) { + const Token &NextTok = GetLookAheadToken(Displ + 1); + if (NextTok.isOneOf(tok::coloncolon, tok::colon)) { + Displ += 2; + continue; + } + if (NextTok.is(tok::l_paren)) + return true; + } + break; + } + return false; +} + + /// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or /// a declaration. We can't tell which we have until we read up to the /// compound-statement in function-definition. TemplateParams, if @@ -875,6 +903,7 @@ AccessSpecifier AS) { // Parse the common declaration-specifiers piece. ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level); + DS.leave(); // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -429,6 +429,33 @@ Token Next = NextToken(); Sema::NestedNameSpecInfo IdInfo(&II, Tok.getLocation(), Next.getLocation(), ObjectType); + if (Next.isOneOf(tok::coloncolon, tok::colon)) { + Token TokenAfterCC = GetLookAheadToken(2); + if (TokenAfterCC.is(tok::identifier)) { + // We have sequence of tokens: 'identifier' '::' 'identifier'. If decl- + // spec is parsed, these tokens may compose a qualified name or the + // decl-spec might finish before the double colon: + // + // friend A::B func(); + // friend A ::B(); + // + // Sema can distinguish between the two cases by looking 'B' in 'A'. + IdInfo.InDeclSpec = InDeclSpec; + IdInfo.NextIdentifier = TokenAfterCC.getIdentifierInfo(); + IdInfo.NextIdentifierLoc = TokenAfterCC.getLocation(); + + // If 'B' is not a member of 'A' in 'A::B', it may mean: + // - decl-spec is finished, '::B' starts a declarator, + // - 'B' is a typo. + // To distinguish between these cases Sema could search global scope for + // 'B', but it doesn't help if this declaration declares a function, as + // it may be the first declaration. If this declaration declares a + // variable, it must be preceded by a declaration in the global + // namespace and can be found by lookup. + IdInfo.FunctionExpected = InDeclSpec && + isFunctionDeclaratorInGlobalNamespace(); + } + } // If we get foo:bar, this is almost certainly a typo for foo::bar. Recover // and emit a fixit hint for it. @@ -458,18 +485,18 @@ } if (Next.is(tok::coloncolon)) { - if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) && + Token TokenAfterCC = GetLookAheadToken(2); + if (CheckForDestructor && TokenAfterCC.is(tok::tilde) && !Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) { *MayBePseudoDestructor = true; return false; } if (ColonIsSacred) { - const Token &Next2 = GetLookAheadToken(2); - if (Next2.is(tok::kw_private) || Next2.is(tok::kw_protected) || - Next2.is(tok::kw_public) || Next2.is(tok::kw_virtual)) { - Diag(Next2, diag::err_unexpected_token_in_nested_name_spec) - << Next2.getName() + if (TokenAfterCC.isOneOf(tok::kw_private, tok::kw_protected, + tok::kw_public, tok::kw_virtual)) { + Diag(TokenAfterCC, diag::err_unexpected_token_in_nested_name_spec) + << TokenAfterCC.getName() << FixItHint::CreateReplacement(Next.getLocation(), ":"); Token ColonColon; PP.Lex(ColonColon); @@ -484,33 +511,44 @@ // We have an identifier followed by a '::'. Lookup this name // as the name in a nested-name-specifier. - Token Identifier = Tok; - SourceLocation IdLoc = ConsumeToken(); - assert(Tok.isOneOf(tok::coloncolon, tok::colon) && - "NextToken() not working properly!"); - Token ColonColon = Tok; - SourceLocation CCLoc = ConsumeToken(); - CheckForLParenAfterColonColon(); + if (TokenAfterCC.is(tok::l_paren)) { + ConsumeToken(); // identifier + ConsumeToken(); // :: + CheckForLParenAfterColonColon(); + } else { + bool IsCorrectedToColon = false; + bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr; + Sema::NNSResult NSSRes = Actions.ActOnCXXNestedNameSpecifier( + getCurScope(), IdInfo, EnteringContext, SS, false, CorrectionFlagPtr); + if (NSSRes == Sema::NNSResult::Stop) { + // Current identifier followed by '::' is not a part of + // nested-name-spec. Stop parsing it. + return false; + } - bool IsCorrectedToColon = false; - bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr; - if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), IdInfo, - EnteringContext, SS, - false, CorrectionFlagPtr)) { - // Identifier is not recognized as a nested name, but we can have - // mistyped '::' instead of ':'. - if (CorrectionFlagPtr && IsCorrectedToColon) { - ColonColon.setKind(tok::colon); - PP.EnterToken(Tok); - PP.EnterToken(ColonColon); - Tok = Identifier; - break; + Token Identifier = Tok; + SourceLocation IdLoc = ConsumeToken(); + assert(Tok.isOneOf(tok::coloncolon, tok::colon) && + "NextToken() not working properly!"); + Token ColonColon = Tok; + SourceLocation CCLoc = ConsumeToken(); + + if (NSSRes == Sema::NNSResult::Failure) { + // Identifier is not recognized as a nested name, but we can have + // mistyped '::' instead of ':'. + if (CorrectionFlagPtr && IsCorrectedToColon) { + ColonColon.setKind(tok::colon); + PP.EnterToken(Tok); + PP.EnterToken(ColonColon); + Tok = Identifier; + break; + } + SS.SetInvalid(SourceRange(IdLoc, CCLoc)); } - SS.SetInvalid(SourceRange(IdLoc, CCLoc)); + HasScopeSpecifier = true; + continue; } - HasScopeSpecifier = true; - continue; } CheckForTemplateAndDigraph(Next, ObjectType, EnteringContext, II, SS); Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -517,6 +517,7 @@ // Parse nested-name-specifier. IdentifierInfo *LastII = nullptr; + InDeclSpec = false; ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false, /*MayBePseudoDtor=*/nullptr, /*IsTypename=*/false, @@ -2405,6 +2406,7 @@ ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class, &CommonLateParsedAttrs); + DS.leave(); // Turn off colon protection that was set for declspec. X.restore(); Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -1548,6 +1548,7 @@ DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext); + DS.leave(); // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4964,7 +4964,8 @@ bool *CanCorrect = nullptr); NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); - /// \brief Keeps information about an identifier in a nested-name-spec. + /// \brief Keeps information about identifier that is examined if it is a part + /// of a nested-name-spec. /// struct NestedNameSpecInfo { /// \brief The type of the object, if we're parsing nested-name-specifier in @@ -4980,30 +4981,56 @@ /// \brief The location of the '::'. SourceLocation CCLoc; - /// \brief Creates info object for the most typical case. + /// \brief If not null, the identifier following the '::'. + IdentifierInfo *NextIdentifier; + + /// \brief The location of the identifier following '::'. + SourceLocation NextIdentifierLoc; + + /// \brief True if the identifier was found during parsing of decl-spec. + bool InDeclSpec; + + /// \brief True if the declaration seems to be a function declaration. + bool FunctionExpected; + NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc, SourceLocation ColonColonLoc, ParsedType ObjectType = ParsedType()) : ObjectType(ObjectType), Identifier(II), IdentifierLoc(IdLoc), - CCLoc(ColonColonLoc) { + CCLoc(ColonColonLoc), NextIdentifier(nullptr), + InDeclSpec(false), FunctionExpected(false) { } NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc, SourceLocation ColonColonLoc, QualType ObjectType) : ObjectType(ParsedType::make(ObjectType)), Identifier(II), - IdentifierLoc(IdLoc), CCLoc(ColonColonLoc) { + IdentifierLoc(IdLoc), CCLoc(ColonColonLoc), NextIdentifier(nullptr), + InDeclSpec(false), FunctionExpected(false) { } }; bool isNonTypeNestedNameSpecifier(Scope *S, CXXScopeSpec &SS, NestedNameSpecInfo &IdInfo); - bool BuildCXXNestedNameSpecifier(Scope *S, - NestedNameSpecInfo &IdInfo, - bool EnteringContext, - CXXScopeSpec &SS, - NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup, - bool *IsCorrectedToColon = nullptr); + /// \brief Enumerates possible results that may be observed when adding an + /// identifier to nested-name-spec. + enum NNSResult { + /// \brief The identifier was successfully added to the nested-name-spec. + Success, + + /// \brief The provided identifier makes the nested-name-spec invalid. + Failure, + + // \brief The identifier is not a part of the nested-name-spec. + Stop + }; + + NNSResult BuildCXXNestedNameSpecifier(Scope *S, + NestedNameSpecInfo &IdInfo, + bool EnteringContext, + CXXScopeSpec &SS, + NamedDecl *ScopeLookupResult, + bool ErrorRecoveryLookup, + bool *IsCorrectedToColon = nullptr); /// \brief The parser has parsed a nested-name-specifier 'identifier::'. /// @@ -5028,12 +5055,12 @@ /// if the identifier is treated as if it was followed by ':', not '::'. /// /// \returns true if an error occurred, false otherwise. - bool ActOnCXXNestedNameSpecifier(Scope *S, - NestedNameSpecInfo &IdInfo, - bool EnteringContext, - CXXScopeSpec &SS, - bool ErrorRecoveryLookup = false, - bool *IsCorrectedToColon = nullptr); + NNSResult ActOnCXXNestedNameSpecifier(Scope *S, + NestedNameSpecInfo &IdInfo, + bool EnteringContext, + CXXScopeSpec &SS, + bool ErrorRecoveryLookup = false, + bool *IsCorrectedToColon = nullptr); ExprResult ActOnDecltypeExpression(Expr *E); Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -61,6 +61,7 @@ friend class ObjCDeclContextSwitch; friend class ParenBraceBracketBalancer; friend class BalancedDelimiterTracker; + friend class ParsingDeclSpec; Preprocessor &PP; @@ -198,6 +199,9 @@ /// should not be set directly. bool InMessageExpression; + /// \brief When true, parser processes decl-spec, or tries to recognize it. + bool InDeclSpec; + /// The "depth" of the template parameters currently being parsed. unsigned TemplateParameterDepth; @@ -1257,6 +1261,7 @@ ParsingDeclSpec *DS = nullptr); bool isDeclarationAfterDeclarator(); bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator); + bool isFunctionDeclaratorInGlobalNamespace(); DeclGroupPtrTy ParseDeclarationOrFunctionDefinition( ParsedAttributesWithRange &attrs, ParsingDeclSpec *DS = nullptr,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits