X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/96b00735351dd8e39fc5d71ff31afbdddd83ecba..0c3d696d84c1d76482d83e76e4c2b4218b028f1f:/src/parse-decl.c diff --git a/src/parse-decl.c b/src/parse-decl.c index a31a377..3c60dd2 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -1,6 +1,6 @@ /* * Parse and validate C declarations. - * Copyright © 2011-2012, 2020-2021, 2023 Nick Bowler + * Copyright © 2011-2012, 2020-2021, 2023-2024 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,8 @@ #include "scan.h" #include "errmsg.h" +static struct cdecl *fake_function_param(struct cdecl_declarator *); + /* * Allocate a "parse item", which is a union of several parse tree * structure types, together with a string buffer. The s_sz argument @@ -73,9 +75,10 @@ struct parse_item *cdecl__alloc_item(size_t s_sz) */ static int valid_typespec(struct cdecl_declspec *s) { + struct cdecl_declspec *c; unsigned long map = 0; - for (struct cdecl_declspec *c = s; c; c = c->next) { + for (c = s; c; c = c->next) { unsigned long bit; if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE) @@ -117,7 +120,7 @@ static int valid_typespec(struct cdecl_declspec *s) */ static bool valid_declspecs(struct cdecl *decl, bool top) { - struct cdecl_declspec *specs = decl->specifiers; + struct cdecl_declspec *c, *specs = decl->specifiers; struct cdecl_declarator *d = decl->declarators; bool abstract = cdecl_is_abstract(d); unsigned num_storage = 0; @@ -125,7 +128,7 @@ static bool valid_declspecs(struct cdecl *decl, bool top) if (!valid_typespec(specs)) return false; - for (struct cdecl_declspec *c = specs; c; c = c->next) { + for (c = specs; c; c = c->next) { switch (cdecl_spec_kind(c)) { case CDECL_SPEC_TYPE: if (c->type == CDECL_TYPE_VOID && @@ -176,6 +179,36 @@ static bool valid_declspecs(struct cdecl *decl, bool top) return true; } +/* + * Find the tree pointer which leads to the parameter's leaf node. + * + * Return a null pointer if the traversal locates a syntactic element which + * prevents function reduction. This occurs if the leaf node declares an + * identifier, or for nontrivial fake function parameters (see below). + */ +static struct cdecl_declarator **leaf_pointer(struct cdecl *param) +{ + struct cdecl_declarator *d, **p = ¶m->declarators; + + if ((param = fake_function_param(param->declarators))) { + if (param->declarators->type != CDECL_DECL_NULL) + return NULL; /* e.g. int (x (*)) */ + } + + while ((d = *p)->child) { + p = &d->child; + + if (fake_function_param(d->child)) + return NULL; /* e.g. int (x (*)[][1]) */ + } + + if (d->type != CDECL_DECL_NULL) + return NULL; /* e.g. int (x y) */ + + return p; +} + + /* * The C grammar leaves ambiguous some cases where parentheses represent a * function declarator or just parentheses. The language uses additional @@ -200,13 +233,10 @@ static bool valid_declspecs(struct cdecl *decl, bool top) static struct cdecl_declarator *reduce_function(struct cdecl *param) { - struct cdecl_declarator *d, **p = ¶m->declarators; struct parse_item *spec = (void *)param->specifiers; + struct cdecl_declarator *d, **p; - while ((d = *p)->child) - p = &d->child; - - if (d->type != CDECL_DECL_NULL) + if (!(p = leaf_pointer(param))) return NULL; /* @@ -259,7 +289,7 @@ simplify_functions(struct cdecl_declarator **p, struct cdecl_declarator *d) new = reduce_function(d->u.function.parameters); if (!new) - return 0; /* e.g. int (foo bar) */ + return 0; *p = new; free(d); @@ -500,9 +530,9 @@ static int forall_declarators(struct cdecl *decl, static struct cdecl *do_parse(const char *str, int english_mode) { + struct cdecl *decl = NULL; YY_BUFFER_STATE state; yyscan_t scanner; - struct cdecl *decl; #if YYDEBUG extern int cdecl__yydebug; @@ -514,8 +544,15 @@ static struct cdecl *do_parse(const char *str, int english_mode) return NULL; state = cdecl__yy_scan_string(str, scanner); - if (cdecl__yyparse(scanner, &decl) != 0) + if (cdecl__yyparse(scanner, &decl) != 0) { + /* + * If the input consists of a complete, valid declaration + * followed by some garbage, that parsed declaration will + * be output by the parser and we need to free it here. + */ + cdecl__free(decl); decl = NULL; + } cdecl__yy_delete_buffer(state, scanner); cdecl__yylex_destroy(scanner);