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; }