re PR c++/16189 (obfuscated error message for missing semicolon after declaration...
authorNathan Froyd <froydnj@codesourcery.com>
Sat, 20 Nov 2010 18:50:00 +0000 (18:50 +0000)
committerNathan Froyd <froydnj@gcc.gnu.org>
Sat, 20 Nov 2010 18:50:00 +0000 (18:50 +0000)
gcc/c-family/
PR c++/16189
PR c++/36888
PR c++/45331
* c-common.h (keyword_begins_type_specifier): Declare.
(keyword_is_storage_class_specifier): Declare.
(keyword_is_type_qualifier): Declare.
* c-common.c (keyword_begins_type_specifier): New function.
(keyword_is_storage_class_specifier): New function.
(keyword_is_type_qualifier): Declare.

gcc/cp/
PR c++/16189
PR c++/36888
PR c++/45331
* parser.c (cp_lexer_set_token_position): New function.
(cp_lexer_previous_token_position): New function.
(cp_lexer_previous_token): Call it.
(cp_parser_class_specifier): Try to gracefully handle a missing
semicolon.

gcc/testsuite/
PR c++/16189
PR c++/36888
PR c++/45331
* g++.dg/parse/semicolon3.C: New test.
* g++.dg/debug/pr22514.C: Adjust.
* g++.dg/init/error1.C: Adjust.
* g++.dg/other/bitfield3.C: Adjust.
* g++.dg/other/semicolon.C: Adjust.
* g++.dg/parse/error14.C: Adjust.
* g++.dg/parse/error5.C: Adjust.
* g++.dg/parse/parameter-declaration-1.C: Adjust.
* g++.dg/template/pr23510.C: Adjust.
* g++.dg/template/pr39425.C: Adjust.
* g++.old-deja/g++.robertl/eb125.C: Adjust.

From-SVN: r166977

17 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/debug/pr22514.C
gcc/testsuite/g++.dg/init/error1.C
gcc/testsuite/g++.dg/other/bitfield3.C
gcc/testsuite/g++.dg/other/semicolon.C
gcc/testsuite/g++.dg/parse/error14.C
gcc/testsuite/g++.dg/parse/error5.C
gcc/testsuite/g++.dg/parse/parameter-declaration-1.C
gcc/testsuite/g++.dg/parse/semicolon3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/pr23510.C
gcc/testsuite/g++.dg/template/pr39425.C
gcc/testsuite/g++.old-deja/g++.robertl/eb125.C

index 1c4b3f5506eeaf5d7af087d71ab7c9fc7c432c8e..f10fde75fac92a9665cc9a39c5c73180dd0a3cdf 100644 (file)
@@ -1,3 +1,15 @@
+2010-11-20  Nathan Froyd  <froydnj@codesourcery.com>
+
+       PR c++/16189
+       PR c++/36888
+       PR c++/45331
+       * c-common.h (keyword_begins_type_specifier): Declare.
+       (keyword_is_storage_class_specifier): Declare.
+       (keyword_is_type_qualifier): Declare.
+       * c-common.c (keyword_begins_type_specifier): New function.
+       (keyword_is_storage_class_specifier): New function.
+       (keyword_is_type_qualifier): Declare.
+
 2010-11-19  Joseph Myers  <joseph@codesourcery.com>
 
        PR c/46547
index cd9175adb7c1e85de7084722fd4c0883ea2e4fee..8b2fd601bdd12d379f1f939420d6d3b3a7b23381 100644 (file)
@@ -9461,4 +9461,82 @@ make_tree_vector_copy (const VEC(tree,gc) *orig)
   return ret;
 }
 
