Updated patch
Hi rsmith,
http://llvm-reviews.chandlerc.com/D2870
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D2870?vs=7321&id=7574#toc
Files:
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Sema/Sema.h
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseStmt.cpp
lib/Parse/ParseTemplate.cpp
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/TreeTransform.h
test/Parser/recovery.cpp
test/SemaCXX/nested-name-spec.cpp
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<KeywordCompat>;
+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,15 +4353,24 @@
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,
SourceLocation CCLoc,
QualType ObjectType,
bool EnteringContext,
CXXScopeSpec &SS,
NamedDecl *ScopeLookupResult,
- bool ErrorRecoveryLookup);
+ ErrorRecoveryMode RecoveryMode);
/// \brief The parser has parsed a nested-name-specifier 'identifier::'.
///
@@ -4384,14 +4393,18 @@
/// 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,
SourceLocation IdentifierLoc,
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,25 +377,44 @@
/// 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: <tt>ptr->nns::f</tt>.
+/// \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: <tt>case AClass::AVal:: break;</tt>.
bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
IdentifierInfo &Identifier,
SourceLocation IdentifierLoc,
SourceLocation CCLoc,
QualType ObjectType,
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<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
!cast<CXXRecordDecl>(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<NamedDecl>())
+ 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<TypeDecl>(OuterDecl)),
Context.getTypeDeclType(cast<TypeDecl>(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<NamespaceDecl>(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 <typename> struct TempID;
template <> struct TempID<BadType> : 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<class T> struct TS1 {
+ typedef int A;
+ };
+ template<class T> void func(int x) {
+ switch(x) {
+ case TS1<T>::A:: break; // expected-error{{expected unqualified-id}}
+ }
+ };
+ void mainf() {
+ func<int>(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; }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits