]> granicus.if.org Git - clang/commitdiff
Implement the first set of changes for PR3963 and rdar://6759604,
authorChris Lattner <sabre@nondot.org>
Sun, 12 Apr 2009 20:42:31 +0000 (20:42 +0000)
committerChris Lattner <sabre@nondot.org>
Sun, 12 Apr 2009 20:42:31 +0000 (20:42 +0000)
which tries to do better error recovery when it is "obvious" that an
identifier is a mis-typed typename.  In this case, we try to parse
it as a typename instead of as the identifier in a declarator, which
gives us several options for better error recovery and immediately
makes diagnostics more useful.  For example, we now produce:

t.c:4:8: error: unknown type name 'foo_t'
static foo_t a = 4;
       ^

instead of:

t.c:4:14: error: invalid token after top level declarator
static foo_t a = 4;
             ^

Also, since we now parse "a" correctly, we make a decl for it,
preventing later uses of 'a' from emitting things like:

t.c:12:20: error: use of undeclared identifier 'a'
int bar() { return a + b; }
                   ^

I'd really appreciate any scrutiny possible on this, it
is a tricky area.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68911 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticParseKinds.td
lib/Parse/ParseDecl.cpp
test/Lexer/block_cmt_end.c
test/Parser/declarators.c
test/Parser/objc-foreach-syntax.m
test/SemaObjC/exception-go-boom.m

index c2e90e8da418f022c6e59ef7db3397cdd7d9e246..66a48cffb02d1f2cfbec1d3c60ed062215c26d8b 100644 (file)
@@ -143,6 +143,9 @@ def err_typename_invalid_functionspec : Error<
   "type name does not allow function specifier to be specified">;
 def err_invalid_decl_spec_combination : Error<
   "cannot combine with previous '%0' declaration specifier">;
+def err_unknown_typename : Error<
+  "unknown type name %0">;
+
 
 /// Objective-C parser diagnostics
 def err_objc_no_attributes_on_category : Error<
index 83c02490fe4fa6bc4e0023a0a085111cb6c495cd..76bdd2f4877cd30d93a2b5e3c6c7481bd5020b1e 100644 (file)
@@ -467,6 +467,29 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) {
   }
 }
 
+/// isValidAfterIdentifierInDeclaratorAfterDeclSpec - Return true if the
+/// specified token is valid after the identifier in a declarator which
+/// immediately follows the declspec.  For example, these things are valid:
+///
+///      int x   [             4];         // direct-declarator
+///      int x   (             int y);     // direct-declarator
+///  int(int x   )                         // direct-declarator
+///      int x   ;                         // simple-declaration
+///      int x   =             17;         // init-declarator-list
+///      int x   ,             y;          // init-declarator-list
+///      int x   __asm__       ("foo");    // init-declarator-list
+///
+/// This is not, because 'x' does not immediately follow the declspec (though
+/// ')' happens to be valid anyway).
+///    int (x)
+///
+static bool isValidAfterIdentifierInDeclarator(const Token &T) {
+  return T.is(tok::l_square) || T.is(tok::l_paren) || T.is(tok::r_paren) ||
+         T.is(tok::semi) || T.is(tok::comma) || T.is(tok::equal) ||
+         T.is(tok::kw_asm);
+    
+}
+
 /// ParseDeclarationSpecifiers
 ///       declaration-specifiers: [C99 6.7]
 ///         storage-class-specifier declaration-specifiers[opt]
@@ -489,7 +512,7 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) {
 ///
 void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
                                         TemplateParameterLists *TemplateParams,
-                                        AccessSpecifier AS){
+                                        AccessSpecifier AS) {
   DS.SetRangeStart(Tok.getLocation());
   while (1) {
     int isInvalid = false;
@@ -604,15 +627,59 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(), 
                                             Tok.getLocation(), CurScope);
 
-      if (TypeRep == 0)
+      // If this is not a typedef name, don't parse it as part of the declspec,
+      // it must be an implicit int or an error.
+      if (TypeRep == 0) {
+        // If we see an identifier that is not a type name, we normally would
+        // parse it as the identifer being declared.  However, when a typename
+        // is typo'd or the definition is not included, this will incorrectly
+        // parse the typename as the identifier name and fall over misparsing
+        // later parts of the diagnostic.
+        //
+        // As such, we try to do some look-ahead in cases where this would
+        // otherwise be an "implicit-int" case to see if this is invalid.  For
+        // example: "static foo_t x = 4;"  In this case, if we parsed foo_t as
+        // an identifier with implicit int, we'd get a parse error because the
+        // next token is obviously invalid for a type.  Parse these as a case
+        // with an invalid type specifier.
+        assert(!DS.hasTypeSpecifier() && "Type specifier checked above");
+        
+        // Since we know that this either implicit int (which is rare) or an
+        // error, we'd do lookahead to try to do better recovery.
+        if (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
+          // identifier in the declarator.
+          goto DoneWithDeclSpec;
+        }
+
+        // Otherwise, if we don't consume this token, we are going to emit an
+        // error anyway.  Since this is almost certainly an invalid type name,
+        // emit a diagnostic that says it, eat the token, and pretend we saw an
+        // 'int'.
+        Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo();
+        DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec);
+        DS.SetRangeEnd(Tok.getLocation());
+        ConsumeToken();
+        
+        // TODO: in C, we could redo the lookup in the tag namespace to catch
+        // things like "foo x" where the user meant "struct foo x" etc, this
+        // would be much nicer for both error recovery, diagnostics, and we
+        // could even emit a fixit hint.
+        
+        // TODO: Could inject an invalid typedef decl in an enclosing scope to
+        // avoid rippling error messages on subsequent uses of the same type,
+        // could be useful if #include was forgotten.
+        
+        // FIXME: Mark DeclSpec as invalid.
         goto DoneWithDeclSpec;
+      }
 
       // C++: If the identifier is actually the name of the class type
       // being defined and the next token is a '(', then this is a
       // constructor declaration. We're done with the decl-specifiers
       // and will treat this token as an identifier.
