]> git.draconx.ca Git - cdecl99.git/commitdiff
libcdecl: Consolidate most error messages.
authorNick Bowler <nbowler@draconx.ca>
Fri, 9 Jun 2023 14:43:36 +0000 (10:43 -0400)
committerNick Bowler <nbowler@draconx.ca>
Fri, 16 Jun 2023 04:10:13 +0000 (00:10 -0400)
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.

Makefile.am
src/cdecl-internal.h
src/cdecl.h
src/error.c
src/parse-decl.c
src/parse.y
src/scan.l
t/cdeclerr.c

index 14ef3b10fef8d6e9ac5f794cb9b3e3ebfc493801..1673167297516428a03083512201b7e50f8dd638 100644 (file)
@@ -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 $@
index 1fba6146e5d842253b68b332014cf005990de9a9..713e1374e9a92c696e16c2f8ccfc84e12899a511 100644 (file)
@@ -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);
index 0846cfd5c6780a96071141b05a71cf99baa23e1c..20a94f2b2c4bb4a2305a1952045e57d877b51204 100644 (file)
@@ -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,
index 8b9f04e55cd4f0de103283d6f707362366ca4483..ef9e4757977621d805b35f6d84174f84feab32ae 100644 (file)
@@ -29,6 +29,8 @@
 #include <glthread/lock.h>
 #include <glthread/tls.h>
 
+#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);
 
index 1b38153216e16b588324318c851a3c660f327a5a..7e181c082f0f1610c3498f21604293b4ec9478d3 100644 (file)
@@ -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;
                }
        }
index bf2a596b1a4a037fee49fe36cd724454e11d8f27..6faf2d2b8b0d6eb4755491a43952b4659ebea9a4 100644 (file)
@@ -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)
index 1688873eea7a98744f68041bd16a858cfd82b332..5610dc30fb7c5b891c59fd4c042ab05b85cde04c 100644 (file)
@@ -29,6 +29,7 @@
 #include <ctype.h>
 #include "cdecl-internal.h"
 #include "cdecl.h"
+#include "errmsg.h"
 
 #if HAVE_STRTOUMAX
 /* Best case, implementation provides strtoumax. */
 #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;
 }
index 80193e434d389a13f2dafeecb7f83d182f5bc5aa..16ba1be809ea0b5abaf1071e72b0a40323430bf5 100644 (file)
@@ -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")) {