+/* Return true if KEYWORD starts a type specifier.  */
+
+bool
+keyword_begins_type_specifier (enum rid keyword)
+{
+  switch (keyword)
+    {
+    case RID_INT:
+    case RID_CHAR:
+    case RID_FLOAT:
+    case RID_DOUBLE:
+    case RID_VOID:
+    case RID_INT128:
+    case RID_UNSIGNED:
+    case RID_LONG:
+    case RID_SHORT:
+    case RID_SIGNED:
+    case RID_DFLOAT32:
+    case RID_DFLOAT64:
+    case RID_DFLOAT128:
+    case RID_FRACT:
+    case RID_ACCUM:
+    case RID_BOOL:
+    case RID_WCHAR:
+    case RID_CHAR16:
+    case RID_CHAR32:
+    case RID_SAT:
+    case RID_COMPLEX:
+    case RID_TYPEOF:
+    case RID_STRUCT:
+    case RID_CLASS:
+    case RID_UNION:
+    case RID_ENUM:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Return true if KEYWORD names a type qualifier.  */
+
+bool
+keyword_is_type_qualifier (enum rid keyword)
+{
+  switch (keyword)
+    {
+    case RID_CONST:
+    case RID_VOLATILE:
+    case RID_RESTRICT:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Return true if KEYWORD names a storage class specifier.
+
+   RID_TYPEDEF is not included in this list despite `typedef' being
+   listed in C99 6.7.1.1.  6.7.1.3 indicates that `typedef' is listed as
+   such for syntactic convenience only.  */
+
+bool
+keyword_is_storage_class_specifier (enum rid keyword)
+{
+  switch (keyword)
+    {
+    case RID_STATIC:
+    case RID_EXTERN:
+    case RID_REGISTER:
+    case RID_AUTO:
+    case RID_MUTABLE:
+    case RID_THREAD:
+      return true;
+    default:
+      return false;
+    }
+}
+
 #include "gt-c-family-c-common.h"
index c01b183203a6e775d646970acd409f6ea5dc9048..0e603303d2c7d68f810e7d8c48f12d45b4dc8d80 100644 (file)
@@ -737,6 +737,10 @@ extern void set_float_const_decimal64 (void);
 extern void clear_float_const_decimal64 (void);
 extern bool float_const_decimal64_p (void);
 
+extern bool keyword_begins_type_specifier (enum rid);
+extern bool keyword_is_storage_class_specifier (enum rid);
+extern bool keyword_is_type_qualifier (enum rid);
+
 #define c_sizeof(LOC, T)  c_sizeof_or_alignof_type (LOC, T, true, 1)
 #define c_alignof(LOC, T) c_sizeof_or_alignof_type (LOC, T, false, 1)
 
index 8310ca6e032feb519a6ab1fc642f1b424166dc25..8d3b7770b4a9846bf060216743c8524412aface0 100644 (file)
@@ -1,3 +1,14 @@
+2010-11-20  Nathan Froyd  <froydnj@codesourcery.com>
+
+       PR c++/16189
+       PR c++/36888
+       PR c++/45331
+       * parser.c (cp_lexer_set_token_position): New function.
+       (cp_lexer_previous_token_position): New function.
+       (cp_lexer_previous_token): Call it.
+       (cp_parser_class_specifier): Try to gracefully handle a missing
+       semicolon.
+
 2010-11-20  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/46538
index c82eb036749b85bbe2e563f1b16368ecfa139698..14961ce3ee946b386bb21a16badfbb03d4ca45c5 100644 (file)
@@ -502,15 +502,25 @@ cp_lexer_token_at (cp_lexer *lexer ATTRIBUTE_UNUSED, cp_token_position pos)
   return pos;
 }
 
-static inline cp_token *
-cp_lexer_previous_token (cp_lexer *lexer)
+static inline void
+cp_lexer_set_token_position (cp_lexer *lexer, cp_token_position pos)
 {
-  cp_token_position tp;
+  lexer->next_token = cp_lexer_token_at (lexer, pos);
+}
 
+static inline cp_token_position
+cp_lexer_previous_token_position (cp_lexer *lexer)
+{
   if (lexer->next_token == &eof_token)
-    tp = lexer->last_token - 1;
+    return lexer->last_token - 1;
   else
-    tp = cp_lexer_token_position (lexer, true);
+    return cp_lexer_token_position (lexer, true);
+}
+
+static inline cp_token *
+cp_lexer_previous_token (cp_lexer *lexer)
+{
+  cp_token_position tp = cp_lexer_previous_token_position (lexer);
 
   return cp_lexer_token_at (lexer, tp);
 }
@@ -16860,6 +16870,100 @@ cp_parser_class_specifier (cp_parser* parser)
     type = finish_struct (type, attributes);
   if (nested_name_specifier_p)
     pop_inner_scope (old_scope, scope);
+
+  /* We've finished a type definition.  Check for the common syntax
+     error of forgetting a semicolon after the definition.  We need to
+     be careful, as we can't just check for not-a-semicolon and be done
+     with it; the user might have typed:
+
+     class X { } c = ...;
+     class X { } *p = ...;
+
+     and so forth.  Instead, enumerate all the possible tokens that
+     might follow this production; if we don't see one of them, then
+     complain and silently insert the semicolon.  */
+  {
+    cp_token *token = cp_lexer_peek_token (parser->lexer);
+    bool want_semicolon = true;
+
+    switch (token->type)
+      {
+      case CPP_NAME:
+      case CPP_SEMICOLON:
+      case CPP_MULT:
+      case CPP_AND:
+      case CPP_OPEN_PAREN:
+      case CPP_CLOSE_PAREN:
+      case CPP_COMMA:
+        want_semicolon = false;
+        break;
+
+        /* While it's legal for type qualifiers and storage class
+           specifiers to follow type definitions in the grammar, only
+           compiler testsuites contain code like that.  Assume that if
+           we see such code, then what we're really seeing is a case
+           like:
+
+           class X { }
+           const <type> var = ...;
+
+           or
+
+           class Y { }
+           static <type> func (...) ...
+
+           i.e. the qualifier or specifier applies to the next
+           declaration.  To do so, however, we need to look ahead one
+           more token to see if *that* token is a type specifier.
+
+          This code could be improved to handle:
+
+          class Z { }
+          static const <type> var = ...;  */
+      case CPP_KEYWORD:
+       if (keyword_is_storage_class_specifier (token->keyword)
+           || keyword_is_type_qualifier (token->keyword))
+         {
+           cp_token *lookahead = cp_lexer_peek_nth_token (parser->lexer, 2);
+
+           if (lookahead->type == CPP_KEYWORD
+               && !keyword_begins_type_specifier (lookahead->keyword))
+             want_semicolon = false;
+           else if (lookahead->type == CPP_NAME)
+             /* Handling user-defined types here would be nice, but
+                very tricky.  */
+             want_semicolon = false;
+         }
+       break;
+      default:
+       break;
+      }
+
+    if (want_semicolon)
+      {
+       cp_token_position prev
+         = cp_lexer_previous_token_position (parser->lexer);
+       cp_token *prev_token = cp_lexer_token_at (parser->lexer, prev);
+       location_t loc = prev_token->location;
+
+       if (CLASSTYPE_DECLARED_CLASS (type))
+         error_at (loc, "expected %<;%> after class definition");
+       else if (TREE_CODE (type) == RECORD_TYPE)
+         error_at (loc, "expected %<;%> after struct definition");
+       else if (TREE_CODE (type) == UNION_TYPE)
+         error_at (loc, "expected %<;%> after union definition");
+       else
+         gcc_unreachable ();
+
+       /* Unget one token and smash it to look as though we encountered
+          a semicolon in the input stream.  */
+       cp_lexer_set_token_position (parser->lexer, prev);
+       token = cp_lexer_peek_token (parser->lexer);
+       token->type = CPP_SEMICOLON;
+       token->keyword = RID_MAX;
+      }
+  }
+
   /* If this class is not itself within the scope of another class,
      then we need to parse the bodies of all of the queued function
      definitions.  Note that the queued functions defined in a class
index 6d32aa633152d5e99c0094ed9d11011a3e9aa50d..fb02c954029ed0763dc512483274dd691b95b472 100644 (file)
@@ -1,3 +1,20 @@
+2010-11-20  Nathan Froyd  <froydnj@codesourcery.com>
+
+       PR c++/16189
+       PR c++/36888
+       PR c++/45331
+       * g++.dg/parse/semicolon3.C: New test.
+       * g++.dg/debug/pr22514.C: Adjust.
+       * g++.dg/init/error1.C: Adjust.
+       * g++.dg/other/bitfield3.C: Adjust.
+       * g++.dg/other/semicolon.C: Adjust.
+       * g++.dg/parse/error14.C: Adjust.
+       * g++.dg/parse/error5.C: Adjust.
+       * g++.dg/parse/parameter-declaration-1.C: Adjust.
+       * g++.dg/template/pr23510.C: Adjust.
+       * g++.dg/template/pr39425.C: Adjust.
+       * g++.old-deja/g++.robertl/eb125.C: Adjust.
+
 2010-11-20  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/46561
index 3df9e230ecb27ab2ffb940b29adf24f599f0576a..ed31cc7a103126531681178b2c6864eeaa3cf217 100644 (file)
@@ -8,6 +8,6 @@ namespace s
   template<int i> struct list : _List_base<i>
   {
     using _List_base<i>::_M_impl;
-  }
-}  /* { dg-error "expected unqualified-id before '\}'" } */
+  } // { dg-error "after struct definition" }
+}
 s::list<1> OutputModuleListType;
index dd12e4cca43d546eabbca844e72ef27ca56bae6b..bdd983f9665756920061dc4945a994a20c173de7 100644 (file)
@@ -2,6 +2,6 @@
 
 struct A {
   static float b[10];
-}
+} // { dg-error "after struct definition" }
 
