From: Nick Bowler Date: Fri, 9 Jun 2023 14:43:36 +0000 (-0400) Subject: libcdecl: Consolidate most error messages. X-Git-Tag: v1.3~165 X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/commitdiff_plain/a9ae295484868287ff1b8f2d613fa4c010c06891 libcdecl: Consolidate most error messages. Almost every error message returned by the library is a fixed string describing some particular syntax problem. Keep this list of strings in one place, and add a new internal helper to report one of these errors. Furthermore, reduce the amount of distinct error codes returned to the user to (once again) just two, as the current plethora of codes seems completely pointless. --- diff --git a/Makefile.am b/Makefile.am index 14ef3b1..1673167 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,7 +43,7 @@ libcdecl_la_LDFLAGS = -export-symbols-regex '^cdecl_([[:lower:]]|_gl_)' \ -no-undefined -version-info 1:0:0 libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/output.c \ src/explain.c src/declare.c src/error.c src/normalize.c \ - src/cdecl-internal.h + src/cdecl-internal.h src/errmsg.h libcdecl_la_LIBADD = $(shared_gl_objects) $(LTLIBINTL) $(LIBTHREAD) EXTRA_libcdecl_la_DEPENDENCIES = $(shared_gl_objects) $(libcdecl_la_OBJECTS): $(gnulib_headers) @@ -64,8 +64,9 @@ dummy $(static_gl_objects): $(gnulib_headers) bin_PROGRAMS = cdecl99 cdecl99_SOURCES = common/src/help.c src/commands.c src/cdecl99.h -cdecl99_LDADD = $(libmain_a_OBJECTS) $(libexec_a_OBJECTS) libcdecl.la \ - libgnu.a $(LTLIBINTL) $(LTLIBREADLINE) +EXTRA_cdecl99_DEPENDENCIES = $(libmain_a_OBJECTS) $(libexec_a_OBJECTS) +cdecl99_LDADD = $(EXTRA_cdecl99_DEPENDENCIES) libcdecl.la libgnu.a \ + $(LTLIBINTL) $(LTLIBREADLINE) $(cdecl99_OBJECTS): $(gnulib_headers) if USE_NLS @@ -103,11 +104,13 @@ t_rng_test_LDADD = $(TEST_LIBS) $(t_rng_test_OBJECTS): $(gnulib_headers) EXTRA_DIST += t/xos256p.c -src/parse.lo: src/scan.h -src/scan.lo: src/parse.h -src/parse-decl.lo: src/scan.h src/parse.h src/typemap.h +src/parse.lo: src/scan.h src/errmsg.h +src/scan.lo: src/parse.h src/errmsg.h +src/parse-decl.lo: src/scan.h src/parse.h src/typemap.h src/errmsg.h src/output.lo: src/specstr.h +src/error.lo: src/errmsg.h t/declgen.$(OBJEXT): t/typegen.h +t/cdeclerr.$(OBJEXT): src/errmsg.h check_PROGRAMS += t/cdeclerr t_cdeclerr_SOURCES = common/src/tap.c t/cdeclerr.c @@ -206,7 +209,7 @@ $(OPTFILES:.opt=.h): $(DX_BASEDIR)/scripts/gen-options.awk MAINTAINERCLEANFILES += $(OPTFILES:.opt=.h) EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-options.awk $(OPTFILES) -STRFILES = src/commands.str +STRFILES = src/commands.str src/errmsg.str .str.h: $(AM_V_GEN) $(AWK) -f $(DX_BASEDIR)/scripts/gen-strtab.awk $< >$@.tmp $(AM_V_at) mv -f $@.tmp $@ diff --git a/src/cdecl-internal.h b/src/cdecl-internal.h index 1fba614..713e137 100644 --- a/src/cdecl-internal.h +++ b/src/cdecl-internal.h @@ -1,6 +1,6 @@ /* * Internal declarations for libcdecl. - * Copyright © 2021 Nick Bowler + * Copyright © 2021, 2023 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 @@ -35,10 +35,9 @@ static inline void cdecl__init_i18n(void) } #endif -const char *cdecl__strerror(unsigned code); -void cdecl__err(unsigned code, ...); +void cdecl__err(unsigned code, const char *fmt, ...); +void cdecl__errmsg(unsigned msg); -unsigned long cdecl__build_typemap(struct cdecl_declspec *s); struct cdecl_declspec *cdecl__normalize_specs(struct cdecl_declspec *specs); size_t cdecl__advance_(char **buf, size_t *n, size_t amount); diff --git a/src/cdecl.h b/src/cdecl.h index 0846cfd..20a94f2 100644 --- a/src/cdecl.h +++ b/src/cdecl.h @@ -1,5 +1,5 @@ /* - * Copyright © 2011 Nick Bowler + * Copyright © 2011, 2021, 2023 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 @@ -119,6 +119,8 @@ static inline _Bool cdecl_is_abstract(const struct cdecl_declarator *d) enum { CDECL_ENOMEM = 1, CDECL_ENOPARSE, + + /* Obsolete error codes (no longer returned by the library) */ CDECL_EBADARRAY, CDECL_EBADDECL, CDECL_EBADPARAMS, diff --git a/src/error.c b/src/error.c index 8b9f04e..ef9e475 100644 --- a/src/error.c +++ b/src/error.c @@ -29,6 +29,8 @@ #include #include +#include "errmsg.h" + gl_once_define(static, tls_initialized); static gl_tls_key_t tls_key; @@ -49,10 +51,26 @@ static void free_err(void *err) free(err); } +static void set_err(unsigned code, struct cdecl_error *err) +{ + static const char errmsgs[] = STRTAB_INITIALIZER; + + switch (code) { + case CDECL__ENOMEM: + err->code = CDECL_ENOMEM; + break; + default: + err->code = CDECL_ENOPARSE; + break; + } + + err->str = _(&errmsgs[code]); +} + static void initialize(void) { cdecl__init_i18n(); - err_no_mem.str = _("failed to allocate memory"); + set_err(CDECL__ENOMEM, &err_no_mem); gl_tls_key_init(tls_key, free_err); } @@ -91,14 +109,25 @@ static struct err_state *get_err_state(void) } /* - * cdecl__err(CDECL_ENOMEM); - * cdecl__err(code, fmt, ...); - * + * Set the library error to one of the preset messages defined in errmsg.h + * (CDECL__Exxx). + */ +void cdecl__errmsg(unsigned msg) +{ + struct err_state *state; + + state = get_err_state(); + if (!state) + return; + + set_err(msg, &state->err); +} + +/* * Sets the library error to code, with a printf-style error string. */ -void cdecl__err(unsigned code, ...) +void cdecl__err(unsigned code, const char *fmt, ...) { - const char *fmt; struct err_state *state; int rc, try = 0; va_list ap; @@ -107,14 +136,8 @@ void cdecl__err(unsigned code, ...) if (!state) return; - if (code == CDECL_ENOMEM) { - state->err.code = code; - state->err.str = err_no_mem.str; - return; - } retry: - va_start(ap, code); - fmt = va_arg(ap, const char *); + va_start(ap, fmt); rc = vsnprintf(state->str, state->nstr, fmt, ap); va_end(ap); diff --git a/src/parse-decl.c b/src/parse-decl.c index 1b38153..7e181c0 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -25,6 +25,7 @@ #include "cdecl-internal.h" #include "parse.h" #include "scan.h" +#include "errmsg.h" /* * We can represent type specifiers as a bitmap, which gives us a finite @@ -63,9 +64,9 @@ static int valid_typespec(struct cdecl_declspec *s) if (map & bit) { if (bit == 1ul << MAP_LLONG_BIT) - cdecl__err(CDECL_EBADTYPE, _("too many long specifiers")); + cdecl__errmsg(CDECL__ETOOLONG); else - cdecl__err(CDECL_EBADTYPE, _("duplicate type specifier")); + cdecl__errmsg(CDECL__EDUPTYPE); return false; } map |= bit; @@ -75,9 +76,9 @@ static int valid_typespec(struct cdecl_declspec *s) return true; if (map == 0) - cdecl__err(CDECL_EBADTYPE, _("no type specified")); + cdecl__errmsg(CDECL__ENOTYPE); else - cdecl__err(CDECL_EBADTYPE, _("invalid type specified")); + cdecl__errmsg(CDECL__EBADTYPE); return false; } @@ -103,23 +104,23 @@ static bool valid_declspecs(struct cdecl *decl, bool top) if (c->type == CDECL_TYPE_VOID && (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_ARRAY)) { - cdecl__err(CDECL_EBADTYPE, _("invalid declaration of type void")); + cdecl__errmsg(CDECL__EBADVOID); return false; } continue; case CDECL_SPEC_STOR: if (top && abstract) { - cdecl__err(CDECL_EBADSTOR, _("type names cannot have storage-class specifiers")); + cdecl__errmsg(CDECL__ETYPESTOR); return false; } if (!top && c->type != CDECL_STOR_REGISTER) { - cdecl__err(CDECL_EBADSTOR, _("function parameters may only have register storage")); + cdecl__errmsg(CDECL__EFUNCSTOR); return false; } if (++num_storage > 1) { - cdecl__err(CDECL_EBADSTOR, _("too many storage-class specifiers")); + cdecl__errmsg(CDECL__EMANYSTOR); return false; } break; @@ -129,20 +130,16 @@ static bool valid_declspecs(struct cdecl *decl, bool top) * pointer qualifier list, which isn't checked here. */ if (c->type == CDECL_QUAL_RESTRICT) { - cdecl__err(CDECL_EBADQUAL, _("only pointer types can be restrict-qualified")); + cdecl__errmsg(CDECL__EBADQUAL); return false; } break; case CDECL_SPEC_FUNC: - if (abstract) { - cdecl__err(CDECL_ENOTFUNC, _("type names cannot have function specifiers")); + if (abstract || !top || d->type != CDECL_DECL_FUNCTION) { + cdecl__errmsg(CDECL__ENOTFUNC); return false; } - if (!top || d->type != CDECL_DECL_FUNCTION) { - cdecl__err(CDECL_ENOTFUNC, _("only function declarations can have function specifiers")); - return false; - } break; default: assert(0); @@ -280,7 +277,7 @@ reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d) } if (d->child->type != CDECL_DECL_NULL) { - cdecl__err(CDECL_EBADPARAMS, _("invalid function parameter")); + cdecl__errmsg(CDECL__EBADPARAM); return -1; } @@ -304,7 +301,7 @@ reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d) if (decl->type == CDECL_DECL_FUNCTION && decl->child->type == CDECL_DECL_NULL && !function_is_reducible(decl)) { - cdecl__err(CDECL_EBADPARAMS, _("too many parentheses in function")); + cdecl__errmsg(CDECL__EMANYPAREN); return -1; } @@ -339,13 +336,13 @@ check_parameters(struct cdecl_declarator **p, struct cdecl_declarator *d) continue; if (spec != param->specifiers || spec->next != NULL) { - cdecl__err(CDECL_EVOIDPARAM, _("void parameter cannot have extra specifiers")); + cdecl__errmsg(CDECL__EVOIDPARAM); return -1; } else if (d->u.function.parameters->next) { - cdecl__err(CDECL_EVOIDPARAM, _("void parameter must stand alone")); + cdecl__errmsg(CDECL__EVOIDPARAM); return -1; } else if (d->u.function.variadic) { - cdecl__err(CDECL_EVOIDPARAM, _("variadic function cannot have void parameter")); + cdecl__errmsg(CDECL__EVOIDPARAM); return -1; } } @@ -366,10 +363,10 @@ check_rettypes(struct cdecl_declarator **p, struct cdecl_declarator *d) switch (d->type) { case CDECL_DECL_FUNCTION: - cdecl__err(CDECL_EBADRETURN, _("functions cannot return functions")); + cdecl__errmsg(CDECL__ERETFUNC); return -1; case CDECL_DECL_ARRAY: - cdecl__err(CDECL_EBADRETURN, _("functions cannot return arrays")); + cdecl__errmsg(CDECL__ERETARRAY); return -1; } @@ -384,7 +381,7 @@ check_arrays(struct cdecl_declarator **p, struct cdecl_declarator *d) switch (d->type) { case CDECL_DECL_FUNCTION: - cdecl__err(CDECL_EBADARRAY, _("array members cannot be functions")); + cdecl__errmsg(CDECL__EFUNCARRAY); return -1; } @@ -425,7 +422,7 @@ check_qualifiers(struct cdecl_declarator **p, struct cdecl_declarator *d) for (spec = ptr->qualifiers; spec; spec = spec->next) { if (spec->type == CDECL_QUAL_RESTRICT && d->type == CDECL_DECL_FUNCTION) { - cdecl__err(CDECL_EBADPOINTER, _("function pointers cannot be restrict-qualified")); + cdecl__errmsg(CDECL__ERESTRICTFUNC); return -1; } } @@ -534,7 +531,7 @@ struct cdecl *cdecl_parse_decl(const char *declstr) if (cdecl_is_abstract(i->declarators) && (i != decl || i->next)) { - cdecl__err(CDECL_EBADDECL, _("mixing type names and declarations is not allowed")); + cdecl__errmsg(CDECL__EDECLTYPE); goto err; } } diff --git a/src/parse.y b/src/parse.y index bf2a596..6faf2d2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1,7 +1,7 @@ %code top { /* * Parser for C declarations. - * Copyright © 2011-2012, 2021 Nick Bowler + * Copyright © 2011-2012, 2021, 2023 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 @@ -34,6 +34,7 @@ #include "scan.h" #include "cdecl.h" #include "cdecl-internal.h" +#include "errmsg.h" #define FAIL(msg) do { \ yyerror(&yylloc, NULL, NULL, msg); \ @@ -43,7 +44,7 @@ #define ALLOC(ptr, size) do { \ (ptr) = malloc(size); \ if (!(ptr)) { \ - cdecl__err(CDECL_ENOMEM); \ + cdecl__errmsg(CDECL__ENOMEM); \ YYERROR; \ } \ } while (0) diff --git a/src/scan.l b/src/scan.l index 1688873..5610dc3 100644 --- a/src/scan.l +++ b/src/scan.l @@ -29,6 +29,7 @@ #include #include "cdecl-internal.h" #include "cdecl.h" +#include "errmsg.h" #if HAVE_STRTOUMAX /* Best case, implementation provides strtoumax. */ @@ -44,15 +45,10 @@ #define STRTOUMAX strtoul #endif -#define lex_error(...) do { \ - cdecl__err(CDECL_ENOPARSE, __VA_ARGS__); \ - return T_LEX_ERROR; \ -} while(0) - #define dup_token() do { \ yylval->strval = malloc(yyleng+1); \ if (!yylval->strval) { \ - cdecl__err(CDECL_ENOMEM); \ + cdecl__errmsg(CDECL__ENOMEM); \ return T_LEX_ERROR; \ } \ strcpy(yylval->strval, yytext); \ @@ -117,10 +113,14 @@ INTEGER 0x[[:xdigit:]]+|0[0-7]+|[[:digit:]]+ errno = 0; yylval->uintval = STRTOUMAX(yytext, &end, 0); - if (errno == ERANGE) - lex_error(_("integer constant out of range")); - if (*end) - lex_error(_("invalid integer constant")); + if (errno == ERANGE) { + cdecl__errmsg(CDECL__ERANGE); + return T_LEX_ERROR; + } + if (*end) { + cdecl__errmsg(CDECL__EBADINT); + return T_LEX_ERROR; + } return T_UINT; } @@ -165,5 +165,6 @@ INTEGER 0x[[:xdigit:]]+|0[0-7]+|[[:digit:]]+ } } - lex_error(_("syntax error, unexpected '%s'"), buf); + cdecl__err(CDECL_ENOPARSE, _("syntax error, unexpected '%s'"), buf); + return T_LEX_ERROR; } diff --git a/t/cdeclerr.c b/t/cdeclerr.c index 80193e4..16ba1be 100644 --- a/t/cdeclerr.c +++ b/t/cdeclerr.c @@ -24,6 +24,7 @@ #include "cdecl-internal.h" #include "cdecl.h" +#include "errmsg.h" #include "tap.h" #if ENABLE_NLS @@ -111,8 +112,8 @@ static void check_enomem() const char expmsg[] = "failed to allocate memory"; const struct cdecl_error *err; - tap_diag("cdecl__err(CDECL_ENOMEM)"); - cdecl__err(CDECL_ENOMEM); + tap_diag("cdecl__errmsg(CDECL__ENOMEM)"); + cdecl__errmsg(CDECL__ENOMEM); err = cdecl_get_error(); if (!tap_result(err->code == CDECL_ENOMEM, "returned error code")) {