sepavloff added you to the CC list for the revision "Improve error recovery
around colon.".
Hi rsmith,
Parse of nested name specifier is modified so that it properly recovers
if colon is mistyped as double colon in case statement.
This patch fixes PR15133.
http://llvm-reviews.chandlerc.com/D2870
Files:
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseStmt.cpp
lib/Parse/RAIIObjectsForParser.h
lib/Sema/SemaCXXScopeSpec.cpp
test/Parser/recovery.cpp
test/SemaCXX/nested-name-spec.cpp
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,18 +439,28 @@
// 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!");
SourceLocation CCLoc = ConsumeToken();
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 <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;
+ }
+ 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; }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits