diff --git a/src/cc65/compile.c b/src/cc65/compile.c index bef55e375..0dc75273d 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -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); } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 96e1f1074..076e94aa8 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -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)); } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 1ce764f7a..4cfc48c68 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -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); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a87335f42..963ea8bd6 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -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; diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 381477faa..b8738992f 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -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 */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 00dde9e83..6b5235679 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -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. diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index e6b788660..ccf3a8805 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -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. diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index abb2c57ca..9df9af7da 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -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); + } } diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e5b6749d6..76658502d 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -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); diff --git a/test/err/type-name-extra-identifier.c b/test/err/type-name-extra-identifier.c new file mode 100644 index 000000000..72de4778d --- /dev/null +++ b/test/err/type-name-extra-identifier.c @@ -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); diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 534c6aaba..2d92ff263 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -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