Don't use the implicit int rule for error recovery in C++. Instead, try to
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 15 May 2012 21:01:51 +0000 (21:01 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 15 May 2012 21:01:51 +0000 (21:01 +0000)
disambiguate whether the type name was forgotten or mistyped.

llvm-svn: 156854

clang/lib/Parse/ParseDecl.cpp
clang/test/CXX/class/class.mem/p14.cpp
clang/test/Parser/cxx-undeclared-identifier.cpp
clang/test/SemaCXX/typo-correction.cpp

index 7ca9e28..dcc96cb 100644 (file)
@@ -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;
index 72b232e..3f14099 100644 (file)
@@ -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 {
index f15deab..6ea2965 100644 (file)
@@ -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'}}
 
index b1e8d91..4d6d065 100644 (file)
@@ -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'}}
+  };
+}