Improve error recovery around colon.

Recognize additional cases, when '::' is mistyped as ':'.
This is a fix to RP18587 - colons have too much protection in member-declarations
Review is tracked by http://reviews.llvm.org/D3653.

This is an attempt to recommit the fix, initially committed as r212957 but then
reverted in r212965 as it broke self-build. In the updated patch ParseDirectDeclarator
turns on colon protection in for context as well.

llvm-svn: 213120
This commit is contained in:
Serge Pavlov 2014-07-16 05:16:52 +00:00
parent c68237bc2c
commit 458ea76041
5 changed files with 161 additions and 28 deletions

View File

@ -2715,24 +2715,23 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// typedef-name
case tok::kw_decltype:
case tok::identifier: {
// In C++, check to see if this is a scope specifier like foo::bar::, if
// so handle it as such. This is important for ctor parsing.
if (getLangOpts().CPlusPlus) {
if (TryAnnotateCXXScopeToken(EnteringContext)) {
if (!DS.hasTypeSpecifier())
DS.SetTypeSpecError();
goto DoneWithDeclSpec;
}
if (!Tok.is(tok::identifier))
continue;
}
// This identifier can only be a typedef name if we haven't already seen
// a type-specifier. Without this check we misparse:
// typedef int X; struct Y { short X; }; as 'short int'.
if (DS.hasTypeSpecifier())
goto DoneWithDeclSpec;
// In C++, check to see if this is a scope specifier like foo::bar::, if
// so handle it as such. This is important for ctor parsing.
if (getLangOpts().CPlusPlus) {
if (TryAnnotateCXXScopeToken(EnteringContext)) {
DS.SetTypeSpecError();
goto DoneWithDeclSpec;
}
if (!Tok.is(tok::identifier))
continue;
}
// Check for need to substitute AltiVec keyword tokens.
if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
break;
@ -4529,7 +4528,9 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
// Member pointers get special handling, since there's no place for the
// scope spec in the generic path below.
if (getLangOpts().CPlusPlus &&
(Tok.is(tok::coloncolon) || Tok.is(tok::identifier) ||
(Tok.is(tok::coloncolon) ||
(Tok.is(tok::identifier) &&
(NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) ||
Tok.is(tok::annot_cxxscope))) {
bool EnteringContext = D.getContext() == Declarator::FileContext ||
D.getContext() == Declarator::MemberContext;
@ -4722,6 +4723,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
if (getLangOpts().CPlusPlus && D.mayHaveIdentifier()) {
// Don't parse FOO:BAR as if it were a typo for FOO::BAR inside a class, in
// this context it is a bitfield. Also in range-based for statement colon
// may delimit for-range-declaration.
ColonProtectionRAIIObject X(*this,
D.getContext() == Declarator::MemberContext ||
(D.getContext() == Declarator::ForContext &&
getLangOpts().CPlusPlus11));
// ParseDeclaratorInternal might already have parsed the scope.
if (D.getCXXScopeSpec().isEmpty()) {
bool EnteringContext = D.getContext() == Declarator::FileContext ||

View File

@ -1239,7 +1239,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// Parse the (optional) nested-name-specifier.
CXXScopeSpec &SS = DS.getTypeSpecScope();
if (getLangOpts().CPlusPlus) {
// "FOO : BAR" is not a potential typo for "FOO::BAR".
// "FOO : BAR" is not a potential typo for "FOO::BAR". In this context it
// is a base-specifier-list.
ColonProtectionRAIIObject X(*this);
if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext))
@ -1926,14 +1927,8 @@ void Parser::ParseCXXMemberDeclaratorBeforeInitializer(
// declarator pure-specifier[opt]
// declarator brace-or-equal-initializer[opt]
// identifier[opt] ':' constant-expression
if (Tok.isNot(tok::colon)) {
// Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
// is a bitfield.
// FIXME: This should only apply when parsing the id-expression (see
// PR18587).
ColonProtectionRAIIObject X(*this);
if (Tok.isNot(tok::colon))
ParseDeclarator(DeclaratorInfo);
}
if (!DeclaratorInfo.isFunctionDeclarator() && TryConsumeToken(tok::colon)) {
BitfieldSize = ParseConstantExpression();
@ -2015,6 +2010,14 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
return;
}
// Turn on colon protection early, while parsing declspec, although there is
// nothing to protect there. It prevents from false errors if error recovery
// incorrectly determines where the declspec ends, as in the example:
// struct A { enum class B { C }; };
// const int C = 4;
// struct D { A::B : C; };
ColonProtectionRAIIObject X(*this);
// Access declarations.
bool MalformedTypeSpec = false;
if (!TemplateInfo.Kind &&
@ -2128,13 +2131,11 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
if (MalformedTypeSpec)
DS.SetTypeSpecError();
{
// Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
// is a bitfield.
ColonProtectionRAIIObject X(*this);
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
&CommonLateParsedAttrs);
}
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
&CommonLateParsedAttrs);
// Turn off colon protection that was set for declspec.
X.restore();
// If we had a free-standing type definition with a missing semicolon, we
// may get this far before the problem becomes obvious.

View File

@ -16,3 +16,15 @@ struct Y {
enum E : int(2);
enum E : Z(); // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'Z'}}
};
namespace pr18587 {
struct A {
enum class B {
C
};
};
const int C = 4;
struct D {
A::B : C;
};
}

View File

@ -227,3 +227,15 @@ namespace test7 {
for (e [[deprecated]] : arr) { e = 0; } // expected-warning {{deprecated}} expected-note {{here}} expected-warning {{extension}}
}
}
namespace pr18587 {
class Arg {};
struct Cont {
int *begin();
int *end();
};
void AddAllArgs(Cont &x) {
for (auto Arg: x) {
}
}
}

View File

@ -311,3 +311,102 @@ namespace N {
namespace TypedefNamespace { typedef int F; };
TypedefNamespace::F::NonexistentName BadNNSWithCXXScopeSpec; // expected-error {{'F' (aka 'int') is not a class, namespace, or scoped enumeration}}
namespace PR18587 {
struct C1 {
int a, b, c;
typedef int C2;
struct B1 {
struct B2 {
int a, b, c;
};
};
};
struct C2 { static const unsigned N1 = 1; };
struct B1 {
enum E1 { B2 = 2 };
static const int B3 = 3;
};
const int N1 = 2;
// Function declarators
struct S1a { int f(C1::C2); };
struct S1b { int f(C1:C2); }; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
struct S2a {
C1::C2 f(C1::C2);
};
struct S2c {
C1::C2 f(C1:C2); // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
struct S3a {
int f(C1::C2), C2 : N1;
int g : B1::B2;
};
struct S3b {
int g : B1:B2; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
// Inside square brackets
struct S4a {
int f[C2::N1];
};
struct S4b {
int f[C2:N1]; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
struct S5a {
int f(int xx[B1::B3 ? C2::N1 : B1::B2]);
};
struct S5b {
int f(int xx[B1::B3 ? C2::N1 : B1:B2]); // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
struct S5c {
int f(int xx[B1:B3 ? C2::N1 : B1::B2]); // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
// Bit fields
struct S6a {
C1::C2 m1 : B1::B2;
};
struct S6c {
C1::C2 m1 : B1:B2; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
struct S6d {
int C2:N1;
};
struct S6e {
static const int N = 3;
B1::E1 : N;
};
struct S6g {
C1::C2 : B1:B2; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
B1::E1 : B1:B2; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
// Template parameters
template <int N> struct T1 {
int a,b,c;
static const unsigned N1 = N;
typedef unsigned C1;
};
T1<C2::N1> var_1a;
T1<C2:N1> var_1b; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
template<int N> int F() {}
int (*X1)() = (B1::B2 ? F<1> : F<2>);
int (*X2)() = (B1:B2 ? F<1> : F<2>); // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
// Bit fields + templates
struct S7a {
T1<B1::B2>::C1 m1 : T1<B1::B2>::N1;
};
struct S7b {
T1<B1:B2>::C1 m1 : T1<B1::B2>::N1; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
struct S7c {
T1<B1::B2>::C1 m1 : T1<B1:B2>::N1; // expected-error{{unexpected ':' in nested name specifier; did you mean '::'?}}
};
}