-float A::b[] = {1,2,3}; // { dg-error "" }
+float A::b[] = {1,2,3};
index b9726c26a51f759d2cbcec495bd374da89b7aafc..befd7f817213e426bcfa2599c0ca1704de94a93b 100644 (file)
@@ -3,13 +3,15 @@
 
 template<int> struct A
 {
-  struct {} : 2;       // { dg-error "with non-integral type" }
+  // multiple errors below: missing semicolon, no anonymous structs, etc.
+  struct {} : 2;       // { dg-error "" }
 };
 
 template<int> struct B
 {
   int a;
-  struct {} : 2;       // { dg-error "with non-integral type" }
+  // multiple errors below: missing semicolon, no anonymous structs, etc.
+  struct {} : 2;       // { dg-error "" }
   int b;
 };
 
index efbae8b7021a9295102d092354c6d2f7a6340353..8797bd32f310dc592f5b2c334b97be558796a80e 100644 (file)
@@ -5,7 +5,6 @@
 
 struct A
 {
-  struct B { int i; } // { dg-error "3:new types may not be defined in a return type" }
-                      // { dg-message "perhaps a semicolon is missing" "note" { target *-*-* } 8 }
-  void foo();   // { dg-error "12:two or more" }
+  struct B { int i; } // { dg-error "after struct definition" }
+  void foo();
 };
