Merge pull request #2296 from acqn/Diagnostics

[cc65] Fixed and improved diagnostics regarding type names and declarations
This commit is contained in:
Bob Andrews 2023-12-10 22:32:33 +01:00 committed by GitHub
commit cc5e9c38ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 709 additions and 409 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 */

View File

@ -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.

View File

@ -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.

View File

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

View File

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

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

View File

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