-      if (getLang().CPlusPlus && 
-          CurScope->isClassScope() &&
+      if (getLang().CPlusPlus && CurScope->isClassScope() &&
           Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) && 
           NextToken().getKind() == tok::l_paren)
         goto DoneWithDeclSpec;
index 65d948f7fd8201934a83d37207a0aa8dc3b8d184..d85cf81f214a184d81b677e03457f96d9bc7bbd4 100644 (file)
@@ -17,7 +17,7 @@ next comment ends with normal escaped newline:
 /* expected-warning {{escaped newline}} expected-warning {{backslash and newline}}  *\  
 /
 
-bar
+int bar
 
 /* xyz
 
index 8a533ee53d8ab42c50501bfdc78bbf5510c79b9a..09b43e50571f706537864be0a8f240d6faa83bb3 100644 (file)
@@ -12,7 +12,7 @@ char ((((*X))));
 
 void (*signal(int, void (*)(int)))(int);
 
-int a, ***C, * const D, b(int);
+int a, ***C, * const D, B(int);
 
 int *A;
 
@@ -36,3 +36,20 @@ int test4(x, x) int x; {} /* expected-error {{redefinition of parameter 'x'}} */
 // PR3031
 int (test5), ;  // expected-error {{expected identifier or '('}}
 
+
+
+// PR3963 & rdar://6759604 - test error recovery for mistyped "typenames".
+
+struct xyz { int y; };
+
+foo_t a = 4;   // expected-error {{unknown type name 'foo_t'}}
+xyz b;         // expected-error {{unknown type name 'xyz'}}
+
+foo_t *d;      // expected-error {{unknown type name 'foo_t'}}
+
+static f;      // expected-warning {{type specifier missing, defaults to 'int'}}
+static g = 4;  // expected-warning {{type specifier missing, defaults to 'int'}}
+static h        // expected-warning {{type specifier missing, defaults to 'int'}} 
+      __asm__("foo"); // expected-warning {{extension used}}
+
+int bar() { return a; }
index e6e3ccf12d1a5cde80bd94e3f94d6ac408d99e74..977dccc88b1d2368eaac18cf749e46d3a21ad10a 100644 (file)
@@ -1,10 +1,8 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
-ce MyList // expected-error {{invalid token after top level declarator}}
-@end
 
 
-@implementation MyList
+@implementation MyList // expected-warning {{cannot find interface declaration for 'MyList'}}
 - (unsigned int)countByEnumeratingWithState:  (struct __objcFastEnumerationState *)state objects:  (id *)items count:(unsigned int)stackcount
 {
      return 0;
@@ -14,10 +12,10 @@ ce MyList // expected-error {{invalid token after top level declarator}}
 
 int LOOP();
 
-@implementation MyList (BasicTest)  // expected-error {{cannot find interface declaration for 'MyList'}}
+@implementation MyList (BasicTest) 
 - (void)compilerTestAgainst {
-MyList * el;  // expected-error {{use of undeclared identifier 'MyList'}}
-     for (el in @"foo")    // expected-error {{use of undeclared identifier 'el'}}
+MyList * el; 
+     for (el in @"foo") 
          { LOOP(); }
 }
 @end
index 1d792c4c131a53a9a9180087e11bb61076c07dfc..774ae7cd63907a33e214984c24dbca3e71420dad 100644 (file)
@@ -4,7 +4,7 @@
 void f0(id x) {
   @try {
   } @catch (NSException *x) { // \
-         expected-warning{{type specifier missing, defaults to 'int'}} \
+         expected-error{{unknown type name 'NSException'}} \
          expected-error{{@catch parameter is not a pointer to an interface type}}
   }
 }