Don't use the implicit int rule for error recovery in C++. Instead, try to

disambiguate whether the type name was forgotten or mistyped.

llvm-svn: 156854
This commit is contained in:
Richard Smith 2012-05-15 21:01:51 +00:00
parent 9295df0569
commit a952ebbcee
4 changed files with 76 additions and 7 deletions

View File

@ -1638,12 +1638,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
assert(!DS.hasTypeSpecifier() && "Type specifier checked above"); assert(!DS.hasTypeSpecifier() && "Type specifier checked above");
// Since we know that this either implicit int (which is rare) or an // Since we know that this either implicit int (which is rare) or an
// error, do lookahead to try to do better recovery. This never applies within // error, do lookahead to try to do better recovery. This never applies
// a type specifier. // within a type specifier. Outside of C++, we allow this even if the
// FIXME: Don't bail out here in languages with no implicit int (like // language doesn't "officially" support implicit int -- we support
// C++ with no -fms-extensions). This is much more likely to be an undeclared // implicit int as an extension in C99 and C11. Allegedly, MS also
// type or typo than a use of implicit int. // supports implicit int in C++ mode.
if (DSC != DSC_type_specifier && DSC != DSC_trailing && if (DSC != DSC_type_specifier && DSC != DSC_trailing &&
(!getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt) &&
isValidAfterIdentifierInDeclarator(NextToken())) { isValidAfterIdentifierInDeclarator(NextToken())) {
// If this token is valid for implicit int, e.g. "static x = 4", then // If this token is valid for implicit int, e.g. "static x = 4", then
// we just avoid eating the identifier, so it will be parsed as the // we just avoid eating the identifier, so it will be parsed as the
@ -1651,6 +1652,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
return false; return false;
} }
if (getLangOpts().CPlusPlus &&
DS.getStorageClassSpec() == DeclSpec::SCS_auto) {
// Don't require a type specifier if we have the 'auto' storage class
// specifier in C++98 -- we'll promote it to a type specifier.
return false;
}
// Otherwise, if we don't consume this token, we are going to emit an // Otherwise, if we don't consume this token, we are going to emit an
// error anyway. Try to recover from various common problems. Check // error anyway. Try to recover from various common problems. Check
// to see if this was a reference to a tag name without a tag specified. // to see if this was a reference to a tag name without a tag specified.
@ -1699,6 +1707,47 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
} }
} }
if (DSC != DSC_type_specifier && DSC != DSC_trailing) {
// Look ahead to the next token to try to figure out what this declaration
// was supposed to be.
switch (NextToken().getKind()) {
case tok::comma:
case tok::equal:
case tok::kw_asm:
case tok::l_brace:
case tok::l_square:
case tok::semi:
// This looks like a variable declaration. The type is probably missing.
// We're done parsing decl-specifiers.
return false;
case tok::l_paren: {
// static x(4); // 'x' is not a type
// x(int n); // 'x' is not a type
// x (*p)[]; // 'x' is a type
//
// Since we're in an error case (or the rare 'implicit int in C++' MS
// extension), we can afford to perform a tentative parse to determine
// which case we're in.
TentativeParsingAction PA(*this);
ConsumeToken();
TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false);
PA.Revert();
if (TPR == TPResult::False())
return false;
// The identifier is followed by a parenthesized declarator.
// It's supposed to be a type.
break;
}
default:
// This is probably supposed to be a type. This includes cases like:
// int f(itn);
// struct S { unsinged : 4; };
break;
}
}
// This is almost certainly an invalid type name. Let the action emit a // This is almost certainly an invalid type name. Let the action emit a
// diagnostic and attempt to recover. // diagnostic and attempt to recover.
ParsedType T; ParsedType T;

View File

@ -9,8 +9,9 @@ struct X0 {
}; };
struct X1 { struct X1 {
int X1; int X1; // expected-note{{hidden by a non-type declaration of 'X1' here}}
X1(); // expected-error{{declarator requires an identifier}} X1(); // expected-error{{must use 'struct' tag to refer to type 'X1' in this scope}} \
// expected-error{{expected member name or ';' after declaration specifiers}}
}; };
struct X2 { struct X2 {

View File

@ -1,5 +1,17 @@
// RUN: %clang_cc1 -fsyntax-only -pedantic -verify %s // RUN: %clang_cc1 -fsyntax-only -pedantic -verify %s
namespace ImplicitInt {
static a(4); // expected-error {{requires a type specifier}}
b(int n); // expected-error {{requires a type specifier}}
c (*p)[]; // expected-error {{unknown type name 'c'}}
itn f(char *p, *q); // expected-error {{unknown type name 'itn'}} expected-error {{requires a type specifier}}
struct S {
void f();
};
S::f() {} // expected-error {{requires a type specifier}}
}
// PR7180 // PR7180
int f(a::b::c); // expected-error {{use of undeclared identifier 'a'}} int f(a::b::c); // expected-error {{use of undeclared identifier 'a'}}

View File

@ -190,3 +190,10 @@ namespace test1 {
}; };
test1::FooBar *b; // expected-error{{no type named 'FooBar' in namespace 'test1'; did you mean 'Foobar'?}} test1::FooBar *b; // expected-error{{no type named 'FooBar' in namespace 'test1'; did you mean 'Foobar'?}}
} }
namespace ImplicitInt {
void f(int, unsinged); // expected-error{{did you mean 'unsigned'}}
struct S {
unsinged : 4; // expected-error{{did you mean 'unsigned'}}
};
}