Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -169,7 +169,18 @@ /// ColonProtectionRAIIObject RAII object. bool ColonIsSacred; - /// \brief When true, we are directly inside an Objective-C messsage + /// \brief Enumerates parser states in which error recovery is modified. + /// + enum RecoveryContextType { + ctxGeneric, /// no special information + ctxCaseStmt /// case statement + }; + + /// \brief Keeps information about language construct being parsed, to assist + /// error recovery. + RecoveryContextType RecoveryContext; + + /// \brief When true, we are directly inside an Objective-C message /// send expression. /// /// This is managed by the \c InMessageExpressionRAIIObject class, and Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4410,6 +4410,11 @@ ParsedType ObjectType, bool EnteringContext); + bool IsValidIfFollowedByColon(Scope *S, CXXScopeSpec &SS, + IdentifierInfo &Identifier, + SourceLocation IdentifierLoc, + SourceLocation ColonLoc); + /// \brief The parser has parsed a nested-name-specifier /// 'template[opt] template-name < template-args >::'. /// Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -439,6 +439,7 @@ // We have an identifier followed by a '::'. Lookup this name // as the name in a nested-name-specifier. + TentativeParsingAction TPA(*this); SourceLocation IdLoc = ConsumeToken(); assert((Tok.is(tok::coloncolon) || Tok.is(tok::colon)) && "NextToken() not working properly!"); @@ -446,11 +447,20 @@ CheckForLParenAfterColonColon(); - HasScopeSpecifier = true; if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), II, IdLoc, CCLoc, - ObjectType, EnteringContext, SS)) + ObjectType, EnteringContext, SS)) { + // Identifier is not recognized as a nested name, but we can have + // mistyped '::' instead of ':'. + if (ColonIsSacred && !ObjectType && RecoveryContext == ctxCaseStmt && + Actions.IsValidIfFollowedByColon(getCurScope(), SS, II, IdLoc, CCLoc)) { + // Don't emit error, this must be made by ParseCaseStatement. + TPA.Revert(); + return false; + } SS.SetInvalid(SourceRange(IdLoc, CCLoc)); - + } + TPA.Commit(); + HasScopeSpecifier = true; continue; } Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -615,7 +615,7 @@ /// We don't want to treat 'case x : y' as a potential typo for 'case x::y'. /// Disable this form of error recovery while we're parsing the case /// expression. - ColonProtectionRAIIObject ColonProtection(*this); + ColonProtectionRAIIObject ColonProtection(*this, true, Parser::ctxCaseStmt); ExprResult LHS(MissingCase ? Expr : ParseConstantExpression()); MissingCase = false; @@ -644,6 +644,10 @@ Diag(ColonLoc, diag::err_expected_after) << "'case'" << tok::colon << FixItHint::CreateReplacement(ColonLoc, ":"); + } else if (TryConsumeToken(tok::coloncolon, ColonLoc)) { + Diag(ColonLoc, diag::err_expected_after) + << "'case'" << tok::colon + << FixItHint::CreateReplacement(ColonLoc, ":"); } else { SourceLocation ExpectedLoc = PP.getLocForEndOfToken(PrevTokLocation); Diag(ExpectedLoc, diag::err_expected_after) Index: lib/Parse/RAIIObjectsForParser.h =================================================================== --- lib/Parse/RAIIObjectsForParser.h +++ lib/Parse/RAIIObjectsForParser.h @@ -264,16 +264,20 @@ class ColonProtectionRAIIObject { Parser &P; bool OldVal; + Parser::RecoveryContextType OldCtx; public: - ColonProtectionRAIIObject(Parser &p, bool Value = true) - : P(p), OldVal(P.ColonIsSacred) { + ColonProtectionRAIIObject(Parser &p, bool Value = true, + Parser::RecoveryContextType RCtx = Parser::ctxGeneric) + : P(p), OldVal(P.ColonIsSacred), OldCtx(P.RecoveryContext) { P.ColonIsSacred = Value; + P.RecoveryContext = RCtx; } /// restore - This can be used to restore the state early, before the dtor /// is run. void restore() { P.ColonIsSacred = OldVal; + P.RecoveryContext = OldCtx; } ~ColonProtectionRAIIObject() { Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -481,9 +481,21 @@ SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc); return false; } - + // FIXME: Deal with ambiguities cleanly. + if (Found.empty() && !ErrorRecoveryLookup && SS.isSet() && LookupCtx) { + // If identifier is not found as class-name-or-namespace-name, but is found + // as other entity, don't look for typos. + LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName); + LookupQualifiedName(R, LookupCtx); + if (!R.empty()) { + Diag(R.getNameLoc(), diag::err_expected_class_or_namespace) + << &Identifier << getLangOpts().CPlusPlus; + return true; + } + } + if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) { // We haven't found anything, and we're not recovering from a // different kind of error, so look for typos. @@ -735,6 +747,42 @@ /*ScopeLookupResult=*/0, true); } +/// \brief This method is used for error recovery purposes to determine whether +/// the specified identifier, followed by '::', could be followed by ':'. +/// +/// \param S Scope in which the identifier is specified. +/// \param SS Optional nested name specifier preceding the identifier. +/// \param Identifier The identifier under question. +/// \param IdentifierLoc Location of the identifier. +/// \param ColonColonLoc Location of '::' following the identifier. +/// +/// This method is called when an identifier followed by '::' is not a class or +/// namespace name, and context in which it occurs allows identifier followed by +/// ':'. For example: +/// \code +/// struct ABC { +/// enum E1 { C1, C2 }; +/// } +/// ... +/// switch(a) { +/// case ABC::C1:: +/// \endcode +bool Sema::IsValidIfFollowedByColon(Scope *S, CXXScopeSpec &SS, + IdentifierInfo &Identifier, + SourceLocation IdentifierLoc, + SourceLocation ColonColonLoc) { + if (SS.isInvalid()) + return false; + DeclContext *LookupCtx = computeDeclContext(SS); + LookupResult R(*this, &Identifier, IdentifierLoc, LookupOrdinaryName); + if (LookupCtx) + LookupQualifiedName(R, LookupCtx); + else + LookupName(R, S); + return R.getResultKind() != LookupResult::NotFound; +} + + bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, Index: test/Parser/recovery.cpp =================================================================== --- test/Parser/recovery.cpp +++ test/Parser/recovery.cpp @@ -135,3 +135,35 @@ template struct TempID; template <> struct TempID : BadType, EnumID::Garbage; // expected-error{{use of undeclared identifier 'BadType'}} } + +namespace pr15133 { + namespace ns { + const int V1 = 1; + } + struct C1 { + enum E1 { V2 = 2 }; + static const int V3 = 3; + }; + enum E2 { V4 = 4 }; // expected-note {{'V4' declared here}} + enum class EC3 { V0 = 0, V5 = 5 }; + void func_1(int x) { + switch(x) { + case 0: break; + case ns::V1:: break; // expected-error{{'V1' is not a class, namespace, or scoped enumeration}} \ + // expected-error{{expected ':' after 'case'}} + case C1::V2:: break; // expected-error{{'V2' is not a class, namespace, or scoped enumeration}} \ + // expected-error{{expected ':' after 'case'}} + case C1::V3:: break; // expected-error{{'V3' is not a class, namespace, or scoped enumeration}} \ + // expected-error{{expected ':' after 'case'}} + case V4:: break; // expected-error{{'V4' is not a class, namespace, or scoped enumeration}} \ + // expected-error{{expected ':' after 'case'}} + } + } + void func_2(EC3 x) { + switch(x) { + case EC3::V0: break; + case EC3::V5:: break; // expected-error{{'V5' is not a class, namespace, or scoped enumeration}} \ + // expected-error{{expected ':' after 'case'}} + } + } +} Index: test/SemaCXX/nested-name-spec.cpp =================================================================== --- test/SemaCXX/nested-name-spec.cpp +++ test/SemaCXX/nested-name-spec.cpp @@ -13,8 +13,7 @@ } A:: ; // expected-error {{expected unqualified-id}} -// FIXME: there is a member 'ax'; it's just not a class. -::A::ax::undef ex3; // expected-error {{no member named 'ax'}} +::A::ax::undef ex3; // expected-error {{'ax' is not a class, namespace, or scoped enumeration}} A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} int A::C::Ag1() { return 0; }