From 7574e36e95324afa8f50559838227d9660c2afae Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH] Improved error recovery with function declarations. Fixed some rare cases when a single file-scope error could get reapeated endlessly until the maximum total count of errors allowed is reached. --- src/cc65/compile.c | 32 ++++- src/cc65/declare.c | 160 +++++++++++++++++++---- src/cc65/declare.h | 12 +- test/ref/bug1889-missing-identifier.cref | 2 - 4 files changed, 170 insertions(+), 36 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index a591a60b8..02d37c53e 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -94,6 +94,8 @@ static void Parse (void) while (CurTok.Tok != TOK_CEOF) { DeclSpec Spec; + int NeedClean = 0; + unsigned PrevErrorCount = ErrorCount; /* Check for empty statements */ if (CurTok.Tok == TOK_SEMI) { @@ -144,7 +146,11 @@ static void Parse (void) Declarator Decl; /* Read the next declaration */ - ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + if (Decl.Ident[0] == '\0') { + Sym = 0; + goto NextDecl; + } /* Check if we must reserve storage for the variable. We do this, ** @@ -310,6 +316,7 @@ static void Parse (void) } +NextDecl: /* Check for end of declaration list */ if (CurTok.Tok == TOK_COMMA) { NextToken (); @@ -325,6 +332,7 @@ static void Parse (void) /* Function */ if (CurTok.Tok == TOK_SEMI) { /* Prototype only */ + NeedClean = 0; NextToken (); } else if (CurTok.Tok == TOK_LCURLY) { /* ISO C: The type category in a function definition cannot be @@ -337,6 +345,7 @@ static void Parse (void) } /* Parse the function body anyways */ + NeedClean = 0; NewFunc (Sym, FuncDef); /* Make sure we aren't omitting any work */ @@ -345,10 +354,27 @@ static void Parse (void) } else { - /* Must be followed by a semicolon */ - ConsumeSemi (); + if (Sym) { + /* Must be followed by a semicolon */ + if (CurTok.Tok != TOK_SEMI) { + NeedClean = -1; + } + ConsumeSemi (); + } } + + /* Try some smart error recovery */ + if (PrevErrorCount != ErrorCount && NeedClean < 0) { + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_SEMI, TOK_RCURLY }; + + SmartErrorSkip (); + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + if (CurTok.Tok == TOK_SEMI || CurTok.Tok == TOK_RCURLY) { + NextToken (); + } + } } /* Done with deferred operations */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 1d60053e9..31a0df2c3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -83,6 +83,104 @@ 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 (void) +/* Try some smart error recovery. Skip tokens until either a comma or semicolon +** that is not enclosed in an open parenthesis/bracket/curly brace, or until an +** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is +** the former case, or -1 if it is the latter case. */ +{ + 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)) { + Res = -1; + goto ExitPoint; + } + break; + + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok)) { + Res = -1; + goto ExitPoint; + } else if (CollCount (&C) == 0) { + goto ExitPoint; + } + break; + + case TOK_COMMA: + if (CollCount (&C) == 0) { + goto ExitPoint; + } + NextToken (); + break; + + case TOK_SEMI: + case TOK_CEOF: + Res = -1; + 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 */ { @@ -1537,6 +1635,12 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp *SignednessSpecified = 1; } break; + } else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + /* Treat this identifier as an unknown type */ + Error ("Unknown type name '%s'", CurTok.Ident); + TypeCopy (Spec->Type, type_int); + NextToken (); + break; } } else { /* This is a label. Use the default type flag to end the loop @@ -1618,14 +1722,13 @@ static void ParseOldStyleParamList (FuncDesc* F) NextToken (); } else { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_RPAREN, TOK_SEMI }; - /* Not a parameter name */ Error ("Identifier expected for parameter name"); /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + if (SmartErrorSkip () < 0) { + break; + } } /* Check for more parameters */ @@ -1711,12 +1814,9 @@ static void ParseOldStyleParamList (FuncDesc* F) ConsumeSemi (); } - if (PrevErrorCount != ErrorCount) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI }; - + if (PrevErrorCount != ErrorCount && CurTok.Tok != TOK_LCURLY) { /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + SmartErrorSkip (); } } @@ -1731,6 +1831,7 @@ static void ParseAnsiParamList (FuncDesc* F) DeclSpec Spec; Declarator Decl; SymEntry* Param; + unsigned PrevErrorCount = ErrorCount; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { @@ -1798,6 +1899,13 @@ static void ParseAnsiParamList (FuncDesc* F) /* Count arguments */ ++F->ParamCount; + if (PrevErrorCount != ErrorCount) { + /* Try some smart error recovery */ + if (SmartErrorSkip () < 0) { + break; + } + } + /* Check for more parameters */ if (CurTok.Tok == TOK_COMMA) { NextToken (); @@ -2065,8 +2173,10 @@ Type* ParseType (Type* T) -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) -/* Parse a variable, type or function declarator */ +int ParseDecl (const 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. +*/ { /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; @@ -2117,7 +2227,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } /* Check a few pre-C99 things */ - if ((Spec->Flags & DS_DEF_TYPE) != 0) { + if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) { /* 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 @@ -2129,7 +2239,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - /* For anthing that is not a function or typedef, check for an implicit + /* For anything that is not a function or typedef, check for an implicit ** int declaration. */ if ((D->StorageClass & SC_FUNC) != SC_FUNC && @@ -2144,22 +2254,7 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (PrevErrorCount != ErrorCount) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; - - /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); - - /* Skip curly braces */ - if (CurTok.Tok == TOK_LCURLY) { - static const token_t CurlyToken[] = { TOK_RCURLY }; - SkipTokens (CurlyToken, sizeof (CurlyToken) / sizeof (CurlyToken[0])); - NextToken (); - } else if (CurTok.Tok == TOK_RCURLY) { - NextToken (); - } - - if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + 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; @@ -2183,7 +2278,14 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } AnonName (D->Ident, Level); } + + /* Try some smart error recovery */ + if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { + return SmartErrorSkip (); + } } + + return 0; } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index ca1b88165..add86594d 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -128,11 +128,19 @@ typedef enum { +int SmartErrorSkip (void); +/* Try some smart error recovery. Skip tokens until either a comma or semicolon +** that is not enclosed in an open parenthesis/bracket/curly brace, or until an +** unpaired right parenthesis/bracket/curly brace is reached. Return 0 if it is +** the former case, or -1 if it is the latter case. */ + Type* ParseType (Type* Type); /* Parse a complete type specification */ -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); -/* Parse a variable, type or function declarator */ +int ParseDecl (const 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. +*/ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage); /* Parse a declaration specification */ diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index acaf53f94..534c6aaba 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,5 +1,3 @@ bug1889-missing-identifier.c:3: Error: Identifier expected bug1889-missing-identifier.c:3: Error: ';' expected -bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature bug1889-missing-identifier.c:4: Error: Identifier expected -bug1889-missing-identifier.c:4: Warning: Implicit 'int' is an obsolete feature