Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -339,6 +339,11 @@ "cannot template a using declaration">; def err_unexected_colon_in_nested_name_spec : Error< "unexpected ':' in nested name specifier; did you mean '::'?">; +def err_unexected_token_in_nested_name_spec : Error< + "unexpected '%0' 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 @@ -4363,6 +4363,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, @@ -4371,7 +4380,8 @@ bool EnteringContext, CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup); + ErrorRecoveryMode RecoveryMode, + NamedDecl **IdDecl = 0); /// \brief The parser has parsed a nested-name-specifier 'identifier::'. /// @@ -4394,6 +4404,13 @@ /// 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. + /// + /// \param IdDeclPtr If not null, it points to a pointer that is assigned + /// declaration of the last identifier before ::, if it is not a + /// namespace, class or scoped enum. + /// /// \returns true if an error occurred, false otherwise. bool ActOnCXXNestedNameSpecifier(Scope *S, IdentifierInfo &Identifier, @@ -4401,7 +4418,9 @@ SourceLocation CCLoc, ParsedType ObjectType, bool EnteringContext, - CXXScopeSpec &SS); + CXXScopeSpec &SS, + ErrorRecoveryMode RecoveryMode = ReportAll, + NamedDecl **IdDeclPtr = 0); ExprResult ActOnDecltypeExpression(Expr *E); Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -365,12 +365,12 @@ // Consume the template-id token. ConsumeToken(); - + assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!"); SourceLocation CCLoc = ConsumeToken(); HasScopeSpecifier = true; - + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); @@ -393,7 +393,6 @@ continue; } - // The rest of the nested-name-specifier possibilities start with // tok::identifier. if (Tok.isNot(tok::identifier)) @@ -434,23 +433,57 @@ 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_unexected_token_in_nested_name_spec) + << Next2.getName() + << FixItHint::CreateReplacement(Next.getLocation(), ":"); + Token ColonColon; + PP.Lex(ColonColon); + ColonColon.setKind(tok::colon); + PP.EnterToken(ColonColon); + break; + } + } + if (LastII) *LastII = &II; // 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; + NamedDecl *IdDecl = 0; if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), II, IdLoc, CCLoc, - ObjectType, EnteringContext, SS)) + ObjectType, EnteringContext, SS, + ErrMode, &IdDecl)) { + // Identifier is not recognized as a nested name, but we can have + // mistyped '::' instead of ':'. + if (ColonIsSacred && IdDecl) { + Diag(CCLoc, diag::err_nested_name_spec_is_not_class) + << &II << getLangOpts().CPlusPlus + << FixItHint::CreateReplacement(CCLoc, ":"); + Diag(IdDecl->getLocation(), diag::note_declared_at); + 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 @@ -639,8 +639,9 @@ ColonProtection.restore(); if (TryConsumeToken(tok::colon, ColonLoc)) { - } else if (TryConsumeToken(tok::semi, ColonLoc)) { - // Treat "case blah;" as a typo for "case blah:". + } else if (TryConsumeToken(tok::semi, ColonLoc) || + TryConsumeToken(tok::coloncolon, ColonLoc)) { + // Treat "case blah;" or "case blah::" as a typo for "case blah:". Diag(ColonLoc, diag::err_expected_after) << "'case'" << tok::colon << FixItHint::CreateReplacement(ColonLoc, ":"); 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,38 @@ /// 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 -/// 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. +/// \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. +/// \param IdDeclPtr If not null, it points to a pointer that is assigned +/// declaration of the last identifier before ::, if it is not a +/// namespace, class or scoped enum. +/// +/// 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 +417,8 @@ bool EnteringContext, CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, - bool ErrorRecoveryLookup) { + ErrorRecoveryMode RecoveryMode, + NamedDecl **IdDeclPtr) { LookupResult Found(*this, &Identifier, IdentifierLoc, LookupNestedNameSpecifierName); @@ -416,7 +439,6 @@ Found.setContextRange(SS.getRange()); } - bool ObjectTypeSearchedInScope = false; if (LookupCtx) { // Perform "qualified" name lookup into the declaration context we @@ -471,20 +493,45 @@ (!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 (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()) { + if (RecoveryMode == ReportAll) + Diag(ND->getLocation(), + diag::note_expected_class_or_namespace_declared_here) + << &Identifier; + if (IdDeclPtr) + *IdDeclPtr = ND; + } + 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 +590,7 @@ !Context.hasSameType( Context.getTypeDeclType(cast(OuterDecl)), Context.getTypeDeclType(cast(SD))))) { - if (ErrorRecoveryLookup) + if (RecoveryMode == DontReport) return true; Diag(IdentifierLoc, @@ -558,9 +605,9 @@ } } - // If we're just performing this lookup for error-recovery purposes, + // 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 +665,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 +728,17 @@ SourceLocation CCLoc, ParsedType ObjectType, bool EnteringContext, - CXXScopeSpec &SS) { + CXXScopeSpec &SS, + ErrorRecoveryMode ErrMode, + NamedDecl **IdDeclPtr) { if (SS.isInvalid()) return true; - + return BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, CCLoc, GetTypeFromParser(ObjectType), EnteringContext, SS, - /*ScopeLookupResult=*/0, false); + /*ScopeLookupResult=*/0, ErrMode, + IdDeclPtr); } bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS, @@ -732,9 +782,11 @@ return !BuildCXXNestedNameSpecifier(S, Identifier, IdentifierLoc, ColonLoc, GetTypeFromParser(ObjectType), EnteringContext, SS, - /*ScopeLookupResult=*/0, true); + /*ScopeLookupResult=*/0, + DontReport); } + bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -584,16 +584,15 @@ CorrectionCandidateCallback *CCC) { DeclarationNameInfo NameInfo(Name, NameLoc); ObjCMethodDecl *CurMethod = getCurMethodDecl(); - + if (NextToken.is(tok::coloncolon)) { BuildCXXNestedNameSpecifier(S, *Name, NameLoc, NextToken.getLocation(), - QualType(), false, SS, 0, false); - + QualType(), false, SS, 0, ReportAll); } - + LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); LookupParsedName(Result, S, &SS, !CurMethod); - + // Perform lookup for Objective-C instance variables (including automatically // synthesized instance variables), if we're in an Objective-C method. // FIXME: This lookup really, really needs to be folded in to the normal Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2919,7 +2919,8 @@ 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,70 @@ 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 {{declared here}} + } + struct C1 { + enum E1 { V2 = 2 }; // expected-note {{declared here}} + static const int V3 = 3; // expected-note {{declared here}} + }; + enum E2 { + V4 = 4, // expected-note {{declared here}} + V6 // expected-note {{declared here}} + }; + enum class EC3 { V0 = 0, V5 = 5 }; // expected-note {{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); + } + + struct S { + static int n; // expected-note{{declared here}} + int nn; // expected-note 2 {{declared here}} + }; + + int func_3(int x) { + return x ? S::n :: 0; // expected-error{{'n' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + } + int func_4(int x, S &s) { + return x ? s.nn :: x; // expected-error{{'nn' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + } + int func_5(int x, S &s) { + return x ? s.nn :: S::n; // expected-error{{'nn' cannot appear before '::' because it is not a class, namespace, or scoped enumeration; did you mean ':'?}} + } + + struct S2 { + struct S3; + }; + + struct S2 :: S3 :: public S2 { // expected-error{{unexpected 'public' in nested name specifier; did you mean ':'?}} + }; +} 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; }