index 9e672c286b25c0c0ac4d9db2fc35691c7c416cc1..37abe37562a276fa927c10f9b8c5a3d23c6dcd9d 100644 (file)
@@ -21,6 +21,6 @@ struct X
 
 }; // { dg-error "2:expected '.' at end of input" "at end of input" }
    // { dg-error "1:expected primary-expression before '.' token" "primary" { target *-*-* } 22 }
-   // { dg-error "1:expected ';' before '.' token" "semicolon" { target *-*-* } 22 }
-   // { dg-error "1:expected unqualified-id at end of input" "unqual" { target *-*-* } 22 }
+   // { dg-error "2:expected ';' after struct definition" "semicolon" { target *-*-* } 22 }
+   // { dg-error "1:expected ';' before '.' token" "function" { target *-*-* } 22 }
 
index 6ebb087306b67597c5a68993a4cda05fdd1e4d8b..eb1f9c730a839f9a22e9771a29c5714c26a46320 100644 (file)
@@ -3,17 +3,17 @@
 
 class Foo { int foo() return 0; } };
 
-// { dg-error "30:expected identifier before numeric constant" "" { target *-*-* } 4 }
+// { dg-error "30:expected identifier before numeric constant" "identifier" { target *-*-* } 4 }
 
-// { dg-error "23:named return values are no longer supported" "" { target *-*-* } 4 }
+// { dg-error "23:named return values are no longer supported" "named return" { target *-*-* } 4 }
 
 // the column number info of this error output is still wrong because the error
 // message has been generated by cp_parser_error() which does not
 // necessarily allow accurate column number display. At some point, we will
 // need make cp_parser_error() report more accurate column numbers.
