mirror of
https://github.com/cc65/cc65.git
synced 2025-02-06 12:31:12 +00:00
Merge pull request #2296 from acqn/Diagnostics
[cc65] Fixed and improved diagnostics regarding type names and declarations
This commit is contained in:
commit
cc5e9c38ca
@ -79,7 +79,6 @@
|
||||
static void Parse (void)
|
||||
/* Top level parser routine. */
|
||||
{
|
||||
int comma;
|
||||
SymEntry* Sym;
|
||||
FuncDesc* FuncDef = 0;
|
||||
|
||||
@ -89,16 +88,18 @@ static void Parse (void)
|
||||
/* Fill up the next token with a bogus semicolon and start the tokenizer */
|
||||
NextTok.Tok = TOK_SEMI;
|
||||
NextToken ();
|
||||
NextToken ();
|
||||
|
||||
/* Parse until end of input */
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
|
||||
DeclSpec Spec;
|
||||
int Comma;
|
||||
int NeedClean = 0;
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
|
||||
/* Check for empty statements */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
@ -119,7 +120,7 @@ static void Parse (void)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read variable defs and functions */
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC);
|
||||
|
||||
/* Don't accept illegal storage classes */
|
||||
@ -138,18 +139,30 @@ static void Parse (void)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Read declarations for this type */
|
||||
Sym = 0;
|
||||
comma = 0;
|
||||
Comma = 0;
|
||||
while (1) {
|
||||
|
||||
Declarator Decl;
|
||||
|
||||
Sym = 0;
|
||||
|
||||
/* Read the next declaration */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
|
||||
if (Decl.Ident[0] == '\0') {
|
||||
Sym = 0;
|
||||
goto NextDecl;
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we must reserve storage for the variable. We do this,
|
||||
@ -191,10 +204,6 @@ static void Parse (void)
|
||||
FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM;
|
||||
}
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) {
|
||||
Error ("Expected ',' or ';' after top level declarator");
|
||||
}
|
||||
|
||||
/* Just a declaration */
|
||||
Decl.StorageClass |= SC_DECL;
|
||||
}
|
||||
@ -317,50 +326,50 @@ static void Parse (void)
|
||||
|
||||
}
|
||||
|
||||
NextDecl:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
NextToken ();
|
||||
comma = 1;
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Comma = 1;
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Finish the declaration */
|
||||
if (Sym) {
|
||||
/* Function definition? */
|
||||
if (IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) {
|
||||
if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) {
|
||||
/* ISO C: The type category in a function definition cannot be
|
||||
** inherited from a typedef.
|
||||
*/
|
||||
Error ("Function cannot be defined with a typedef");
|
||||
} else if (comma) {
|
||||
/* ISO C: A function definition cannot shall its return type
|
||||
** specifier with other declarators.
|
||||
*/
|
||||
Error ("';' expected after top level declarator");
|
||||
}
|
||||
if (Sym && IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) {
|
||||
/* A function definition is not terminated with a semicolon */
|
||||
if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) {
|
||||
/* ISO C: The type category in a function definition cannot be
|
||||
** inherited from a typedef.
|
||||
*/
|
||||
Error ("Function cannot be defined with a typedef");
|
||||
} else if (Comma) {
|
||||
/* ISO C: A function definition cannot shall its return type
|
||||
** specifier with other declarators.
|
||||
*/
|
||||
Error ("';' expected after top level declarator");
|
||||
}
|
||||
|
||||
/* Parse the function body anyways */
|
||||
NeedClean = 0;
|
||||
NewFunc (Sym, FuncDef);
|
||||
/* Parse the function body anyways */
|
||||
NeedClean = 0;
|
||||
NewFunc (Sym, FuncDef);
|
||||
|
||||
/* Make sure we aren't omitting any work */
|
||||
CheckDeferredOpAllDone ();
|
||||
/* Make sure we aren't omitting any work */
|
||||
CheckDeferredOpAllDone ();
|
||||
} else if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (CurTok.Tok != TOK_SEMI) {
|
||||
Error ("',' or ';' expected after top level declarator");
|
||||
NeedClean = -1;
|
||||
} else {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
NextToken ();
|
||||
NeedClean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EndOfDecl:
|
||||
/* Try some smart error recovery */
|
||||
if (PrevErrorCount != ErrorCount && NeedClean < 0) {
|
||||
if (NeedClean < 0) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@
|
||||
|
||||
|
||||
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified);
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags);
|
||||
/* Parse a type specifier */
|
||||
|
||||
|
||||
@ -83,125 +83,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
|
||||
|
||||
|
||||
static void OpenBrace (Collection* C, token_t Tok)
|
||||
/* Consume an opening parenthesis/bracket/curly brace and remember that */
|
||||
{
|
||||
switch (Tok) {
|
||||
case TOK_LPAREN: Tok = TOK_RPAREN; break;
|
||||
case TOK_LBRACK: Tok = TOK_RBRACK; break;
|
||||
case TOK_LCURLY: Tok = TOK_RCURLY; break;
|
||||
default: Internal ("Unexpected opening token: %02X", (unsigned)Tok);
|
||||
}
|
||||
CollAppend (C, (void*)Tok);
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CloseBrace (Collection* C, token_t Tok)
|
||||
/* Consume a closing parenthesis/bracket/curly brace if it is matched with an
|
||||
** opening one and return 0, or bail out and return -1 if it is not matched.
|
||||
*/
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
token_t LastTok = (token_t)CollLast (C);
|
||||
if (LastTok == Tok) {
|
||||
CollPop (C);
|
||||
NextToken ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SmartErrorSkip (int WholeDecl)
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If WholeDecl is 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached.
|
||||
**
|
||||
** - If WholeDecl is non-0:
|
||||
** Skip tokens until a closing curly brace that is not enclosed in an open
|
||||
** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached.
|
||||
**
|
||||
** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if
|
||||
** this exits with no open parentheses/brackets/curly braces. Otherwise, return
|
||||
** -1.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI,
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!WholeDecl) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!WholeDecl) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
} else if (CollCount (&C) == 0) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_COMMA:
|
||||
if (CollCount (&C) == 0 && !WholeDecl) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_SEMI:
|
||||
if (CollCount (&C) != 0) {
|
||||
Res = -1;
|
||||
}
|
||||
goto ExitPoint;
|
||||
|
||||
case TOK_CEOF:
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned ParseOneStorageClass (void)
|
||||
/* Parse and return a storage class specifier */
|
||||
{
|
||||
@ -451,20 +332,36 @@ static void OptionalInt (void)
|
||||
|
||||
|
||||
|
||||
static void OptionalSigned (int* SignednessSpecified)
|
||||
static void OptionalSigned (DeclSpec* Spec)
|
||||
/* Eat an optional "signed" token */
|
||||
{
|
||||
if (CurTok.Tok == TOK_SIGNED) {
|
||||
/* Skip it */
|
||||
NextToken ();
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
if (Spec != NULL) {
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void UseDefaultType (DeclSpec* Spec, typespec_t TSFlags)
|
||||
/* Use the default type for the type specifier */
|
||||
{
|
||||
if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_NONE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void InitDeclSpec (DeclSpec* Spec)
|
||||
/* Initialize the DeclSpec struct for use */
|
||||
{
|
||||
@ -1080,9 +977,15 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
|
||||
/* Get the type of the entry */
|
||||
DeclSpec Spec;
|
||||
int SignednessSpecified = 0;
|
||||
int NeedClean = 0;
|
||||
|
||||
/* Check for extra semicolons */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
ParseStaticAssert ();
|
||||
@ -1090,7 +993,27 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
}
|
||||
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Check if this is only a type declaration */
|
||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
CheckEmptyDecl (&Spec);
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Allow anonymous bit-fields */
|
||||
Spec.Flags |= DS_ALLOW_BITFIELD;
|
||||
|
||||
/* Read fields with this type */
|
||||
while (1) {
|
||||
@ -1098,7 +1021,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
Declarator Decl;
|
||||
|
||||
/* Get type and name of the struct field */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a bit-field declaration */
|
||||
FieldWidth = ParseFieldWidth (&Decl);
|
||||
@ -1123,9 +1051,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||
}
|
||||
} else {
|
||||
/* A non bit-field without a name is legal but useless */
|
||||
Warning ("Declaration does not declare anything");
|
||||
|
||||
/* Invalid member */
|
||||
goto NextMember;
|
||||
}
|
||||
} else if (FieldWidth > 0) {
|
||||
@ -1160,7 +1086,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
** bit-field.
|
||||
*/
|
||||
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
|
||||
SignednessSpecified);
|
||||
(Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0);
|
||||
} else if (Decl.Ident[0] != '\0') {
|
||||
/* Add the new field to the table */
|
||||
Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
|
||||
@ -1189,17 +1115,23 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
||||
}
|
||||
}
|
||||
|
||||
NextMember: if (CurTok.Tok != TOK_COMMA) {
|
||||
NextMember:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Must be followed by a semicolon */
|
||||
if (NeedClean >= 0 && ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
EndOfDecl:
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
@ -1265,9 +1197,15 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
|
||||
/* Get the type of the entry */
|
||||
DeclSpec Spec;
|
||||
int SignednessSpecified = 0;
|
||||
int NeedClean = 0;
|
||||
|
||||
/* Check for extra semicolons */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
ParseStaticAssert ();
|
||||
@ -1275,7 +1213,27 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
}
|
||||
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Check if this is only a type declaration */
|
||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
CheckEmptyDecl (&Spec);
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Allow anonymous bit-fields */
|
||||
Spec.Flags |= DS_ALLOW_BITFIELD;
|
||||
|
||||
/* Read fields with this type */
|
||||
while (1) {
|
||||
@ -1291,7 +1249,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
}
|
||||
|
||||
/* Get type and name of the struct field */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a bit-field declaration */
|
||||
FieldWidth = ParseFieldWidth (&Decl);
|
||||
@ -1335,9 +1298,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||
}
|
||||
} else {
|
||||
/* A non bit-field without a name is legal but useless */
|
||||
Warning ("Declaration does not declare anything");
|
||||
|
||||
/* Invalid member */
|
||||
goto NextMember;
|
||||
}
|
||||
} else if (FieldWidth > 0) {
|
||||
@ -1387,8 +1348,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
** bit-field as a char type in expressions.
|
||||
*/
|
||||
CHECK (BitOffs < CHAR_BITS);
|
||||
AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs,
|
||||
FieldWidth, SignednessSpecified);
|
||||
AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, FieldWidth,
|
||||
(Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0);
|
||||
BitOffs += FieldWidth;
|
||||
CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type));
|
||||
/* Add any full bytes to the struct size */
|
||||
@ -1427,17 +1388,23 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
||||
}
|
||||
}
|
||||
|
||||
NextMember: if (CurTok.Tok != TOK_COMMA) {
|
||||
NextMember:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Must be followed by a semicolon */
|
||||
if (NeedClean >= 0 && ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
EndOfDecl:
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
@ -1472,7 +1439,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
|
||||
|
||||
|
||||
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified)
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags)
|
||||
/* Parse a type specifier. Store whether one of "signed" or "unsigned" was
|
||||
** specified, so bit-fields of unspecified signedness can be treated as
|
||||
** unsigned; without special handling, it would be treated as signed.
|
||||
@ -1482,12 +1449,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
SymEntry* TagEntry;
|
||||
TypeCode Qualifiers = T_QUAL_NONE;
|
||||
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 0;
|
||||
}
|
||||
|
||||
/* Assume we have an explicit type */
|
||||
Spec->Flags &= ~DS_DEF_TYPE;
|
||||
/* Assume we have an explicitly specified type */
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_EXPLICIT_TYPE;
|
||||
|
||||
/* Read storage specifiers and/or type qualifiers if we have any */
|
||||
OptionalSpecifiers (Spec, &Qualifiers, TSFlags);
|
||||
@ -1511,15 +1474,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
case TOK_LONG:
|
||||
NextToken ();
|
||||
if (CurTok.Tok == TOK_UNSIGNED) {
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_ULONG;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
OptionalSigned (SignednessSpecified);
|
||||
OptionalSigned (Spec);
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_LONG;
|
||||
Spec->Type[1].C = T_END;
|
||||
@ -1529,15 +1490,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
case TOK_SHORT:
|
||||
NextToken ();
|
||||
if (CurTok.Tok == TOK_UNSIGNED) {
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_USHORT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
OptionalSigned (SignednessSpecified);
|
||||
OptionalSigned (Spec);
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_SHORT;
|
||||
Spec->Type[1].C = T_END;
|
||||
@ -1550,10 +1509,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
Spec->Type[1].C = T_END;
|
||||
break;
|
||||
|
||||
case TOK_SIGNED:
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
case TOK_SIGNED:
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
@ -1589,9 +1546,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
break;
|
||||
|
||||
case TOK_UNSIGNED:
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
@ -1640,15 +1595,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
|
||||
case TOK_UNION:
|
||||
NextToken ();
|
||||
/* */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "union");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Declare the union in the current scope */
|
||||
TagEntry = ParseUnionSpec (Ident, &Spec->Flags);
|
||||
/* Encode the union entry into the type */
|
||||
@ -1659,15 +1618,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
|
||||
case TOK_STRUCT:
|
||||
NextToken ();
|
||||
/* */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "struct");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Declare the struct in the current scope */
|
||||
TagEntry = ParseStructSpec (Ident, &Spec->Flags);
|
||||
/* Encode the struct entry into the type */
|
||||
@ -1678,18 +1641,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
|
||||
case TOK_ENUM:
|
||||
NextToken ();
|
||||
/* Named enum */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_LCURLY) {
|
||||
Error ("Identifier expected for enum tag name");
|
||||
}
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "enum");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Parse the enum decl */
|
||||
TagEntry = ParseEnumSpec (Ident, &Spec->Flags);
|
||||
/* Encode the enum entry into the type */
|
||||
@ -1699,9 +1663,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
/* The signedness of enums is determined by the type, so say this is specified to avoid
|
||||
** the int -> unsigned int handling for plain int bit-fields in AddBitField.
|
||||
*/
|
||||
if (SignednessSpecified) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
break;
|
||||
|
||||
case TOK_IDENT:
|
||||
@ -1718,9 +1680,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
** Unforunately, this will cause plain int bit-fields defined via typedefs
|
||||
** to be treated as signed rather than unsigned.
|
||||
*/
|
||||
if (SignednessSpecified) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
break;
|
||||
} else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
/* Treat this identifier as an unknown type */
|
||||
@ -1734,23 +1694,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
||||
** in DeclareLocals. The type code used here doesn't matter as
|
||||
** long as it has no qualifiers.
|
||||
*/
|
||||
Spec->Flags |= DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_INT);
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
Spec->Flags |= DS_NO_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
Spec->Flags |= DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
}
|
||||
UseDefaultType (Spec, TSFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1839,6 +1789,9 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
|
||||
|
||||
/* Paremeters must have identifiers as names */
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
|
||||
/* We accept only auto and register as storage class specifiers, but
|
||||
** we ignore all this, since we use auto anyway.
|
||||
*/
|
||||
@ -1848,7 +1801,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
||||
}
|
||||
|
||||
/* Type must be specified */
|
||||
if ((Spec.Flags & DS_NO_TYPE) != 0) {
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
Error ("Expected declaration specifiers");
|
||||
break;
|
||||
}
|
||||
@ -1859,7 +1812,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
||||
Declarator Decl;
|
||||
|
||||
/* Read the parameter */
|
||||
ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
|
||||
ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Warn about new local type declaration */
|
||||
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) {
|
||||
@ -1941,7 +1894,7 @@ static void ParseAnsiParamList (FuncDesc* F)
|
||||
}
|
||||
|
||||
/* Type must be specified */
|
||||
if ((Spec.Flags & DS_NO_TYPE) != 0) {
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
Error ("Type specifier missing");
|
||||
}
|
||||
|
||||
@ -2083,7 +2036,7 @@ static FuncDesc* ParseFuncDecl (void)
|
||||
|
||||
|
||||
|
||||
static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Recursively process direct declarators. Build a type array in reverse order. */
|
||||
{
|
||||
/* Read optional function or pointer qualifiers that modify the identifier
|
||||
@ -2101,19 +2054,19 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
||||
NextToken ();
|
||||
|
||||
/* A pointer type cannot be used as an empty declaration */
|
||||
if (Mode == DM_ACCEPT_IDENT) {
|
||||
Mode = DM_NEED_IDENT;
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
|
||||
/* Allow const, restrict, and volatile qualifiers */
|
||||
Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR);
|
||||
|
||||
/* Parse the type that the pointer points to */
|
||||
Mode = DirectDecl (Spec, D, Mode);
|
||||
DirectDecl (Spec, D, Mode);
|
||||
|
||||
/* Add the type */
|
||||
AddTypeCodeToDeclarator (D, T_PTR | Qualifiers);
|
||||
return Mode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurTok.Tok == TOK_LPAREN) {
|
||||
@ -2121,28 +2074,27 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
||||
/* An empty declaration cannot contain parentheses where an identifier
|
||||
** would show up if it were a non-empty declaration.
|
||||
*/
|
||||
if (Mode == DM_ACCEPT_IDENT) {
|
||||
Mode = DM_NEED_IDENT;
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
Mode = DirectDecl (Spec, D, Mode);
|
||||
DirectDecl (Spec, D, Mode);
|
||||
ConsumeRParen ();
|
||||
} else if (CurTok.Tok == TOK_IDENT) {
|
||||
if (Mode == DM_NO_IDENT) {
|
||||
Error ("Unexpected identifier in type name");
|
||||
}
|
||||
strcpy (D->Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
D->Ident[0] = '\0';
|
||||
if (Mode == DM_NEED_IDENT) {
|
||||
if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0 &&
|
||||
CurTok.Tok != TOK_LBRACK &&
|
||||
((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) {
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
}
|
||||
|
||||
while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) {
|
||||
/* An array or function type cannot be used as an empty declaration */
|
||||
if (Mode == DM_ACCEPT_IDENT && D->Ident[0] == '\0') {
|
||||
Mode = DM_NEED_IDENT;
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
|
||||
if (CurTok.Tok == TOK_LPAREN) {
|
||||
|
||||
/* Function declarator */
|
||||
@ -2181,6 +2133,18 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
||||
/* Array declarator */
|
||||
long Size = UNSPECIFIED;
|
||||
|
||||
/* An array type cannot be used as an empty declaration */
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
if (D->Ident[0] == '\0') {
|
||||
if ((Spec->Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
Error ("Identifier or ';' expected after declaration specifiers");
|
||||
} else {
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We cannot have any qualifiers for an array */
|
||||
if (Qualifiers != T_QUAL_NONE) {
|
||||
Error ("Invalid qualifiers for array");
|
||||
@ -2228,8 +2192,6 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
||||
if (Qualifiers & T_QUAL_CDECL) {
|
||||
Error ("Invalid '__cdecl__' qualifier");
|
||||
}
|
||||
|
||||
return Mode;
|
||||
}
|
||||
|
||||
|
||||
@ -2241,20 +2203,41 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
||||
|
||||
|
||||
Type* ParseType (Type* T)
|
||||
/* Parse a complete type specification */
|
||||
/* Parse a complete type specification in parentheses */
|
||||
{
|
||||
DeclSpec Spec;
|
||||
Declarator Decl;
|
||||
int NeedClean = -1;
|
||||
|
||||
/* Skip the left paren */
|
||||
NextToken ();
|
||||
|
||||
/* Get a type without a default */
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Parse additional declarators */
|
||||
ParseDecl (&Spec, &Decl, DM_NO_IDENT);
|
||||
/* Only parse further if there is a type specifier */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
/* Parse additional declarators */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_NO_IDENT);
|
||||
|
||||
/* Copy the type to the target buffer */
|
||||
TypeCopy (T, Decl.Type);
|
||||
/* Copy the type to the target buffer */
|
||||
TypeCopy (T, Decl.Type);
|
||||
} else {
|
||||
/* Fail-safe */
|
||||
TypeCopy (T, type_int);
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
if (NeedClean < 0) {
|
||||
SimpleErrorSkip ();
|
||||
}
|
||||
|
||||
/* Closing paren */
|
||||
if (!ConsumeRParen ()) {
|
||||
SimpleErrorSkip ();
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Return a pointer to the target buffer */
|
||||
return T;
|
||||
@ -2262,14 +2245,24 @@ Type* ParseType (Type* T)
|
||||
|
||||
|
||||
|
||||
int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Parse a variable, type or function declarator. Return -1 if this stops at
|
||||
** an unpaired right parenthesis/bracket/curly brace.
|
||||
** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops
|
||||
** after consuming a semicolon or closing curly brace, or reaching an EOF.
|
||||
** Return 1 otherwise.
|
||||
*/
|
||||
{
|
||||
/* Used to check if we have any errors during parsing this */
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
|
||||
/* If there is no explicit type specifier, an optional identifier becomes
|
||||
** required.
|
||||
*/
|
||||
if (Mode == DM_IDENT_OR_EMPTY &&
|
||||
(Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
|
||||
/* Initialize the Declarator struct */
|
||||
InitDeclarator (D);
|
||||
|
||||
@ -2283,6 +2276,11 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Use the storage class from the declspec */
|
||||
D->StorageClass = Spec->StorageClass;
|
||||
|
||||
/* If we have a function, add a special symbol type */
|
||||
if (IsTypeFunc (D->Type)) {
|
||||
D->StorageClass |= SC_FUNC;
|
||||
}
|
||||
|
||||
/* Do several fixes on qualifiers */
|
||||
FixQualifiers (D->Type);
|
||||
|
||||
@ -2297,26 +2295,8 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Parse attributes for this declarator */
|
||||
ParseAttribute (D);
|
||||
|
||||
/* If we have a function, add a special storage class */
|
||||
if (IsTypeFunc (D->Type)) {
|
||||
|
||||
D->StorageClass |= SC_FUNC;
|
||||
|
||||
} else if (!IsTypeVoid (D->Type)) {
|
||||
/* Check the size of the generated type */
|
||||
unsigned Size = SizeOf (D->Type);
|
||||
|
||||
if (Size >= 0x10000) {
|
||||
if (D->Ident[0] != '\0') {
|
||||
Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size);
|
||||
} else {
|
||||
Error ("Invalid size in declaration (0x%06X)", Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check a few pre-C99 things */
|
||||
if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) {
|
||||
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
|
||||
/* Check and warn about an implicit int return in the function */
|
||||
if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) {
|
||||
/* Function has an implicit int return. Output a warning if we don't
|
||||
@ -2331,7 +2311,7 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* For anything that is not a function or typedef, check for an implicit
|
||||
** int declaration.
|
||||
*/
|
||||
if ((D->StorageClass & SC_FUNC) != SC_FUNC &&
|
||||
if (!IsTypeFunc (D->Type) &&
|
||||
(D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
||||
/* If the standard was not set explicitly to C89, print a warning
|
||||
** for variables with implicit int type.
|
||||
@ -2342,11 +2322,32 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
}
|
||||
}
|
||||
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
if ((Spec->Flags & DS_DEF_TYPE) == 0 && Mode == DM_NEED_IDENT && D->Ident[0] == '\0') {
|
||||
/* Make the declaration fictitious if is is not parsed correctly */
|
||||
D->StorageClass |= SC_FICTITIOUS;
|
||||
/* Check the size of the declared type */
|
||||
if (IsObjectType (D->Type)) {
|
||||
unsigned Size = SizeOf (D->Type);
|
||||
|
||||
if (Size >= 0x10000) {
|
||||
if (D->Ident[0] != '\0') {
|
||||
Error ("Size of '%s' is too large (0x%06X)", D->Ident, Size);
|
||||
} else {
|
||||
Error ("Size in declaration is too large (0x%06X)", Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* An empty declaration must be terminated with a semicolon */
|
||||
if (PrevErrorCount == ErrorCount &&
|
||||
Mode == DM_IDENT_OR_EMPTY &&
|
||||
D->Ident[0] == '\0' &&
|
||||
CurTok.Tok != TOK_SEMI &&
|
||||
((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) {
|
||||
Error ("Identifier or ';' expected after declaration specifiers");
|
||||
}
|
||||
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
if ((Spec->Flags & DS_TYPE_MASK) != DS_DEF_TYPE &&
|
||||
(Spec->Flags & DS_NO_EMPTY_DECL) != 0 &&
|
||||
D->Ident[0] == '\0') {
|
||||
/* Use a fictitious name for the identifier if it is missing */
|
||||
const char* Level = "";
|
||||
|
||||
@ -2366,15 +2367,21 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
break;
|
||||
}
|
||||
AnonName (D->Ident, Level);
|
||||
|
||||
/* Make the declarator fictitious */
|
||||
D->StorageClass |= SC_FICTITIOUS;
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) {
|
||||
return SmartErrorSkip (0);
|
||||
/* Skip to the end of the whole declaration if it is not part of a
|
||||
** parameter list or a type cast.
|
||||
*/
|
||||
return SmartErrorSkip (Mode == DM_IDENT_OR_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -2389,7 +2396,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage)
|
||||
Spec->Flags &= ~DS_DEF_STORAGE;
|
||||
|
||||
/* Parse the type specifiers */
|
||||
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL);
|
||||
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC);
|
||||
|
||||
/* If no explicit storage class is given, use the default */
|
||||
if (Spec->StorageClass == 0) {
|
||||
@ -2406,7 +2413,13 @@ void CheckEmptyDecl (const DeclSpec* Spec)
|
||||
** warning if not.
|
||||
*/
|
||||
{
|
||||
if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Warning ("Useless declaration");
|
||||
if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* No declaration at all */
|
||||
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Warning ("Declaration does not declare anything");
|
||||
} else if (IsClassStruct (Spec->Type) &&
|
||||
!IsIncompleteESUType (Spec->Type) &&
|
||||
SymHasAnonName (GetESUTagSym (Spec->Type))) {
|
||||
Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type));
|
||||
}
|
||||
}
|
||||
|
@ -65,18 +65,26 @@ enum typespec_t {
|
||||
TS_DEFAULT_TYPE_AUTO = 0x02, /* C23 type inference with auto */
|
||||
|
||||
/* Whether to allow certain kinds of specifiers */
|
||||
TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage storage class specifiers */
|
||||
TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */
|
||||
TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage class specifiers */
|
||||
TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */
|
||||
};
|
||||
|
||||
/* Masks for the Flags field in DeclSpec */
|
||||
#define DS_NONE 0x0000U /* Nothing specified or used */
|
||||
#define DS_DEF_STORAGE 0x0001U /* Default storage class used */
|
||||
#define DS_NO_TYPE 0x0002U /* No type explicitly specified */
|
||||
#define DS_DEF_TYPE 0x0006U /* Default type used */
|
||||
#define DS_EXTRA_TYPE 0x0008U /* Extra type declared */
|
||||
#define DS_EXPLICIT_TYPE 0x0002U /* Type specified */
|
||||
#define DS_DEF_TYPE 0x0004U /* Implicit type used */
|
||||
#define DS_AUTO_TYPE 0x0006U /* C23 auto type used */
|
||||
#define DS_TYPE_MASK 0x0006U /* Mask for type of spec decl */
|
||||
#define DS_EXTRA_TYPE 0x0008U /* ESU type in declaration */
|
||||
#define DS_NEW_TYPE_DECL 0x0010U /* New type declared */
|
||||
#define DS_NEW_TYPE_DEF 0x0020U /* New type defined */
|
||||
#define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF)
|
||||
#define DS_EXPLICIT_SIGNEDNESS 0x0040U /* Signedness specified */
|
||||
#define DS_NO_EMPTY_DECL 0x0100U /* Disallow empty declaration */
|
||||
#define DS_ALLOW_BITFIELD 0x0200U /* Allow anonymous bit-fields */
|
||||
|
||||
|
||||
|
||||
/* Result of ParseDeclSpec */
|
||||
typedef struct DeclSpec DeclSpec;
|
||||
@ -99,24 +107,22 @@ struct Declarator {
|
||||
};
|
||||
|
||||
/* Modes for ParseDecl:
|
||||
** - DM_NEED_IDENT means:
|
||||
** we *must* have a type and a variable identifer.
|
||||
** - DM_IDENT_OR_EMPTY means:
|
||||
** we *may* have an identifier, or none. If it is the latter case,
|
||||
** the type specifier must be used for an empty declaration,
|
||||
** or it is an error.
|
||||
** - DM_NO_IDENT means:
|
||||
** we must have a type but no variable identifer
|
||||
** (if there is one, it's not read).
|
||||
** - DM_ACCEPT_IDENT means:
|
||||
** we *may* have an identifier, or none. If it is the latter case,
|
||||
** the type must be used as an empty declaration, or it is an error.
|
||||
** Note: this is used for struct/union members.
|
||||
** - DM_IGNORE_IDENT means:
|
||||
** Note: this is used for type names.
|
||||
** - DM_ACCEPT_PARAM_IDENT means:
|
||||
** we *may* have an identifier. If there is an identifier,
|
||||
** it is read, but it is no error, if there is none.
|
||||
** Note: this is used for function parameter type lists.
|
||||
*/
|
||||
typedef enum {
|
||||
DM_NEED_IDENT,
|
||||
DM_IDENT_OR_EMPTY,
|
||||
DM_NO_IDENT,
|
||||
DM_ACCEPT_IDENT,
|
||||
DM_ACCEPT_PARAM_IDENT,
|
||||
} declmode_t;
|
||||
|
||||
@ -128,29 +134,14 @@ typedef enum {
|
||||
|
||||
|
||||
|
||||
int SmartErrorSkip (int WholeDecl);
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If WholeDecl is 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached.
|
||||
**
|
||||
** - If WholeDecl is non-0:
|
||||
** Skip tokens until a closing curly brace that is not enclosed in an open
|
||||
** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached.
|
||||
**
|
||||
** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if
|
||||
** this exits with no open parentheses/brackets/curly braces. Otherwise, return
|
||||
** -1.
|
||||
*/
|
||||
|
||||
Type* ParseType (Type* Type);
|
||||
/* Parse a complete type specification */
|
||||
/* Parse a complete type specification in parentheses */
|
||||
|
||||
int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode);
|
||||
int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode);
|
||||
/* Parse a variable, type or function declarator. Return -1 if this stops at
|
||||
** an unpaired right parenthesis/bracket/curly brace.
|
||||
** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops
|
||||
** after consuming a semicolon or closing curly brace, or reaching an EOF.
|
||||
** Return 1 otherwise.
|
||||
*/
|
||||
|
||||
void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage);
|
||||
|
@ -1414,25 +1414,9 @@ static void Primary (ExprDesc* E)
|
||||
DeclSpec Spec;
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
|
||||
|
||||
if ((Spec.Flags & DS_DEF_TYPE) == 0) {
|
||||
/* Recognized but not supported */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
Error ("Mixed declarations and code are not supported in cc65");
|
||||
|
||||
while (CurTok.Tok != TOK_SEMI) {
|
||||
Declarator Decl;
|
||||
|
||||
/* Parse one declaration */
|
||||
ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
if (CurTok.Tok == TOK_ASSIGN) {
|
||||
NextToken ();
|
||||
ParseInit (Decl.Type);
|
||||
}
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
NextToken ();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
SmartErrorSkip (0);
|
||||
} else {
|
||||
Error ("Expression expected");
|
||||
E->Flags |= E_EVAL_MAYBE_UNUSED;
|
||||
@ -2089,9 +2073,7 @@ void hie10 (ExprDesc* Expr)
|
||||
NextToken ();
|
||||
if (TypeSpecAhead ()) {
|
||||
Type T[MAXTYPELEN];
|
||||
NextToken ();
|
||||
Size = ExprCheckedSizeOf (ParseType (T));
|
||||
ConsumeRParen ();
|
||||
} else {
|
||||
/* Remember the output queue pointer */
|
||||
CodeMark Mark;
|
||||
|
@ -441,14 +441,15 @@ static void ParseStaticDecl (Declarator* Decl)
|
||||
|
||||
|
||||
|
||||
static void ParseOneDecl (const DeclSpec* Spec)
|
||||
static int ParseOneDecl (DeclSpec* Spec)
|
||||
/* Parse one variable declarator. */
|
||||
{
|
||||
Declarator Decl; /* Declarator data structure */
|
||||
Declarator Decl; /* Declarator data structure */
|
||||
int NeedClean;
|
||||
|
||||
|
||||
/* Read the declarator */
|
||||
ParseDecl (Spec, &Decl, DM_NEED_IDENT);
|
||||
NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Check if there are any non-extern storage classes set for function
|
||||
** declarations. Function can only be declared inside functions with the
|
||||
@ -538,6 +539,8 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
||||
|
||||
/* Make sure we aren't missing some work */
|
||||
CheckDeferredOpAllDone ();
|
||||
|
||||
return NeedClean;
|
||||
}
|
||||
|
||||
|
||||
@ -553,15 +556,8 @@ void DeclareLocals (void)
|
||||
|
||||
/* Loop until we don't find any more variables */
|
||||
while (1) {
|
||||
|
||||
/* Check variable declarations. We need to distinguish between a
|
||||
** default int type and the end of variable declarations. So we
|
||||
** will do the following: If there is no explicit storage class
|
||||
** specifier *and* no explicit type given, *and* no type qualifiers
|
||||
** have been read, it is assumed that we have reached the end of
|
||||
** declarations.
|
||||
*/
|
||||
DeclSpec Spec;
|
||||
int NeedClean;
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
@ -569,10 +565,18 @@ void DeclareLocals (void)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
|
||||
if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
|
||||
(Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
|
||||
GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
|
||||
|
||||
/* Check variable declarations. We need distinguish between a default
|
||||
** int type and the end of variable declarations. So we will do the
|
||||
** following: If there is no explicit storage class specifier *and* no
|
||||
** explicit type given, *and* no type qualifiers have been read, it is
|
||||
** assumed that we have reached the end of declarations.
|
||||
*/
|
||||
if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
|
||||
(Spec.Flags & DS_TYPE_MASK) == DS_DEF_TYPE && /* No type given */
|
||||
GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
|
||||
break;
|
||||
}
|
||||
|
||||
@ -584,11 +588,24 @@ void DeclareLocals (void)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Parse a comma separated variable list */
|
||||
while (1) {
|
||||
|
||||
/* Parse one declaration */
|
||||
ParseOneDecl (&Spec);
|
||||
/* Parse one declarator */
|
||||
NeedClean = ParseOneDecl (&Spec);
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if there is more */
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
@ -600,8 +617,20 @@ void DeclareLocals (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* A semicolon must follow */
|
||||
ConsumeSemi ();
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
EndOfDecl:
|
||||
/* Try some smart error recovery */
|
||||
if (NeedClean < 0) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Be sure to allocate any reserved space for locals */
|
||||
|
@ -1235,6 +1235,212 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount)
|
||||
|
||||
|
||||
|
||||
static void OpenBrace (Collection* C, token_t Tok)
|
||||
/* Consume an opening parenthesis/bracket/curly brace and remember that */
|
||||
{
|
||||
switch (Tok) {
|
||||
case TOK_LPAREN: Tok = TOK_RPAREN; break;
|
||||
case TOK_LBRACK: Tok = TOK_RBRACK; break;
|
||||
case TOK_LCURLY: Tok = TOK_RCURLY; break;
|
||||
default: Internal ("Unexpected opening token: %02X", (unsigned)Tok);
|
||||
}
|
||||
CollAppend (C, (void*)Tok);
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void PopBrace (Collection* C)
|
||||
/* Close the latest open parenthesis/bracket/curly brace */
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
CollPop (C);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CloseBrace (Collection* C, token_t Tok)
|
||||
/* Consume a closing parenthesis/bracket/curly brace if it is matched with an
|
||||
** opening one to close and return 0, or bail out and return -1 if it is not
|
||||
** matched.
|
||||
*/
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
token_t LastTok = (token_t)CollLast (C);
|
||||
if (LastTok == Tok) {
|
||||
CollPop (C);
|
||||
NextToken ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SmartErrorSkip (int TillEnd)
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If TillEnd == 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached. The closing
|
||||
** curly brace is consumed in the former case.
|
||||
**
|
||||
** - If TillEnd != 0:
|
||||
** Skip tokens until a right curly brace or semicolon is reached and consumed
|
||||
** while there are no open parentheses/brackets/curly braces, or until an EOF
|
||||
** is reached anytime. Any open parenthesis/bracket/curly brace is considered
|
||||
** to be closed by consuming a right parenthesis/bracket/curly brace even if
|
||||
** they didn't match.
|
||||
**
|
||||
** - Return -1:
|
||||
** If this exits at a semicolon or unpaired right parenthesis/bracket/curly
|
||||
** brace while there are still open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 0:
|
||||
** If this exits as soon as it reaches an EOF;
|
||||
** Or if this exits right after consuming a semicolon or right curly brace
|
||||
** while there are no open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 1:
|
||||
** If this exits at a non-EOF without consuming it.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI,
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!TillEnd) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
PopBrace (&C);
|
||||
NextToken ();
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!TillEnd) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
PopBrace (&C);
|
||||
NextToken ();
|
||||
}
|
||||
if (CollCount (&C) == 0) {
|
||||
/* We consider this as a terminator as well */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_COMMA:
|
||||
if (CollCount (&C) == 0 && !TillEnd) {
|
||||
Res = 1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_SEMI:
|
||||
if (CollCount (&C) == 0) {
|
||||
if (TillEnd) {
|
||||
NextToken ();
|
||||
Res = 0;
|
||||
} else {
|
||||
Res = 1;
|
||||
}
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_CEOF:
|
||||
/* We cannot consume this */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SimpleErrorSkip (void)
|
||||
/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace
|
||||
** is reached. Return 0 If this exits at an EOF. Otherwise return -1.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = {
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
/* Found a terminator */
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_CEOF:
|
||||
/* We cannot go any farther */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Consume (token_t Token, const char* ErrorMsg)
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
** message. Returns true if the token was found and false otherwise.
|
||||
|
@ -310,6 +310,40 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount);
|
||||
** This routine is used for error recovery.
|
||||
*/
|
||||
|
||||
int SmartErrorSkip (int TillEnd);
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If TillEnd == 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached. The closing
|
||||
** curly brace is consumed in the former case.
|
||||
**
|
||||
** - If TillEnd != 0:
|
||||
** Skip tokens until a right curly brace or semicolon is reached and consumed
|
||||
** while there are no open parentheses/brackets/curly braces, or until an EOF
|
||||
** is reached anytime. Any open parenthesis/bracket/curly brace is considered
|
||||
** to be closed by consuming a right parenthesis/bracket/curly brace even if
|
||||
** they didn't match.
|
||||
**
|
||||
** - Return -1:
|
||||
** If this exits at a semicolon or unpaired right parenthesis/bracket/curly
|
||||
** brace while there are still open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 0:
|
||||
** If this exits as soon as it reaches an EOF;
|
||||
** Or if this exits right after consuming a semicolon or right curly brace
|
||||
** while there are no open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 1:
|
||||
** If this exits at a non-EOF without consuming it.
|
||||
*/
|
||||
|
||||
int SimpleErrorSkip (void);
|
||||
/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace
|
||||
** is reached. Return 0 If this exits at an EOF. Otherwise return -1.
|
||||
*/
|
||||
|
||||
int Consume (token_t Token, const char* ErrorMsg);
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
** message. Returns true if the token was found and false otherwise.
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
|
||||
|
||||
void ParseStaticAssert ()
|
||||
void ParseStaticAssert (void)
|
||||
{
|
||||
/*
|
||||
** static_assert-declaration ::=
|
||||
@ -53,20 +53,23 @@ void ParseStaticAssert ()
|
||||
** _Static_assert ( constant-expression , string-literal ) ;
|
||||
*/
|
||||
ExprDesc Expr;
|
||||
int failed;
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
int failed = 0;
|
||||
|
||||
/* Skip the _Static_assert token itself */
|
||||
CHECK (CurTok.Tok == TOK_STATIC_ASSERT);
|
||||
NextToken ();
|
||||
|
||||
/* We expect an opening paren */
|
||||
if (!ConsumeLParen ()) {
|
||||
return;
|
||||
if (ConsumeLParen ()) {
|
||||
/* Parse assertion condition */
|
||||
Expr = NoCodeConstAbsIntExpr (hie1);
|
||||
failed = !Expr.IVal;
|
||||
}
|
||||
|
||||
/* Parse assertion condition */
|
||||
Expr = NoCodeConstAbsIntExpr (hie1);
|
||||
failed = !Expr.IVal;
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* If there is a comma, we also have an error message. The message is optional because we
|
||||
** support the C2X syntax with only an expression.
|
||||
@ -84,19 +87,16 @@ void ParseStaticAssert ()
|
||||
/* String literal */
|
||||
if (CurTok.Tok != TOK_SCONST) {
|
||||
Error ("String literal expected for static_assert message");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Issue an error including the message if the static_assert failed. */
|
||||
if (failed) {
|
||||
Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal));
|
||||
}
|
||||
|
||||
/* Issue an error including the message if the static_assert failed. */
|
||||
if (failed) {
|
||||
Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal));
|
||||
}
|
||||
|
||||
/* Consume the string constant, now that we don't need it anymore.
|
||||
** This should never fail since we checked the token type above.
|
||||
*/
|
||||
if (!Consume (TOK_SCONST, "String literal expected")) {
|
||||
return;
|
||||
/* Consume the string constant, now that we don't need it anymore.
|
||||
** This should never fail since we checked the token type above.
|
||||
*/
|
||||
Consume (TOK_SCONST, "String literal expected");
|
||||
}
|
||||
} else {
|
||||
/* No message. */
|
||||
@ -105,7 +105,24 @@ void ParseStaticAssert ()
|
||||
}
|
||||
}
|
||||
|
||||
/* Closing paren and semi-colon needed */
|
||||
ConsumeRParen ();
|
||||
ConsumeSemi ();
|
||||
/* The assertion failure error is not a syntax error */
|
||||
if (failed) {
|
||||
++PrevErrorCount;
|
||||
}
|
||||
|
||||
if (PrevErrorCount == ErrorCount) {
|
||||
/* Closing paren needed */
|
||||
ConsumeRParen ();
|
||||
}
|
||||
|
||||
if (PrevErrorCount == ErrorCount) {
|
||||
/* Must be followed by a semicolon */
|
||||
ConsumeSemi ();
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
/* Try some smart error recovery */
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
@ -321,15 +321,9 @@ void TypeCast (ExprDesc* Expr)
|
||||
{
|
||||
Type NewType[MAXTYPELEN];
|
||||
|
||||
/* Skip the left paren */
|
||||
NextToken ();
|
||||
|
||||
/* Read the type */
|
||||
/* Read the type enclosed in parentheses */
|
||||
ParseType (NewType);
|
||||
|
||||
/* Closing paren */
|
||||
ConsumeRParen ();
|
||||
|
||||
/* Read the expression we have to cast */
|
||||
hie10 (Expr);
|
||||
|
||||
|
25
test/err/type-name-extra-identifier.c
Normal file
25
test/err/type-name-extra-identifier.c
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2023 The cc65 Authors
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
Test of type name with extra identifier
|
||||
*/
|
||||
|
||||
int a = sizeof (int b);
|
@ -1,3 +1,3 @@
|
||||
bug1889-missing-identifier.c:3: Error: Identifier expected
|
||||
bug1889-missing-identifier.c:3: Error: ';' expected
|
||||
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
|
||||
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature
|
||||
bug1889-missing-identifier.c:4: Error: Identifier expected
|
||||
|
Loading…
x
Reference in New Issue
Block a user