]> granicus.if.org Git - clang/commitdiff
Implement PR6180, substantially improving the diagnostics we get from
authorChris Lattner <sabre@nondot.org>
Tue, 2 Feb 2010 01:23:29 +0000 (01:23 +0000)
committerChris Lattner <sabre@nondot.org>
Tue, 2 Feb 2010 01:23:29 +0000 (01:23 +0000)
forgetting a ';' at the end of a struct.  For something like:

class c {
}
void foo() {}

we now produce:

t.cc:3:2: error: expected ';' after class
}
 ^
 ;

instead of:

t.cc:4:1: error: cannot combine with previous 'class' declaration specifier
void foo() {}
^
t.cc:2:7: error: 'class c' can not be defined in the result type of a function
class c {
      ^

GCC produces:

t.cc:4: error: new types may not be defined in a return type
t.cc:4: note: (perhaps a semicolon is missing after the definition of ‘c’)
t.cc:4: error: two or more data types in declaration of ‘foo’

I *think* I got the follow set right, but if I forgot anything, we'll start
getting spurious "expected ';' after class" errors, let me know if you see
any.

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

include/clang/Basic/DiagnosticParseKinds.td
lib/Parse/ParseDeclCXX.cpp
test/Sema/declspec.c

index 6093282423d990de150522c66a9b9a82a8f68f3f..dc83e87e967f8e79f546a55bee3f09166dcfa188 100644 (file)
@@ -305,6 +305,9 @@ def err_out_of_line_type_names_constructor : Error<
 
 def err_expected_qualified_after_typename : Error<
   "expected a qualified name after 'typename'">;
+def err_expected_semi_after_tagdecl : Error<
+  "expected ';' after %0">;
+
 def err_typename_refers_to_non_type_template : Error<
   "typename specifier refers to a non-template">;
 def err_expected_type_name_after_typename : Error<
index e83743cd67cde1b1e9ac5feaf4ff53dac26df2e1..234ec4c8b59c23b8621c4c73e8f570ae31fdc063 100644 (file)
@@ -924,6 +924,42 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
   if (DS.SetTypeSpecType(TagType, TSTLoc, PrevSpec, DiagID,
                          Result, Owned))
     Diag(StartLoc, DiagID) << PrevSpec;
+  
+  // At this point, we've successfully parsed a class-specifier in 'definition'
+  // form (e.g. "struct foo { int x; }".  While we could just return here, we're
+  // going to look at what comes after it to improve error recovery.  If an
+  // impossible token occurs next, we assume that the programmer forgot a ; at
+  // the end of the declaration and recover that way.
+  //
+  // This switch enumerates the valid "follow" set for definition.
+  if (TUK == Action::TUK_Definition) {
+    switch (Tok.getKind()) {
+    case tok::semi:               // struct foo {...} ;
+    case tok::star:               // struct foo {...} *       P;
+    case tok::amp:                // struct foo {...} &       R = ...
+    case tok::identifier:         // struct foo {...} V       ;
+    case tok::r_paren:            //(struct foo {...} )       {4}
+    case tok::annot_cxxscope:     // struct foo {...} a::     b;
+    case tok::annot_typename:     // struct foo {...} a       ::b;
+    case tok::annot_template_id:  // struct foo {...} a<int>  ::b;
+      break;
+        
+    case tok::r_brace:  // struct bar { struct foo {...} } 
+      // Missing ';' at end of struct is accepted as an extension in C mode.
+      if (!getLang().CPlusPlus) break;
+      // FALL THROUGH.
+    default:
+      ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl,
+                       TagType == DeclSpec::TST_class ? "class"
+                       : TagType == DeclSpec::TST_struct? "struct" : "union");
+      // Push this token back into the preprocessor and change our current token
+      // to ';' so that the rest of the code recovers as though there were an
+      // ';' after the definition.
+      PP.EnterToken(Tok);
+      Tok.setKind(tok::semi);  
+      break;
+    }
+  }
 }
 
 /// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived].
@@ -1168,7 +1204,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
     return ParseCXXClassMemberDeclaration(AS, TemplateInfo);
   }
 
-  // Don't parse FOO:BAR as if it were a typo for FOO::BAR.
+  // Don't parse FOO:BAR as if it were a typo for FOO::BAR, in this context it
+  // is a bitfield.
   ColonProtectionRAIIObject X(*this);
   
   CXX0XAttributeList AttrList;
index 5b1196016032ba5a763b6197699a653c4ae1d736..e1f2bf4911a6c6dc3aa9625b15be1766c6e6dd75 100644 (file)
@@ -7,11 +7,10 @@ void foof(const char *, ...) __attribute__((__format__(__printf__, 1, 2))), barf
 
 int typedef validTypeDecl() { } // expected-error {{function definition declared 'typedef'}}
 
-struct _zend_module_entry { }
-typedef struct _zend_function_entry { } // expected-error {{cannot combine with previous 'struct' declaration specifier}}
-static void buggy(int *x) { } // expected-error {{function definition declared 'typedef'}} \
-                              // expected-error {{cannot combine with previous 'typedef' declaration specifier}} \
-                              // expected-error {{cannot combine with previous 'struct' declaration specifier}}
+struct _zend_module_entry { }    // expected-error {{expected ';' after struct}}
+typedef struct _zend_function_entry { } // expected-error {{expected ';' after struct}} \
+                                        // expected-error {{declaration does not declare anything}}
+static void buggy(int *x) { }
 
 // Type qualifiers.
 typedef int f(void); 
@@ -22,3 +21,10 @@ __restrict__ fptr v3; // expected-error {{pointer to function type 'f' (aka 'int
 f *__restrict__ v4;   // expected-error {{pointer to function type 'f' (aka 'int (void)') may not be 'restrict' qualified}}
 
 restrict struct hallo; // expected-error {{restrict requires a pointer or reference}}
+
+// PR6180
+struct test1 {
+} // expected-error {{expected ';' after struct}}
+
+void test2() {}
+