diff --git a/contrib/bash_compl/_rgblink.bash b/contrib/bash_compl/_rgblink.bash index 9b94fccd..49d28c51 100755 --- a/contrib/bash_compl/_rgblink.bash +++ b/contrib/bash_compl/_rgblink.bash @@ -142,6 +142,7 @@ _rgblink_completions() { mapfile -t COMPREPLY < <(compgen -W " assert div + large-constant obsolete shift shift-amount diff --git a/contrib/zsh_compl/_rgblink b/contrib/zsh_compl/_rgblink index fa4f5831..8818dda5 100644 --- a/contrib/zsh_compl/_rgblink +++ b/contrib/zsh_compl/_rgblink @@ -9,6 +9,7 @@ _rgblink_warnings() { 'assert:Warn when WARN-type asserts fail' 'div:Warn when dividing the smallest int by -1' + 'large-constant:Warn on constants too large for a signed 32-bit int' 'obsolete:Warn when using deprecated features' 'shift:Warn when shifting negative values' 'shift-amount:Warn when a shift'\''s operand is negative or \> 32' diff --git a/include/link/warning.hpp b/include/link/warning.hpp index a19f7a16..4156c164 100644 --- a/include/link/warning.hpp +++ b/include/link/warning.hpp @@ -22,11 +22,12 @@ enum WarningLevel { }; enum WarningID { - WARNING_ASSERT, // Assertions - WARNING_DIV, // Undefined division behavior - WARNING_OBSOLETE, // Obsolete/deprecated things - WARNING_SHIFT, // Undefined `SHIFT` behavior - WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount + WARNING_ASSERT, // Assertions + WARNING_DIV, // Undefined division behavior + WARNING_LARGE_CONSTANT, // Constants too large + WARNING_OBSOLETE, // Obsolete/deprecated things + WARNING_SHIFT, // Undefined `SHIFT` behavior + WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount NB_PLAIN_WARNINGS, @@ -47,6 +48,8 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...); [[gnu::format(printf, 1, 2)]] void warning(char const *fmt, ...); +[[gnu::format(printf, 2, 3)]] +void scriptWarning(WarningID id, char const *fmt, ...); [[gnu::format(printf, 3, 4)]] void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...); diff --git a/man/rgblink.1 b/man/rgblink.1 index d7826478..a3df1fd3 100644 --- a/man/rgblink.1 +++ b/man/rgblink.1 @@ -369,6 +369,8 @@ for Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow. This warning is enabled by .Fl Wall . +.It Fl Wno-large-constant +Warn when a constant too large to fit in a signed 32-bit integer is encountered. .It Fl Wno-obsolete Warn when obsolete features are encountered, which have been deprecated and may later be removed. .It Fl Wshift diff --git a/src/link/lexer.cpp b/src/link/lexer.cpp index c2602dfa..61b19211 100644 --- a/src/link/lexer.cpp +++ b/src/link/lexer.cpp @@ -113,9 +113,18 @@ static yy::parser::symbol_type readNumber(int initial, char const *prefix, char context.file.sbumpc(); } for (int c = context.file.sgetc(); isDigit(c) || c == '_'; c = context.file.snextc()) { - if (c != '_') { - number = number * Base + parseDigit(c); + if (c == '_') { + continue; } + uint32_t digit = parseDigit(c); + if (number > (UINT32_MAX - digit) / Base) { + scriptWarning(WARNING_LARGE_CONSTANT, "Integer constant is too large"); + // Discard any additional digits + for (c = context.file.snextc(); isDigit(c) || c == '_'; + c = context.file.snextc()) {} + return yy::parser::make_number(0); + } + number = number * Base + digit; } return yy::parser::make_number(number); } diff --git a/src/link/warning.cpp b/src/link/warning.cpp index 48cdb795..a9b85b7e 100644 --- a/src/link/warning.cpp +++ b/src/link/warning.cpp @@ -17,18 +17,19 @@ // clang-format off: nested initializers Diagnostics warnings = { .metaWarnings = { - {"all", LEVEL_ALL }, - {"everything", LEVEL_EVERYTHING}, + {"all", LEVEL_ALL }, + {"everything", LEVEL_EVERYTHING}, }, .warningFlags = { - {"assert", LEVEL_DEFAULT }, - {"div", LEVEL_ALL }, - {"obsolete", LEVEL_DEFAULT }, - {"shift", LEVEL_ALL }, - {"shift-amount", LEVEL_ALL }, + {"assert", LEVEL_DEFAULT }, + {"div", LEVEL_ALL }, + {"large-constant", LEVEL_DEFAULT }, + {"obsolete", LEVEL_DEFAULT }, + {"shift", LEVEL_ALL }, + {"shift-amount", LEVEL_ALL }, // Parametric warnings - {"truncation", LEVEL_DEFAULT }, - {"truncation", LEVEL_EVERYTHING}, + {"truncation", LEVEL_DEFAULT }, + {"truncation", LEVEL_EVERYTHING}, }, .paramWarnings = { {WARNING_TRUNCATION_1, WARNING_TRUNCATION_2, 1}, @@ -198,3 +199,29 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const va_end(args); } + +void scriptWarning(WarningID id, char const *fmt, ...) { + char const *flag = warnings.warningFlags[id].name; + va_list args; + + va_start(args, fmt); + + switch (warnings.getWarningBehavior(id)) { + case WarningBehavior::DISABLED: + break; + + case WarningBehavior::ENABLED: + printDiag(nullptr, 0, fmt, args, "warning", STYLE_YELLOW, "[-W%s]", flag); + break; + + case WarningBehavior::ERROR: + printDiag(nullptr, 0, fmt, args, "error", STYLE_RED, "[-Werror=%s]", flag); + + warnings.incrementErrors(); + break; + } + + va_end(args); + + lexer_TraceCurrent(); +} diff --git a/test/link/large-constant.asm b/test/link/large-constant.asm new file mode 100644 index 00000000..143c8b68 --- /dev/null +++ b/test/link/large-constant.asm @@ -0,0 +1,2 @@ +section "test", rom0 +label:: dw label diff --git a/test/link/large-constant.inc b/test/link/large-constant.inc new file mode 100644 index 00000000..4fc75d34 --- /dev/null +++ b/test/link/large-constant.inc @@ -0,0 +1 @@ + org $1_ffff_ffff diff --git a/test/link/large-constant.link b/test/link/large-constant.link new file mode 100644 index 00000000..63e95e27 --- /dev/null +++ b/test/link/large-constant.link @@ -0,0 +1,3 @@ +ROM0 + INCLUDE "large-constant.inc" + "test" diff --git a/test/link/large-constant.out b/test/link/large-constant.out new file mode 100644 index 00000000..7c9ebf3b --- /dev/null +++ b/test/link/large-constant.out @@ -0,0 +1,2 @@ +warning: Integer constant is too large [-Wlarge-constant] + at large-constant.inc(1) <- large-constant.link(3)