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:
parent
c1775967e3
commit
a9aa2c3ae2
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
116
gcc/cp/parser.c
116
gcc/cp/parser.c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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 *-*-* } }
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -15,4 +15,4 @@ class a {
|
|||
|
||||
static const unsigned int value = _rec < 1 >::size;
|
||||
|
||||
} // { dg-error "unqualified-id" }
|
||||
} // { dg-error "after class definition" }
|
||||
|
|
|
@ -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 *);
|
||||
|
|
Loading…
Reference in New Issue