From 191ee4ba1fb090422b1ac2919d0e8dff11c178ba Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Mon, 18 Nov 2019 09:23:32 +0100 Subject: [PATCH] Add support for toggleable warnings --- Makefile | 1 + include/asm/main.h | 23 --- include/asm/warning.h | 52 +++++++ src/asm/asmy.y | 41 +++--- src/asm/charmap.c | 3 +- src/asm/constexpr.c | 5 +- src/asm/fstack.c | 3 +- src/asm/globlex.c | 5 +- src/asm/lexer.c | 1 + src/asm/main.c | 70 ++------- src/asm/output.c | 1 + src/asm/rpn.c | 6 +- src/asm/symbol.c | 3 +- src/asm/util.c | 1 + src/asm/warning.c | 240 +++++++++++++++++++++++++++++++ test/asm/correct-line-number.out | 4 +- test/asm/multiple-charmaps.out | 2 +- test/asm/overflow.out | 12 +- test/asm/strsub.out | 16 +-- test/asm/test.sh | 4 +- 20 files changed, 362 insertions(+), 131 deletions(-) create mode 100644 include/asm/warning.h create mode 100644 src/asm/warning.c diff --git a/Makefile b/Makefile index e36a6b93..351a1dbd 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ rgbasm_obj := \ src/asm/rpn.o \ src/asm/symbol.o \ src/asm/util.o \ + src/asm/warning.o \ src/extern/err.o \ src/extern/getopt.o \ src/extern/utf8decoder.o \ diff --git a/include/asm/main.h b/include/asm/main.h index 39857fe9..63812f5e 100644 --- a/include/asm/main.h +++ b/include/asm/main.h @@ -40,29 +40,6 @@ void opt_Push(void); void opt_Pop(void); void opt_Parse(char *s); -/* - * Used for errors that compromise the whole assembly process by affecting the - * folliwing code, potencially making the assembler generate errors caused by - * the first one and unrelated to the code that the assembler complains about. - * It is also used when the assembler goes into an invalid state (for example, - * when it fails to allocate memory). - */ -noreturn_ void fatalerror(const char *fmt, ...); - -/* - * Used for errors that make it impossible to assemble correctly, but don't - * affect the following code. The code will fail to assemble but the user will - * get a list of all errors at the end, making it easier to fix all of them at - * once. - */ -void yyerror(const char *fmt, ...); - -/* - * Used to warn the user about problems that don't prevent the generation of - * valid code. - */ -void warning(const char *fmt, ...); - #define YY_FATAL_ERROR fatalerror #ifdef YYLMAX diff --git a/include/asm/warning.h b/include/asm/warning.h new file mode 100644 index 00000000..89112e69 --- /dev/null +++ b/include/asm/warning.h @@ -0,0 +1,52 @@ +#ifndef WARNING_H +#define WARNING_H + +extern unsigned int nbErrors; + +enum WarningID { + WARNING_USER, + WARNING_OBSOLETE, + WARNING_BUILTIN_ARG, + WARNING_LARGE_CONSTANT, + WARNING_SHIFT, + WARNING_DIV, + WARNING_EMPTY_ENTRY, + WARNING_LONG_STR, + + NB_WARNINGS, + + /* Warnings past this point are "meta" warnings */ + WARNING_ALL = NB_WARNINGS, + WARNING_EXTRA, + WARNING_EVERYTHING, + + NB_WARNINGS_ALL +#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS) +}; + +void processWarningFlag(char const *flag); + +/* + * Used to warn the user about problems that don't prevent the generation of + * valid code. + */ +void warning(enum WarningID id, const char *fmt, ...); + +/* + * Used for errors that compromise the whole assembly process by affecting the + * following code, potencially making the assembler generate errors caused by + * the first one and unrelated to the code that the assembler complains about. + * It is also used when the assembler goes into an invalid state (for example, + * when it fails to allocate memory). + */ +noreturn_ void fatalerror(const char *fmt, ...); + +/* + * Used for errors that make it impossible to assemble correctly, but don't + * affect the following code. The code will fail to assemble but the user will + * get a list of all errors at the end, making it easier to fix all of them at + * once. + */ +void yyerror(const char *fmt, ...); + +#endif diff --git a/src/asm/asmy.y b/src/asm/asmy.y index b5e223f8..be554158 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -27,6 +27,7 @@ #include "asm/rpn.h" #include "asm/symbol.h" #include "asm/util.h" +#include "asm/warning.h" #include "extern/utf8decoder.h" @@ -482,7 +483,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) uint32_t curLen = 0; if (pos < 1) { - warning("STRSUB: Position starts at 1"); + warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1"); pos = 1; } @@ -500,7 +501,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) } if (!src[srcIndex]) - warning("STRSUB: Position %lu is past the end of the string", + warning(WARNING_BUILTIN_ARG, "STRSUB: Position %lu is past the end of the string", (unsigned long)pos); /* Copy from source to destination. */ @@ -517,7 +518,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) } if (curLen < len) - warning("STRSUB: Length too big: %lu", (unsigned long)len); + warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu", (unsigned long)len); /* Check for partial code point. */ if (state != 0) @@ -807,7 +808,7 @@ pushs : T_POP_PUSHS { out_PushSection(); } fail : T_POP_FAIL string { fatalerror("%s", $2); } ; -warn : T_POP_WARN string { warning("%s", $2); } +warn : T_POP_WARN string { warning(WARNING_USER, "%s", $2); } ; shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); } @@ -905,7 +906,7 @@ ds : T_POP_DS uconst db : T_POP_DB constlist_8bit_entry comma constlist_8bit { if (nListCountEmpty > 0) { - warning("Empty entry in list of 8-bit elements (treated as 0)."); + warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 8-bit elements (treated as 0)."); } } | T_POP_DB constlist_8bit_entry @@ -913,7 +914,7 @@ db : T_POP_DB constlist_8bit_entry comma constlist_8bit { dw : T_POP_DW constlist_16bit_entry comma constlist_16bit { if (nListCountEmpty > 0) { - warning("Empty entry in list of 16-bit elements (treated as 0)."); + warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 16-bit elements (treated as 0)."); } } | T_POP_DW constlist_16bit_entry @@ -921,7 +922,7 @@ dw : T_POP_DW constlist_16bit_entry comma constlist_16bit { dl : T_POP_DL constlist_32bit_entry comma constlist_32bit { if (nListCountEmpty > 0) { - warning("Empty entry in list of 32-bit elements (treated as 0)."); + warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 32-bit elements (treated as 0)."); } } | T_POP_DL constlist_32bit_entry @@ -957,7 +958,7 @@ import_list_entry : T_ID * This is done automatically if the label isn't found * in the list of defined symbols. */ - warning("IMPORT is a deprecated keyword with no effect: %s", $1); + warning(WARNING_OBSOLETE, "IMPORT is a deprecated keyword with no effect: %s", $1); } ; @@ -1461,7 +1462,7 @@ const : T_ID { constexpr_Symbol(&$$, $1); } string : T_STRING { if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN) - warning("String is too long '%s'", $1); + warning(WARNING_LONG_STR, "String is too long '%s'", $1); } | T_OP_STRSUB '(' string comma uconst comma uconst ')' { @@ -1470,19 +1471,19 @@ string : T_STRING | T_OP_STRCAT '(' string comma string ')' { if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN) - warning("STRCAT: String too long '%s%s'", $3, $5); + warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'", $3, $5); } | T_OP_STRUPR '(' string ')' { if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN) - warning("STRUPR: String too long '%s'", $3); + warning(WARNING_LONG_STR, "STRUPR: String too long '%s'", $3); upperstring($$); } | T_OP_STRLWR '(' string ')' { if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN) - warning("STRUPR: String too long '%s'", $3); + warning(WARNING_LONG_STR, "STRUPR: String too long '%s'", $3); lowerstring($$); } @@ -1533,22 +1534,22 @@ sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } | T_SECT_OAM { $$ = SECTTYPE_OAM; } | T_SECT_HOME { - warning("HOME section name is deprecated, use ROM0 instead."); + warning(WARNING_OBSOLETE, "HOME section name is deprecated, use ROM0 instead."); $$ = SECTTYPE_ROM0; } | T_SECT_DATA { - warning("DATA section name is deprecated, use ROMX instead."); + warning(WARNING_OBSOLETE, "DATA section name is deprecated, use ROMX instead."); $$ = SECTTYPE_ROMX; } | T_SECT_CODE { - warning("CODE section name is deprecated, use ROMX instead."); + warning(WARNING_OBSOLETE, "CODE section name is deprecated, use ROMX instead."); $$ = SECTTYPE_ROMX; } | T_SECT_BSS { - warning("BSS section name is deprecated, use WRAM0 instead."); + warning(WARNING_OBSOLETE, "BSS section name is deprecated, use WRAM0 instead."); $$ = SECTTYPE_WRAM0; } ; @@ -1746,7 +1747,7 @@ z80_jp : T_Z80_JP const_16bit | T_Z80_JP T_MODE_HL_IND { out_AbsByte(0xE9); - warning("'JP [HL]' is obsolete, use 'JP HL' instead."); + warning(WARNING_OBSOLETE, "'JP [HL]' is obsolete, use 'JP HL' instead."); } | T_Z80_JP T_MODE_HL { @@ -1773,7 +1774,7 @@ z80_ldi : T_Z80_LDI T_MODE_HL_IND comma T_MODE_A | T_Z80_LDI T_MODE_A comma T_MODE_HL { out_AbsByte(0x0A | (2 << 4)); - warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead."); + warning(WARNING_OBSOLETE, "'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead."); } | T_Z80_LDI T_MODE_A comma T_MODE_HL_IND { @@ -1788,7 +1789,7 @@ z80_ldd : T_Z80_LDD T_MODE_HL_IND comma T_MODE_A | T_Z80_LDD T_MODE_A comma T_MODE_HL { out_AbsByte(0x0A | (3 << 4)); - warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead."); + warning(WARNING_OBSOLETE, "'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead."); } | T_Z80_LDD T_MODE_A comma T_MODE_HL_IND { @@ -1842,7 +1843,7 @@ z80_ld_hl : T_Z80_LD T_MODE_HL comma '[' T_MODE_SP const_8bit ']' { out_AbsByte(0xF8); out_RelByte(&$6); - warning("'LD HL,[SP+e8]' is obsolete, use 'LD HL,SP+e8' instead."); + warning(WARNING_OBSOLETE, "'LD HL,[SP+e8]' is obsolete, use 'LD HL,SP+e8' instead."); } | T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit { diff --git a/src/asm/charmap.c b/src/asm/charmap.c index 3b29707b..c522f0e3 100644 --- a/src/asm/charmap.c +++ b/src/asm/charmap.c @@ -17,6 +17,7 @@ #include "asm/main.h" #include "asm/output.h" #include "asm/util.h" +#include "asm/warning.h" #define CHARMAP_HASH_SIZE (1 << 9) @@ -39,7 +40,7 @@ static void warnSectionCharmap(void) if (warned) return; - warning("Using 'charmap' within a section when the current charmap is 'main' is deprecated"); + warning(WARNING_OBSOLETE, "Using 'charmap' within a section when the current charmap is 'main' is deprecated"); warned = true; } diff --git a/src/asm/constexpr.c b/src/asm/constexpr.c index 349c17b1..cec13b9d 100644 --- a/src/asm/constexpr.c +++ b/src/asm/constexpr.c @@ -17,6 +17,7 @@ #include "asm/mymath.h" #include "asm/rpn.h" #include "asm/symbol.h" +#include "asm/warning.h" #include "asmy.h" @@ -171,7 +172,7 @@ void constexpr_BinaryOp(struct ConstExpression *expr, break; case T_OP_SHL: if (value1 < 0) - warning("Left shift of negative value: %d", + warning(WARNING_SHIFT, "Left shift of negative value: %d", value1); if (value2 < 0) @@ -200,7 +201,7 @@ void constexpr_BinaryOp(struct ConstExpression *expr, if (value2 == 0) fatalerror("Division by zero"); if (value1 == INT32_MIN && value2 == -1) { - warning("Division of min value by -1"); + warning(WARNING_DIV, "Division of min value by -1"); result = INT32_MIN; } else { result = value1 / value2; diff --git a/src/asm/fstack.c b/src/asm/fstack.c index ae7bfd2c..9953bb71 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -22,6 +22,7 @@ #include "asm/main.h" #include "asm/output.h" #include "asm/symbol.h" +#include "asm/warning.h" #include "extern/err.h" @@ -291,7 +292,7 @@ void fstk_DumpToStr(char *buf, size_t buflen) len -= retcode; if (!len) - warning("File stack dump too long, got truncated"); + warning(WARNING_LONG_STR, "File stack dump too long, got truncated"); } /* diff --git a/src/asm/globlex.c b/src/asm/globlex.c index 14f45c5d..0d6e0198 100644 --- a/src/asm/globlex.c +++ b/src/asm/globlex.c @@ -20,6 +20,7 @@ #include "asm/rpn.h" #include "asm/symbol.h" #include "asm/symbol.h" +#include "asm/warning.h" #include "helpers.h" @@ -126,7 +127,7 @@ static int32_t ascii2bin(char *s) * the Game Boy tile width, produces a nonsensical result. */ if (size > 8) { - warning("Graphics constant '%s' is too long", + warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long", start); } } else { @@ -143,7 +144,7 @@ static int32_t ascii2bin(char *s) } if (overflow) - warning("Integer constant '%s' is too large", + warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large", start); } diff --git a/src/asm/lexer.c b/src/asm/lexer.c index ee391b43..e4607944 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -20,6 +20,7 @@ #include "asm/lexer.h" #include "asm/main.h" #include "asm/rpn.h" +#include "asm/warning.h" #include "extern/err.h" diff --git a/src/asm/main.c b/src/asm/main.c index ba07468b..2962536e 100644 --- a/src/asm/main.c +++ b/src/asm/main.c @@ -21,6 +21,7 @@ #include "asm/output.h" #include "asm/main.h" #include "asm/charmap.h" +#include "asm/warning.h" #include "extern/err.h" #include "extern/getopt.h" @@ -38,7 +39,7 @@ char **cldefines; clock_t nStartClock, nEndClock; int32_t nLineNo; -uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth, nErrors; +uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth; bool skipElif; uint32_t unionStart[128], unionSize[128]; @@ -234,61 +235,8 @@ static void opt_ParseDefines(void) sym_AddString(cldefines[i], cldefines[i + 1]); } -/* - * Error handling - */ -void verror(const char *fmt, va_list args) -{ - fputs("ERROR: ", stderr); - fstk_Dump(); - fputs(":\n ", stderr); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - fstk_DumpStringExpansions(); - nErrors++; -} - -void yyerror(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - verror(fmt, args); - va_end(args); -} - -noreturn_ void fatalerror(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - verror(fmt, args); - va_end(args); - - exit(5); -} - -void warning(const char *fmt, ...) -{ - if (!CurrentOptions.warnings) - return; - - va_list args; - - va_start(args, fmt); - - fputs("warning: ", stderr); - fstk_Dump(); - fputs(":\n ", stderr); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - fstk_DumpStringExpansions(); - - va_end(args); -} - /* Short options */ -static char const *optstring = "b:D:Eg:hi:LM:o:p:r:Vvw"; +static char const *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w"; /* * Equivalent long options @@ -314,7 +262,7 @@ static struct option const longopts[] = { { "recursion-depth", required_argument, NULL, 'r' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, - { "warning", no_argument, NULL, 'w' }, + { "warning", required_argument, NULL, 'W' }, { NULL, no_argument, NULL, 0 } }; @@ -323,7 +271,7 @@ static void print_usage(void) printf( "usage: rgbasm [-EhLVvw] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n" " [-M dependfile] [-o outfile] [-p pad_value]\n" -" [-r recursion_depth] file.asm\n"); +" [-r recursion_depth] [-W warning] [-w] file.asm\n"); exit(1); } @@ -436,6 +384,9 @@ int main(int argc, char *argv[]) case 'v': newopt.verbose = true; break; + case 'W': + processWarningFlag(optarg); + break; case 'w': newopt.warnings = false; break; @@ -476,7 +427,6 @@ int main(int argc, char *argv[]) skipElif = true; nUnionDepth = 0; nPC = 0; - nErrors = 0; sym_Init(); sym_SetExportAll(CurrentOptions.exportall); fstk_Init(tzMainfile); @@ -486,8 +436,8 @@ int main(int argc, char *argv[]) yy_set_state(LEX_STATE_NORMAL); opt_SetCurrentOptions(&DefaultOptions); - if (yyparse() != 0 || nErrors != 0) - errx(1, "Assembly aborted (%ld errors)!", nErrors); + if (yyparse() != 0 || nbErrors != 0) + errx(1, "Assembly aborted (%ld errors)!", nbErrors); if (nIFDepth != 0) errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth); diff --git a/src/asm/output.c b/src/asm/output.c index 986674cf..f0eeb773 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -24,6 +24,7 @@ #include "asm/output.h" #include "asm/rpn.h" #include "asm/symbol.h" +#include "asm/warning.h" #include "extern/err.h" diff --git a/src/asm/rpn.c b/src/asm/rpn.c index 03c534fb..47f90f98 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -19,6 +19,7 @@ #include "asm/main.h" #include "asm/rpn.h" #include "asm/symbol.h" +#include "asm/warning.h" #include "linkdefs.h" @@ -396,7 +397,8 @@ void rpn_SHL(struct Expression *expr, const struct Expression *src1, if (!expr->isReloc) { if (src1->nVal < 0) - warning("Left shift of negative value: %d", src1->nVal); + warning(WARNING_SHIFT, "Left shift of negative value: %d", + src1->nVal); if (src2->nVal < 0) fatalerror("Shift by negative value: %d", src2->nVal); @@ -447,7 +449,7 @@ void rpn_DIV(struct Expression *expr, const struct Expression *src1, fatalerror("Division by zero"); if (src1->nVal == INT32_MIN && src2->nVal == -1) { - warning("Division of min value by -1"); + warning(WARNING_DIV, "Division of min value by -1"); expr->nVal = INT32_MIN; } else { expr->nVal = (src1->nVal / src2->nVal); diff --git a/src/asm/symbol.c b/src/asm/symbol.c index e4f243e1..b974e151 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -23,6 +23,7 @@ #include "asm/mymath.h" #include "asm/output.h" #include "asm/util.h" +#include "asm/warning.h" #include "extern/err.h" @@ -133,7 +134,7 @@ struct sSymbol *createsymbol(char *s) } if (snprintf((*ppsym)->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN) - warning("Symbol name is too long: '%s'", s); + warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s); (*ppsym)->nValue = 0; (*ppsym)->nType = 0; diff --git a/src/asm/util.c b/src/asm/util.c index 0b13cfba..4ed805e9 100644 --- a/src/asm/util.c +++ b/src/asm/util.c @@ -10,6 +10,7 @@ #include "asm/main.h" #include "asm/util.h" +#include "asm/warning.h" #include "extern/utf8decoder.h" diff --git a/src/asm/warning.c b/src/asm/warning.c new file mode 100644 index 00000000..15aa431c --- /dev/null +++ b/src/asm/warning.c @@ -0,0 +1,240 @@ + +#include +#include +#include +#include +#include + +#include "asm/fstack.h" +#include "asm/main.h" +#include "asm/warning.h" + +#include "extern/err.h" + +unsigned int nbErrors = 0; + +enum WarningState { + WARNING_DEFAULT, + WARNING_DISABLED, + WARNING_ENABLED, + WARNING_ERROR +}; + +static enum WarningState const defaultWarnings[NB_WARNINGS] = { + WARNING_ENABLED, /* User warnings */ + WARNING_DISABLED, /* Obsolete things */ + WARNING_DISABLED, /* Invalid args to builtins */ + WARNING_DISABLED, /* Constants too large */ + WARNING_DISABLED, /* Shifting undefined behavior */ + WARNING_DISABLED, /* Division undefined behavior */ + WARNING_DISABLED, /* Empty entry in `db`, `dw` or `dl` */ + WARNING_DISABLED, /* String too long for internal buffers */ +}; + +static enum WarningState warningStates[NB_WARNINGS]; + +static bool warningsAreErrors; /* Set if `-Werror` was specified */ + +static enum WarningState warningState(enum WarningID id) +{ + /* Check if warnings are globally disabled */ + if (!CurrentOptions.warnings) + return WARNING_DISABLED; + + /* Get the actual state */ + enum WarningState state = warningStates[id]; + + if (state == WARNING_DEFAULT) + /* The state isn't set, grab its default state */ + state = defaultWarnings[id]; + + if (warningsAreErrors && state == WARNING_ENABLED) + state = WARNING_ERROR; + + return state; +} + +static char const *warningFlags[NB_WARNINGS_ALL] = { + "user", + "obsolete", + "builtin-args", + "large-constant", + "shift", + "div", + "empty-entry", + "long-string", + + /* Meta warnings */ + "all", + "extra", + "everything" /* Especially useful for testing */ +}; + +enum MetaWarningCommand { + META_WARNING_DONE = NB_WARNINGS +}; + +/* Warnings that probably indicate an error */ +static uint8_t const _wallCommands[] = { + WARNING_USER, + WARNING_BUILTIN_ARG, + WARNING_LARGE_CONSTANT, + WARNING_EMPTY_ENTRY, + WARNING_LONG_STR, + META_WARNING_DONE +}; + +/* Warnings that are less likely to indicate an error */ +static uint8_t const _wextraCommands[] = { + WARNING_OBSOLETE, + META_WARNING_DONE +}; + +/* Literally everything. Notably useful for testing */ +static uint8_t const _weverythingCommands[] = { + WARNING_USER, + WARNING_OBSOLETE, + WARNING_BUILTIN_ARG, + WARNING_LARGE_CONSTANT, + WARNING_SHIFT, + WARNING_DIV, + WARNING_EMPTY_ENTRY, + WARNING_LONG_STR, + META_WARNING_DONE +}; + +static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = { + _wallCommands, + _wextraCommands, + _weverythingCommands +}; + +void processWarningFlag(char const *flag) +{ + static bool setError = false; + + /* First, try to match against a "meta" warning */ + for (enum WarningID id = NB_WARNINGS; id < NB_WARNINGS_ALL; id++) { + /* TODO: improve the matching performance? */ + if (!strcmp(flag, warningFlags[id])) { + /* We got a match! */ + uint8_t const *ptr = + metaWarningCommands[id - NB_WARNINGS]; + + for (;;) { + if (*ptr == META_WARNING_DONE) + return; + + /* Warning flag, set without override */ + if (warningStates[*ptr] == WARNING_DEFAULT) + warningStates[*ptr] = WARNING_ENABLED; + ptr++; + } + } + } + + /* If it's not a meta warning, specially check against `-Werror` */ + if (!strncmp(flag, "error", strlen("error"))) { + char const *errorFlag = flag + strlen("error"); + + switch (*errorFlag) { + case '\0': + /* `-Werror` */ + warningsAreErrors = true; + return; + + case '=': + /* `-Werror=XXX */ + setError = true; + processWarningFlag(errorFlag + 1); /* Skip the `=` */ + setError = false; + return; + + /* Otherwise, allow parsing as another flag */ + } + } + + /* Well, it's either a normal warning or a mistake */ + + /* Check if this is a negation */ + bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError; + char const *rootFlag = isNegation ? flag + strlen("no-") : flag; + enum WarningState state = setError ? WARNING_ERROR : + isNegation ? WARNING_DISABLED : WARNING_ENABLED; + + /* Try to match the flag against a "normal" flag */ + for (enum WarningID id = 0; id < NB_WARNINGS; id++) { + if (!strcmp(rootFlag, warningFlags[id])) { + /* We got a match! */ + warningStates[id] = state; + return; + } + } + + warnx("Unknown warning `%s`", flag); +} + +void verror(const char *fmt, va_list args, char const *flag) +{ + fputs("ERROR: ", stderr); + fstk_Dump(); + fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fstk_DumpStringExpansions(); + nbErrors++; +} + +void yyerror(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + verror(fmt, args, NULL); + va_end(args); +} + +noreturn_ void fatalerror(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + verror(fmt, args, NULL); + va_end(args); + + exit(5); +} + +void warning(enum WarningID id, char const *fmt, ...) +{ + char const *flag = warningFlags[id]; + va_list args; + + va_start(args, fmt); + + switch (warningState(id)) { + case WARNING_DISABLED: + return; + + case WARNING_ERROR: + verror(fmt, args, flag); + va_end(args); + return; + + case WARNING_DEFAULT: + abort(); + /* Not reached */ + + case WARNING_ENABLED: + break; + } + + fputs("warning: ", stderr); + fstk_Dump(); + fprintf(stderr, ": [-W%s]\n ", flag); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fstk_DumpStringExpansions(); + + va_end(args); +} diff --git a/test/asm/correct-line-number.out b/test/asm/correct-line-number.out index 34e07cae..02693638 100644 --- a/test/asm/correct-line-number.out +++ b/test/asm/correct-line-number.out @@ -1,4 +1,4 @@ -warning: correct-line-number.asm(5): +warning: correct-line-number.asm(5): [-Wuser] Am I geting ahead of myself? -warning: correct-line-number.asm(11): +warning: correct-line-number.asm(11): [-Wuser] Hopefully not. diff --git a/test/asm/multiple-charmaps.out b/test/asm/multiple-charmaps.out index 16418b15..34647462 100644 --- a/test/asm/multiple-charmaps.out +++ b/test/asm/multiple-charmaps.out @@ -1,4 +1,4 @@ -warning: multiple-charmaps.asm(75): +warning: multiple-charmaps.asm(75): [-Wobsolete] Using 'charmap' within a section when the current charmap is 'main' is deprecated ERROR: multiple-charmaps.asm(100) -> multiple-charmaps.asm::new_(7): Charmap 'map1' already exists diff --git a/test/asm/overflow.out b/test/asm/overflow.out index 6e14b159..ecb432c3 100644 --- a/test/asm/overflow.out +++ b/test/asm/overflow.out @@ -1,14 +1,14 @@ -warning: overflow.asm(24): +warning: overflow.asm(24): [-Wdiv] Division of min value by -1 -warning: overflow.asm(25): +warning: overflow.asm(25): [-Wdiv] Division of min value by -1 -warning: overflow.asm(34): +warning: overflow.asm(34): [-Wshift] Left shift of negative value: -1 -warning: overflow.asm(35): +warning: overflow.asm(35): [-Wshift] Left shift of negative value: -1 -warning: overflow.asm(39): +warning: overflow.asm(39): [-Wlarge-constant] Integer constant '4294967296' is too large -warning: overflow.asm(42): +warning: overflow.asm(42): [-Wlarge-constant] Graphics constant '`333333333' is too long $80000000 $7FFFFFFF diff --git a/test/asm/strsub.out b/test/asm/strsub.out index a19fdc5e..75a60663 100644 --- a/test/asm/strsub.out +++ b/test/asm/strsub.out @@ -1,18 +1,18 @@ -warning: strsub.asm(13) -> strsub.asm::xstrsub(4): +warning: strsub.asm(13) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 32 -warning: strsub.asm(14) -> strsub.asm::xstrsub(4): +warning: strsub.asm(14) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 300 -warning: strsub.asm(15) -> strsub.asm::xstrsub(4): +warning: strsub.asm(15) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Position starts at 1 -warning: strsub.asm(15) -> strsub.asm::xstrsub(4): +warning: strsub.asm(15) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 300 -warning: strsub.asm(16) -> strsub.asm::xstrsub(4): +warning: strsub.asm(16) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Position 4 is past the end of the string -warning: strsub.asm(17) -> strsub.asm::xstrsub(4): +warning: strsub.asm(17) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Position 4 is past the end of the string -warning: strsub.asm(17) -> strsub.asm::xstrsub(4): +warning: strsub.asm(17) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 1 -warning: strsub.asm(20) -> strsub.asm::xstrsub(4): +warning: strsub.asm(20) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 10 A B diff --git a/test/asm/test.sh b/test/asm/test.sh index 8192a2dd..1f736f3e 100755 --- a/test/asm/test.sh +++ b/test/asm/test.sh @@ -10,7 +10,7 @@ rc=0 for i in *.asm; do for variant in '' '.pipe'; do if [ -z "$variant" ]; then - ../../rgbasm -o $o $i > $after 2>&1 + ../../rgbasm -Weverything -o $o $i > $after 2>&1 desired_output=${i%.asm}.out else # `include-recursion.asm` refers to its own name inside the test code. @@ -23,7 +23,7 @@ for i in *.asm; do # stdin redirection makes the input an unseekable pipe - a scenario # that's harder to deal with and was broken when the feature was # first implemented. - cat $i | ../../rgbasm -o $o - > $after 2>&1 + cat $i | ../../rgbasm -Weverything -o $o - > $after 2>&1 # Escape regex metacharacters desired_output=$before