From ac04394254be9bc7d0ec58c572d5d07c6e808792 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 27 Nov 2023 20:39:15 +0800 Subject: [PATCH] Fixed and improved diagnostics about declaration errors. --- src/cc65/declare.c | 110 +++++++++++++++++++++++++++------------------ src/cc65/declare.h | 23 ++++++++-- 2 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4bfd52c5e..1d60053e9 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1766,7 +1766,7 @@ static void ParseAnsiParamList (FuncDesc* F) /* Allow parameters without a name, but remember if we had some to ** eventually print an error message later. */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + ParseDecl (&Spec, &Decl, DM_ACCEPT_PARAM_IDENT); if (Decl.Ident[0] == '\0') { /* Unnamed symbol. Generate a name that is not user accessible, @@ -1886,7 +1886,7 @@ static FuncDesc* ParseFuncDecl (void) -static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +static declmode_t DirectDecl (const 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 @@ -1903,61 +1903,49 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Skip the star */ NextToken (); + /* A pointer type cannot be used as an empty declaration */ + if (Mode == DM_ACCEPT_IDENT) { + Mode = DM_NEED_IDENT; + } + /* Allow const, restrict, and volatile qualifiers */ Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); /* Parse the type that the pointer points to */ - DirectDecl (Spec, D, Mode); + Mode = DirectDecl (Spec, D, Mode); /* Add the type */ AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); - return; + return Mode; } if (CurTok.Tok == TOK_LPAREN) { NextToken (); - DirectDecl (Spec, D, Mode); - ConsumeRParen (); - } else { - /* Things depend on Mode now: - ** - Mode == DM_NEED_IDENT means: - ** we *must* have a type and a variable identifer. - ** - Mode == DM_NO_IDENT means: - ** we must have a type but no variable identifer - ** (if there is one, it's not read). - ** - Mode == DM_ACCEPT_IDENT means: - ** we *may* have an identifier. If there is an identifier, - ** it is read, but it is no error, if there is none. + /* An empty declaration cannot contain parentheses where an identifier + ** would show up if it were a non-empty declaration. */ - if (Mode == DM_NO_IDENT) { - D->Ident[0] = '\0'; - } else if (CurTok.Tok == TOK_IDENT) { - strcpy (D->Ident, CurTok.Ident); - NextToken (); - } else { - if (Mode == DM_NEED_IDENT) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, TOK_LCURLY, TOK_RCURLY }; - - Error ("Identifier expected"); - - /* 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 (); - } - } - D->Ident[0] = '\0'; + if (Mode == DM_ACCEPT_IDENT) { + Mode = DM_NEED_IDENT; + } + Mode = DirectDecl (Spec, D, Mode); + ConsumeRParen (); + } else if (CurTok.Tok == TOK_IDENT) { + strcpy (D->Ident, CurTok.Ident); + NextToken (); + } else { + D->Ident[0] = '\0'; + if (Mode == DM_NEED_IDENT) { + 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 */ @@ -2043,6 +2031,8 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) if (Qualifiers & T_QUAL_CDECL) { Error ("Invalid '__cdecl__' qualifier"); } + + return Mode; } @@ -2154,12 +2144,44 @@ void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } if (PrevErrorCount != ErrorCount) { - /* Make the declaration fictitious if is is not parsed correctly */ - D->StorageClass |= SC_FICTITIOUS; + /* 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') { + /* Make the declaration fictitious if is is not parsed correctly */ + D->StorageClass |= SC_FICTITIOUS; + /* Use a fictitious name for the identifier if it is missing */ - AnonName (D->Ident, "global"); + const char* Level = ""; + + switch (GetLexicalLevel ()) { + case LEX_LEVEL_GLOBAL: + Level = "global"; + break; + case LEX_LEVEL_FUNCTION: + case LEX_LEVEL_BLOCK: + Level = "local"; + break; + case LEX_LEVEL_STRUCT: + Level = "field"; + break; + default: + Level = "unknown"; + break; + } + AnonName (D->Ident, Level); } } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 89d7be7ea..ca1b88165 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -98,11 +98,26 @@ struct Declarator { unsigned Index; /* Used to build Type */ }; -/* Modes for ParseDecl */ +/* Modes for ParseDecl: +** - DM_NEED_IDENT means: +** we *must* have a type and a variable identifer. +** - 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: +** 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, /* We must have an identifier */ - DM_NO_IDENT, /* We won't read an identifier */ - DM_ACCEPT_IDENT, /* We will accept an id if there is one */ + DM_NEED_IDENT, + DM_NO_IDENT, + DM_ACCEPT_IDENT, + DM_ACCEPT_PARAM_IDENT, } declmode_t;