-// { dg-error "30:expected '\{' at end of input" "" { target *-*-* } 4 }
+// { dg-error "30:expected '\{' at end of input" "brace" { target *-*-* } 4 }
 
-// { dg-error "35:expected unqualified-id before '\}' token" "" {target *-*-* } 4 }
+// { dg-error "33:expected ';' after class definition" "semicolon" {target *-*-* } 4 }
 
-// { dg-error "35:expected declaration before '\}' token" "" {target *-*-* } 4 }
+// { dg-error "35:expected declaration before '\}' token" "declaration" {target *-*-* } 4 }
 
index 22d6f214d7df03c0a5c51757dee0c067ced7cad3..58f679944e21cf80049396eb5a9080ca2a103380 100644 (file)
@@ -2,5 +2,5 @@
 // Origin: Robert Schiele; PR C++/8799
 // { dg-do compile }
 
-struct {
+struct {                       // { dg-error "" }
    a(void = 0; a(0), a(0)      // { dg-error "" "" { target *-*-* } }
diff --git a/gcc/testsuite/g++.dg/parse/semicolon3.C b/gcc/testsuite/g++.dg/parse/semicolon3.C
new file mode 100644 (file)
index 0000000..a119ef4
--- /dev/null
@@ -0,0 +1,210 @@
+// PR c++/45331
+// { dg-do compile }
+
+struct OK1
+{
+  int a;
+} // no complaints
+  *s5;
+
+struct OK2
+{
+  int a;
+} // no complaints
+  &s6 = *(new OK2());
+
+struct OK3
+{
+  int a;
+} // no complaints
+  (s7);
+
+__SIZE_TYPE__
+test_offsetof (void)
+{
+  // no complaints about a missing semicolon
+  return __builtin_offsetof (struct OK4 { int a; int b; }, b);
+}
+
+struct OK5
+{
+  int a;
+} ok5_var;                     // no complaints
+
+struct OK6
+{
+  int a;
+} static ok6_var;              // no complaints
+
+class OK7
+{
+public:
+  OK7() { };
+  int a;
+} const ok7_var;               // no complaints
+
+class OK8
+{
+  int a;
+} extern ok8_var;              // no complaints
+
+class OK9
+{
+  class OK9sub { int a; } mutable ok9sub; // no complaints
+  int a;
+};
+
+int
+autotest (void)
+{
+  struct OK10 { int a; } auto ok10 = { 0 }; // no complaints
+
+  return ok10.a;
+}
+
+struct E1
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+typedef float BAR;
+
+struct E2
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+const int i0 = 1;
+
+struct E3
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+volatile long l0 = 1;
+
+struct E4
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+extern char c0;
+
+struct E5
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+static wchar_t wc0;
+
+struct E6
+{
+  int a;
+} // { dg-error "after struct definition" }
+
+bool b0;
+
+class E7
+{
+  int a;
+} // { dg-error "after class definition" }
+
+extern double d0;
+
+class E8
+{
+  int a;
+} // { dg-error "after class definition" }
+
+inline short f(void)
+{
+  return 2;
+}
+
+class E9
+{
+  int a;
+} // { dg-error "after class definition" }
+
+class D0
+{
+  int a;
+};
+
+class E10
+{
+  int a;
+} // { dg-error "after class definition" }
+
+extern class D0 &f0 (void);
+
+class E11
+{
+  int a;
+} // { dg-error "after class definition" }
+
+const struct E6 *f1 (void) { return 0; }
+
+union U0 {
+  int i;
+  double d;
+};
+
+class E12
+{
+  int a;
+} // { dg-error "after class definition" }
+
+const union U0 *f2 (void) { return 0; }
+
+enum e {
+  U, V
+};
+
+class E13
+{
+  int a;
+} // { dg-error "after class definition" }
+
+static enum e f3 (void) { return U; }
+
+union E14
+{
+  int i;
+  double d;
+} // { dg-error "after union definition" }
+
+unsigned int i1 = 2;
+
+union E15
+{
+  int i;
+  double d;
+} // { dg-error "after union definition" }
+
+signed long l1 = 3;
+
+class E16
+{
+  class sub0 { int a; }                // { dg-error "after class definition" }
+    virtual int f2 (void);
+} // { dg-error "after class definition" }
+
+class E17
+{
+  class sub0 { int a; }                // { dg-error "after class definition" }
+    mutable int i;
+} // { dg-error "after class definition" }
+
+/* This was the original test from the PR.  */
+
+class C0
+{
+public:
+ int a;
+} // { dg-error "after class definition" }
+
+const int foo(const C0 &x)
+{
+ return x.a;
+}
index b9e9889e9c0f5f442aaa2a2f9734e977d4e0478b..1c3180f8331fdcf9b8596b7f209350959ed8b884 100644 (file)
@@ -6,13 +6,13 @@ struct Factorial
   enum { nValue = nFactor * Factorial<nFactor - 1>::nValue }; // { dg-error "depth exceeds maximum" } 
   // { dg-message "recursively instantiated" "" { target *-*-* } 6 } 
   // { dg-error "incomplete type" "" { target *-*-* } 6 } 
-} 
+} // { dg-error "expected ';' after" }
 
-  template<> // { dg-error "expected" } 
+  template<>
   struct Factorial<0>
   {
     enum { nValue = 1 };
-  }
+  };
 
     static const unsigned int FACTOR = 20;
 
index a063e05c2c71fcbf9802e3c44db56c62a28bd423..d55f547e25350a3eb8edc959a275f8d367b4a4df 100644 (file)
@@ -15,4 +15,4 @@ class a {
 
   static const unsigned int value = _rec < 1 >::size;
 
-}              // { dg-error "unqualified-id" }
+} // { dg-error "after class definition" }
index b06823685e426b1e7c471c24315b8c28d67458c2..f2352c2221659cf48e8ad85c409d58374b7c1cf9 100644 (file)
@@ -10,13 +10,13 @@ void test<class BOX> (test_box *);   // { dg-error "" } illegal code
 class test_square
     {
       friend void test<class BOX> (test_box *); // { dg-error "" } does not match
-    }
+    }                                          // { dg-error "after class definition" }
 
 
 
-template <class BOX> void test(BOX *the_box)  // { dg-error "" } semicolon missing
-    {x
-    the_box->print();
-    };
+template <class BOX> void test(BOX *the_box)
+    {x                         // { dg-error "not declared in this scope" }
+    the_box->print();          // { dg-error "before" }
+    }
 
-template void test<> (test_box *); // { dg-error "" }
+template void test<> (test_box *);