From 827adaff666e53ae2f2db994bcd62ebe1ff5b9ce Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 15 May 2012 21:01:51 +0000 Subject: [PATCH] Don't use the implicit int rule for error recovery in C++. Instead, try to disambiguate whether the type name was forgotten or mistyped. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156854 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Parse/ParseDecl.cpp | 59 +++++++++++++++++++++-- test/CXX/class/class.mem/p14.cpp | 5 +- test/Parser/cxx-undeclared-identifier.cpp | 12 +++++ test/SemaCXX/typo-correction.cpp | 7 +++ 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7ca9e280ca..dcc96cb861 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1638,12 +1638,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, assert(!DS.hasTypeSpecifier() && "Type specifier checked above"); // 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 - // a type specifier. - // FIXME: Don't bail out here in languages with no implicit int (like - // C++ with no -fms-extensions). This is much more likely to be an undeclared - // type or typo than a use of implicit int. + // error, do lookahead to try to do better recovery. This never applies + // within a type specifier. Outside of C++, we allow this even if the + // language doesn't "officially" support implicit int -- we support + // implicit int as an extension in C99 and C11. Allegedly, MS also + // supports implicit int in C++ mode. if (DSC != DSC_type_specifier && DSC != DSC_trailing && + (!getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt) && isValidAfterIdentifierInDeclarator(NextToken())) { // 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 @@ -1651,6 +1652,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, 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 // 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. @@ -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 // diagnostic and attempt to recover. ParsedType T; diff --git a/test/CXX/class/class.mem/p14.cpp b/test/CXX/class/class.mem/p14.cpp index 72b232e8f7..3f14099ef8 100644 --- a/test/CXX/class/class.mem/p14.cpp +++ b/test/CXX/class/class.mem/p14.cpp @@ -9,8 +9,9 @@ struct X0 { }; struct X1 { - int X1; - X1(); // expected-error{{declarator requires an identifier}} + int X1; // expected-note{{hidden by a non-type declaration of 'X1' here}} + 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 { diff --git a/test/Parser/cxx-undeclared-identifier.cpp b/test/Parser/cxx-undeclared-identifier.cpp index f15deabc6d..6ea2965913 100644 --- a/test/Parser/cxx-undeclared-identifier.cpp +++ b/test/Parser/cxx-undeclared-identifier.cpp @@ -1,5 +1,17 @@ // 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 int f(a::b::c); // expected-error {{use of undeclared identifier 'a'}} diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index b1e8d91d45..4d6d0654f2 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -190,3 +190,10 @@ namespace test1 { }; 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'}} + }; +} -- 2.40.0