re PR c++/16189 (obfuscated error message for missing semicolon after declaration in C++)

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
This commit is contained in:
Nathan Froyd 2010-11-20 18:50:00 +00:00 committed by Nathan Froyd
parent c1775967e3
commit a9aa2c3ae2
17 changed files with 472 additions and 35 deletions

View 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

View 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"

View 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)

View 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

View File

@ -502,15 +502,25 @@ cp_lexer_token_at (cp_lexer *lexer ATTRIBUTE_UNUSED, cp_token_position pos)
return pos;
}
static inline void
cp_lexer_set_token_position (cp_lexer *lexer, cp_token_position pos)
{
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)
return lexer->last_token - 1;
else
return cp_lexer_token_position (lexer, true);
}
static inline cp_token *
cp_lexer_previous_token (cp_lexer *lexer)
{
cp_token_position tp;
if (lexer->next_token == &eof_token)
tp = lexer->last_token - 1;
else
tp = cp_lexer_token_position (lexer, true);
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

View 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

View 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;

View 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};

View 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;
};

View 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();
};

View 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 }

View 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 }

View 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 *-*-* } }

View File

@ -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;
}

View 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;

View File

@ -15,4 +15,4 @@ class a {
static const unsigned int value = _rec < 1 >::size;
} // { dg-error "unqualified-id" }
} // { dg-error "after class definition" }

View 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)
{x // { dg-error "not declared in this scope" }
the_box->print(); // { dg-error "before" }
}
template <class BOX> void test(BOX *the_box) // { dg-error "" } semicolon missing
{x
the_box->print();
};
template void test<> (test_box *); // { dg-error "" }
template void test<> (test_box *);