Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -62,6 +62,8 @@ "keyword '%0' will be made available as an identifier " "%select{here|for the remainder of the translation unit}1">, InGroup; +def note_declared_here : Note<"%0 declared here">; + def error_empty_enum : Error<"use of empty enum">; def err_invalid_sign_spec : Error<"'%0' cannot be signed or unsigned">; @@ -339,6 +341,9 @@ "cannot template a using declaration">; def err_unexected_colon_in_nested_name_spec : Error< "unexpected ':' in nested name specifier; did you mean '::'?">; +def err_nested_name_spec_is_not_class : Error< + "%0 cannot appear before '::' because it is not a class" + "%select{ or namespace|, namespace, or scoped enumeration}1; did you mean ':'?">; def err_bool_redeclaration : Error< "redeclaration of C++ built-in type 'bool'">; def ext_c11_static_assert : Extension< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4353,6 +4353,15 @@ IdentifierInfo &II, ParsedType ObjectType); + /// \brief Enumerates possible kinds of action taken if an error is found + /// while building nested name specifier. + enum ErrorRecoveryMode { + ReportAll, ///< Report all errors. + DontReport, ///< Called for error recovery, no diagnostics. + DontReportWrongMember ///< No error reported if id is found but is not a + /// namespace/class/enum. + }; + bool BuildCXXNestedNameSpecifier(Scope *S, IdentifierInfo &Identifier, SourceLocation IdentifierLoc, @@ -4361,7 +4370,7 @@ bool EnteringContext, CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup); + ErrorRecoveryMode RecoveryMode); /// \brief The parser has parsed a nested-name-specifier 'identifier::'. /// @@ -4384,6 +4393,9 @@ /// output parameter (containing the full nested-name-specifier, /// including this new type). /// + /// \param RecoveryMode Specifies what kind of error recovery the method is + /// called for. + /// /// \returns true if an error occurred, false otherwise. bool ActOnCXXNestedNameSpecifier(Scope *S, IdentifierInfo &Identifier, @@ -4391,7 +4403,8 @@ SourceLocation CCLoc, ParsedType ObjectType, bool EnteringContext, - CXXScopeSpec &SS); + CXXScopeSpec &SS, + ErrorRecoveryMode RecoveryMode = ReportAll); ExprResult ActOnDecltypeExpression(Expr *E); @@ -4406,6 +4419,12 @@ ParsedType ObjectType, bool EnteringContext); + bool IsValidIfFollowedByColon(Scope *S, CXXScopeSpec &SS, + IdentifierInfo &Identifier, + SourceLocation IdentifierLoc, + SourceLocation ColonLoc, + NamedDecl **IdDeclPtr); + /// \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,18 +439,40 @@ // 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.is(tok::coloncolon) || Tok.is(tok::colon)) && "NextToken() not working properly!"); + Token ColonColon = Tok; SourceLocation CCLoc = ConsumeToken(); CheckForLParenAfterColonColon(); - HasScopeSpecifier = true; + Sema::ErrorRecoveryMode ErrMode = + ColonIsSacred ? Sema::DontReportWrongMember : Sema::ReportAll; if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), II, IdLoc, CCLoc, - ObjectType, EnteringContext, SS)) + ObjectType, EnteringContext, SS, + ErrMode)) { + // Identifier is not recognized as a nested name, but we can have + // mistyped '::' instead of ':'. + NamedDecl *IdDecl = 0; + if (ColonIsSacred && !ObjectType && + Actions.IsValidIfFollowedByColon(getCurScope(), SS, II, IdLoc, + CCLoc, &IdDecl)) { + Diag(CCLoc, diag::err_nested_name_spec_is_not_class) + << &II << getLangOpts().CPlusPlus + << FixItHint::CreateReplacement(CCLoc, ":"); + if (IdDecl) + Diag(IdDecl->getLocation(), diag::note_declared_here) << IdDecl; + ColonColon.setKind(tok::colon); + PP.EnterToken(Tok); + PP.EnterToken(ColonColon); + Tok = Identifier; + break; + } SS.SetInvalid(SourceRange(IdLoc, CCLoc)); - + } + HasScopeSpecifier = true; continue; } Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -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/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -1161,6 +1161,7 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { // Template argument lists are constant-evaluation contexts. EnterExpressionEvaluationContext EvalContext(Actions,Sema::ConstantEvaluated); + ColonProtectionRAIIObject ColonProtection(*this, false); do { ParsedTemplateArgument Arg = ParseTemplateArgument(); Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -377,16 +377,35 @@ /// by ActOnCXXNestedNameSpecifier. /// /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in -/// that it contains an extra parameter \p ScopeLookupResult, which provides -/// the result of name lookup within the scope of the nested-name-specifier -/// that was computed at template definition time. +/// that it contains an extra parameter \p ScopeLookupResult. /// -/// If ErrorRecoveryLookup is true, then this call is used to improve error -/// recovery. This means that it should not emit diagnostics, it should +/// \param S Scope in which the nested-name-specifier occurs. +/// \param Identifier Identifier in the sequence "identifier" "::". +/// \param IdentifierLoc Location of the \p Identifier. +/// \param CCLoc Location of "::" following Identifier. +/// \param ObjectType Type of postfix expression if the nested-name-specifier +/// occurs in construct like: ptr->nns::f. +/// \param EnteringContext If true, enter the context specified by the +/// nested-name-specifier. +/// \param SS Optional nested name specifier preceding the identifier. +/// \param ScopeLookupResult Provides the result of name lookup within the +/// scope of the nested-name-specifier that was computed at template +/// definition time. +/// \param RecoveryMode Specifies if the method is called to improve error +/// recovery and what kind of recovery is performed. +/// +/// If \p RecoveryMode equals to \p DontReport, then this call is used to improve +/// error recovery. This means that it should not emit diagnostics, it should /// just return true on failure. It also means it should only return a valid /// 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. +/// If \p RecoveryMode is \p ReportAll, all errors are diagnosed. Value +/// \p DontReportWrongMember acts similarly except when identifier is not a +/// namespace/class/enum, but is found as other entity. In this case diagnostics +/// is not emitted, this should be done by caller. This mode is helpful for +/// error recovery in cases when ':' is can be mistyped as '::', for instance in +/// case statement: case AClass::AVal:: break;. bool Sema::BuildCXXNestedNameSpecifier(Scope *S, IdentifierInfo &Identifier, SourceLocation IdentifierLoc, @@ -395,7 +414,7 @@ bool EnteringContext, CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup) { + ErrorRecoveryMode RecoveryMode) { LookupResult Found(*this, &Identifier, IdentifierLoc, LookupNestedNameSpecifierName); @@ -416,7 +435,6 @@ Found.setContextRange(SS.getRange()); } - bool ObjectTypeSearchedInScope = false; if (LookupCtx) { // Perform "qualified" name lookup into the declaration context we @@ -471,20 +489,42 @@ (!cast(LookupCtx)->hasDefinition() || !cast(LookupCtx)->hasAnyDependentBases()))) { // Don't speculate if we're just trying to improve error recovery. - if (ErrorRecoveryLookup) + if (RecoveryMode == DontReport) return true; - + // 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, &Identifier, IdentifierLoc, CCLoc); return false; - } - + } + // FIXME: Deal with ambiguities cleanly. - if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) { + if (Found.empty() && RecoveryMode != DontReport) { + // If identifier is not found as class-name-or-namespace-name, but is found + // as other entity, don't look for typos if caller processes such errors. + LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName); + if (SS.isSet() && LookupCtx) + LookupQualifiedName(R, LookupCtx); + else if (S && !isDependent) + LookupName(R, S); + if (!R.empty()) { + if (RecoveryMode == ReportAll) { + Diag(R.getNameLoc(), diag::err_expected_class_or_namespace) + << &Identifier << getLangOpts().CPlusPlus; + if (NamedDecl *ND = R.getAsSingle()) + Diag(ND->getLocation(), + diag::note_expected_class_or_namespace_declared_here) + << &Identifier; + } + return true; + } + } + + if (Found.empty() && RecoveryMode != DontReport && + !getLangOpts().MSVCCompat) { // We haven't found anything, and we're not recovering from a // different kind of error, so look for typos. DeclarationName Name = Found.getLookupName(); @@ -543,7 +583,7 @@ !Context.hasSameType( Context.getTypeDeclType(cast(OuterDecl)), Context.getTypeDeclType(cast(SD))))) { - if (ErrorRecoveryLookup) + if (RecoveryMode == DontReport) return true; Diag(IdentifierLoc, @@ -560,7 +600,7 @@ // If we're just performing this lookup for error-recovery purposes, // don't extend the nested-name-specifier. Just return now. - if (ErrorRecoveryLookup) + if (RecoveryMode == DontReport) return false; if (NamespaceDecl *Namespace = dyn_cast(SD)) { @@ -618,7 +658,7 @@ // Otherwise, we have an error case. If we don't want diagnostics, just // return an error now. - if (ErrorRecoveryLookup) + if (RecoveryMode == DontReport) return true; // If we didn't find anything during our lookup, try again with @@ -681,14 +721,15 @@ SourceLocation CCLoc, ParsedType ObjectType, bool EnteringContext, - CXXScopeSpec &SS) { + CXXScopeSpec &SS, + ErrorRecoveryMode ErrMode) { if (SS.isInvalid()) return true; return BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, CCLoc, GetTypeFromParser(ObjectType), EnteringContext, SS, - /*ScopeLookupResult=*/0, false); + /*ScopeLookupResult=*/0, ErrMode); } bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS, @@ -732,9 +773,54 @@ return !BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, ColonLoc, GetTypeFromParser(ObjectType), EnteringContext, SS, - /*ScopeLookupResult=*/0, true); + /*ScopeLookupResult=*/0, + DontReport); +} + +/// \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. +/// \param IdDeclPtr Address of pointer that will be assigned the declaration +/// found for 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, + NamedDecl **IdDeclPtr) { + if (SS.isInvalid()) + return false; + DeclContext *LookupCtx = computeDeclContext(SS); + LookupResult R(*this, &Identifier, IdentifierLoc, LookupOrdinaryName); + if (LookupCtx) + LookupQualifiedName(R, LookupCtx); + else + LookupName(R, S); + if (R.getResultKind() != LookupResult::NotFound) { + if (IdDeclPtr) + *IdDeclPtr = R.getFoundDecl(); + return true; + } + return false; } + bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -588,7 +588,7 @@ if (NextToken.is(tok::coloncolon)) { BuildCXXNestedNameSpecifier(S, *Name, NameLoc, NextToken.getLocation(), - QualType(), false, SS, 0, false); + QualType(), false, SS, 0, ReportAll); } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2904,7 +2904,7 @@ Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType, false, SS, - FirstQualifierInScope, false)) + FirstQualifierInScope, Sema::ReportAll)) return NestedNameSpecifierLoc(); break; Index: test/Parser/recovery.cpp =================================================================== --- test/Parser/recovery.cpp +++ test/Parser/recovery.cpp @@ -135,3 +135,48 @@ template struct TempID; template <> struct TempID : BadType, EnumID::Garbage; // expected-error{{use of undeclared identifier 'BadType'}} } + +namespace pr15133 { + namespace ns { + const int V1 = 1; // expected-note {{'V1' declared here}} + } + struct C1 { + enum E1 { V2 = 2 }; // expected-note {{'V2' declared here}} + static const int V3 = 3; // expected-note {{'V3' declared here}} + }; + enum E2 { + V4 = 4, // expected-note {{'V4' declared here}} + V6 // expected-note {{'V6' declared here}} + }; + enum class EC3 { V0 = 0, V5 = 5 }; // expected-note {{'V5' declared here}} + void func_3(); + + void func_1(int x) { + switch(x) { + case 0: break; + case ns::V1:: break; // expected-error{{'V1' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + case C1::V2:: break; // expected-error{{'V2' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + case C1::V3:: break; // expected-error{{'V3' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + case V4:: break; // expected-error{{'V4' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + case V6:: func_3(); // expected-error{{'V6' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + } + } + void func_2(EC3 x) { + switch(x) { + case EC3::V0: break; + case EC3::V5:: break; // expected-error{{'V5' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + } + } + + template struct TS1 { + typedef int A; + }; + template void func(int x) { + switch(x) { + case TS1::A:: break; // expected-error{{expected unqualified-id}} + } + }; + void mainf() { + func(1); + } +} Index: test/SemaCXX/nested-name-spec.cpp =================================================================== --- test/SemaCXX/nested-name-spec.cpp +++ test/SemaCXX/nested-name-spec.cpp @@ -8,13 +8,12 @@ static int Ag1(); static int Ag2(); }; - int ax; + int ax; // expected-note {{'ax' declared here}} void Af(); } 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; }