Implement warning diagnostic flags for RGBLINK (#1754)

This commit is contained in:
Rangi
2025-07-17 12:54:29 -04:00
committed by GitHub
parent 529989bde5
commit 5eb093f13e
15 changed files with 332 additions and 55 deletions

View File

@@ -21,6 +21,7 @@ _rgblink_completions() {
[O]="overlay:glob-*.gb *.gbc *.sgb" [O]="overlay:glob-*.gb *.gbc *.sgb"
[o]="output:glob-*.gb *.gbc *.sgb" [o]="output:glob-*.gb *.gbc *.sgb"
[p]="pad:unk" [p]="pad:unk"
[W]="warning:warning"
) )
# Parse command-line up to current word # Parse command-line up to current word
local opt_ena=true local opt_ena=true
@@ -136,6 +137,18 @@ _rgblink_completions() {
case "$state" in case "$state" in
unk) # Return with no replies: no idea what to complete! unk) # Return with no replies: no idea what to complete!
;; ;;
warning)
mapfile -t COMPREPLY < <(compgen -W "
assert
div
obsolete
shift
shift-amount
truncation
all
everything
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
;;
normal) # Acts like a glob... normal) # Acts like a glob...
state="glob-*.o *.obj" state="glob-*.o *.obj"
;& ;&

View File

@@ -23,7 +23,7 @@ _rgbasm_warnings() {
'obsolete:Warn when using deprecated features' 'obsolete:Warn when using deprecated features'
'purge:Warn when purging exported symbols or labels' 'purge:Warn when purging exported symbols or labels'
'shift:Warn when shifting negative values' 'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand it negative or \> 32' 'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
'truncation:Warn when implicit truncation loses bits' 'truncation:Warn when implicit truncation loses bits'
'unmapped-char:Warn on unmapped character' 'unmapped-char:Warn on unmapped character'
'unmatched-directive:Warn on unmatched directive pair' 'unmatched-directive:Warn on unmatched directive pair'

View File

@@ -1,5 +1,22 @@
#compdef rgblink #compdef rgblink
_rgblink_warnings() {
local warnings=(
'error:Turn all warnings into errors'
'all:Enable most warning messages'
'everything:Enable literally everything'
'assert:Warn when WARN-type asserts fail'
'div:Warn when dividing the smallest int by -1'
'obsolete:Warn when using deprecated features'
'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
'truncation:Warn when implicit truncation loses bits'
)
_describe warning warnings
}
local args=( local args=(
# Arguments are listed here in the same order as in the manual, except for the version and help # Arguments are listed here in the same order as in the manual, except for the version and help
'(- : * options)'{-V,--version}'[Print version number and exit]' '(- : * options)'{-V,--version}'[Print version number and exit]'
@@ -19,6 +36,7 @@ local args=(
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'" '(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:' '(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec' '(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgblink_warnings'
'*'":object files:_files -g '*.o'" '*'":object files:_files -g '*.o'"
) )

View File

@@ -5,12 +5,37 @@
#include <stdint.h> #include <stdint.h>
#include "diagnostics.hpp"
#define warningAt(where, ...) warning(where.src, where.lineNo, __VA_ARGS__) #define warningAt(where, ...) warning(where.src, where.lineNo, __VA_ARGS__)
#define errorAt(where, ...) error(where.src, where.lineNo, __VA_ARGS__) #define errorAt(where, ...) error(where.src, where.lineNo, __VA_ARGS__)
#define fatalAt(where, ...) fatal(where.src, where.lineNo, __VA_ARGS__) #define fatalAt(where, ...) fatal(where.src, where.lineNo, __VA_ARGS__)
enum WarningLevel {
LEVEL_DEFAULT, // Warnings that are enabled by default
LEVEL_ALL, // Warnings that probably indicate an error
LEVEL_EVERYTHING, // Literally every warning
};
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_TRUNCATION, // Implicit truncation loses some bits
NB_PLAIN_WARNINGS,
NB_WARNINGS = NB_PLAIN_WARNINGS,
};
extern Diagnostics<WarningLevel, WarningID> warnings;
struct FileStackNode; struct FileStackNode;
[[gnu::format(printf, 4, 5)]]
void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]] [[gnu::format(printf, 3, 4)]]
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...); void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]

View File

@@ -350,11 +350,7 @@ Block comments cannot be nested, so the first
.Ql */ .Ql */
will end the whole comment. will end the whole comment.
.It Fl Wno-obsolete .It Fl Wno-obsolete
Warn when obsolete constructs such as the Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.Ic _PI
constant or
.Ic PRINTT
directive are encountered.
.It Fl Wnumeric-string= .It Fl Wnumeric-string=
Warn when a multi-character string is treated as a number. Warn when a multi-character string is treated as a number.
.Fl Wnumeric-string=0 .Fl Wnumeric-string=0

View File

@@ -16,6 +16,7 @@
.Op Fl o Ar out_file .Op Fl o Ar out_file
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl S Ar spec .Op Fl S Ar spec
.Op Fl W Ar warning
.Ar .Ar
.Sh DESCRIPTION .Sh DESCRIPTION
The The
@@ -114,6 +115,15 @@ Useful for ROMs that fit in 32 KiB.
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v , Fl \-verbose .It Fl v , Fl \-verbose
Verbose: enable printing more information to standard error. Verbose: enable printing more information to standard error.
.It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag
.Ar warning .
A warning message will be printed if
.Ar warning
is an unknown warning flag.
See the
.Sx DIAGNOSTICS
section for a list of warnings.
.It Fl w , Fl \-wramx .It Fl w , Fl \-wramx
Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM. Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM.
WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0. WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0.
@@ -176,6 +186,84 @@ as
.Ic WRAMX .Ic WRAMX
sections will be treated as sections will be treated as
.Ic WRAM0 . .Ic WRAM0 .
.Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the linking process.
The following options alter the way warnings are processed.
.Bl -tag -width Ds
.It Fl Werror
Make all warnings into errors.
This can be negated as
.Fl Wno-error
to prevent turning all warnings into errors.
.It Fl Werror=
Make the specified warning or meta warning into an error.
A warning's name is appended
.Pq example: Fl Werror=assert ,
and this warning is implicitly enabled and turned into an error.
This can be negated as
.Fl Wno-error=
to prevent turning a specified warning into an error, even if
.Fl Werror
is in effect.
.El
.Pp
The following warnings are
.Dq meta
warnings, that enable a collection of other warnings.
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
The position on the command-line acts as a tie breaker, the last one taking effect.
.Bl -tag -width Ds
.It Fl Wall
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
.It Fl Weverything
Enables literally every warning.
.El
.Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flag also has a negation (for example,
.Fl Wobsolete
enables the warning that
.Fl Wno-obsolete
disables; and
.Fl Wall
enables every warning that
.Fl Wno-all
disables).
Only the non-default flag is listed here.
Ignoring the
.Dq no-
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wno-assert
Warn when
.Ic WARN Ns No -type
assertions fail. (See
.Dq Aborting the assembly process
in
.Xr rgbasm 5
for
.Ic ASSERT ) .
.It Fl Wdiv
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-obsolete
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.It Fl Wshift
Warn when shifting right a negative value.
Use a division by 2**N instead.
This warning is enabled by
.Fl Wall .
.It Fl Wshift-amount
Warn when a shift's operand is negative or greater than 32.
This warning is enabled by
.Fl Wall .
.It Fl Wno-truncation
Warn when an implicit truncation (for example,
.Ic db
to an 8-bit value) loses some bits.
This occurs when an N-bit value is 2**N or greater, or less than -2**N.
.El
.Sh EXAMPLES .Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM image like so: All you need for a basic ROM is an object file, which can be made into a ROM image like so:
.Pp .Pp

View File

@@ -69,7 +69,7 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
} }
// Short options // Short options
static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvwx"; static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvW:wx";
// Equivalent long options // Equivalent long options
// Please keep in the same order as short opts. // Please keep in the same order as short opts.
@@ -92,6 +92,7 @@ static option const longopts[] = {
{"tiny", no_argument, nullptr, 't'}, {"tiny", no_argument, nullptr, 't'},
{"version", no_argument, nullptr, 'V'}, {"version", no_argument, nullptr, 'V'},
{"verbose", no_argument, nullptr, 'v'}, {"verbose", no_argument, nullptr, 'v'},
{"warning", required_argument, nullptr, 'W'},
{"wramx", no_argument, nullptr, 'w'}, {"wramx", no_argument, nullptr, 'w'},
{"nopad", no_argument, nullptr, 'x'}, {"nopad", no_argument, nullptr, 'x'},
{nullptr, no_argument, nullptr, 0 } {nullptr, no_argument, nullptr, 0 }
@@ -347,6 +348,9 @@ int main(int argc, char *argv[]) {
beVerbose = true; beVerbose = true;
break; break;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'W':
warnings.processWarningFlag(musl_optarg);
break;
case 'w': case 'w':
isWRAM0Mode = true; isWRAM0Mode = true;
break; break;

View File

@@ -33,6 +33,25 @@ static void pushRPN(int32_t value, bool comesFromError) {
// has popped any values with the error flag set. // has popped any values with the error flag set.
static bool isError = false; static bool isError = false;
#define diagnosticAt(patch, id, ...) \
do { \
bool errorDiag = warnings.getWarningBehavior(id) == WarningBehavior::ERROR; \
if (!isError || !errorDiag) { \
warningAt(patch, id, __VA_ARGS__); \
} \
if (errorDiag) { \
isError = true; \
} \
} while (0)
#define firstErrorAt(...) \
do { \
if (!isError) { \
errorAt(__VA_ARGS__); \
isError = true; \
} \
} while (0)
static int32_t popRPN(Patch const &patch) { static int32_t popRPN(Patch const &patch) {
if (rpnStack.empty()) { if (rpnStack.empty()) {
fatalAt(patch, "Internal error, RPN stack empty"); fatalAt(patch, "Internal error, RPN stack empty");
@@ -98,23 +117,26 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_DIV: case RPN_DIV:
value = popRPN(patch); value = popRPN(patch);
if (value == 0) { if (value == 0) {
if (!isError) { firstErrorAt(patch, "Division by 0");
errorAt(patch, "Division by 0");
isError = true;
}
popRPN(patch); popRPN(patch);
value = INT32_MAX; value = 0;
} else if (int32_t lval = popRPN(patch); lval == INT32_MIN && value == -1) {
diagnosticAt(
patch,
WARNING_DIV,
"Division of %" PRId32 " by -1 yields %" PRId32,
INT32_MIN,
INT32_MIN
);
value = INT32_MIN;
} else { } else {
value = op_divide(popRPN(patch), value); value = op_divide(lval, value);
} }
break; break;
case RPN_MOD: case RPN_MOD:
value = popRPN(patch); value = popRPN(patch);
if (value == 0) { if (value == 0) {
if (!isError) { firstErrorAt(patch, "Modulo by 0");
errorAt(patch, "Modulo by 0");
isError = true;
}
popRPN(patch); popRPN(patch);
value = 0; value = 0;
} else { } else {
@@ -127,10 +149,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_EXP: case RPN_EXP:
value = popRPN(patch); value = popRPN(patch);
if (value < 0) { if (value < 0) {
if (!isError) { firstErrorAt(patch, "Exponent by negative value %" PRId32, value);
errorAt(patch, "Exponent by negative value %" PRId32, value);
isError = true;
}
popRPN(patch); popRPN(patch);
value = 0; value = 0;
} else { } else {
@@ -204,14 +223,49 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_SHL: case RPN_SHL:
value = popRPN(patch); value = popRPN(patch);
if (value < 0) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, value
);
}
if (value >= 32) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, value
);
}
value = op_shift_left(popRPN(patch), value); value = op_shift_left(popRPN(patch), value);
break; break;
case RPN_SHR: case RPN_SHR: {
value = popRPN(patch); value = popRPN(patch);
value = op_shift_right(popRPN(patch), value); int32_t lval = popRPN(patch);
if (lval < 0) {
diagnosticAt(patch, WARNING_SHIFT, "Shifting right negative value %" PRId32, lval);
}
if (value < 0) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
);
}
if (value >= 32) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
);
}
value = op_shift_right(lval, value);
break; break;
}
case RPN_USHR: case RPN_USHR:
value = popRPN(patch); value = popRPN(patch);
if (value < 0) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
);
}
if (value >= 32) {
diagnosticAt(
patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
);
}
value = op_shift_right_unsigned(popRPN(patch), value); value = op_shift_right_unsigned(popRPN(patch), value);
break; break;
@@ -324,14 +378,13 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_HRAM: case RPN_HRAM:
value = popRPN(patch); value = popRPN(patch);
if (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF) { if (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF) {
if (!isError) { firstErrorAt(patch, "Address $%" PRIx32 " for LDH is not in HRAM range", value);
errorAt(patch, "Address $%" PRIx32 " for LDH is not in HRAM range", value);
isError = true;
}
value = 0; value = 0;
} else if (value >= 0 && value <= 0xFF) { } else if (value >= 0 && value <= 0xFF) {
warningAt( warningAt(
patch, "LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF" patch,
WARNING_OBSOLETE,
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF"
); );
} }
value &= 0xFF; value &= 0xFF;
@@ -341,10 +394,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
value = popRPN(patch); value = popRPN(patch);
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38 // Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
if (value & ~0x38) { if (value & ~0x38) {
if (!isError) { firstErrorAt(patch, "Value $%" PRIx32 " is not a RST vector", value);
errorAt(patch, "Value $%" PRIx32 " is not a RST vector", value);
isError = true;
}
value = 0; value = 0;
} }
value |= 0xC7; value |= 0xC7;
@@ -355,10 +405,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
int32_t mask = getRPNByte(expression, size, patch); int32_t mask = getRPNByte(expression, size, patch);
// Acceptable values are 0 to 7 // Acceptable values are 0 to 7
if (value & ~0x07) { if (value & ~0x07) {
if (!isError) { firstErrorAt(patch, "Value $%" PRIx32 " is not a bit index", value);
errorAt(patch, "Value $%" PRIx32 " is not a bit index", value);
isError = true;
}
value = 0; value = 0;
} }
value = mask | (value << 3); value = mask | (value << 3);
@@ -437,6 +484,7 @@ void patch_CheckAssertions() {
case ASSERT_WARN: case ASSERT_WARN:
warningAt( warningAt(
assert.patch, assert.patch,
WARNING_ASSERT,
"%s", "%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure" !assert.message.empty() ? assert.message.c_str() : "assert failure"
); );
@@ -486,8 +534,8 @@ static void applyFilePatches(Section &section, Section &dataSection) {
uint16_t address = patch.pcSection->org + patch.pcOffset + 2; uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127)) { if (jumpOffset < -128 || jumpOffset > 127) {
errorAt( firstErrorAt(
patch, patch,
"JR target must be between -128 and 127 bytes away, not %" PRId16 "JR target must be between -128 and 127 bytes away, not %" PRId16
"; use JP instead", "; use JP instead",
@@ -497,12 +545,13 @@ static void applyFilePatches(Section &section, Section &dataSection) {
dataSection.data[offset] = jumpOffset & 0xFF; dataSection.data[offset] = jumpOffset & 0xFF;
} else { } else {
// Patch a certain number of bytes // Patch a certain number of bytes
if (!isError && (value < type.min || value > type.max)) { if (value < type.min || value > type.max) {
errorAt( diagnosticAt(
patch, patch,
"Value %" PRId32 "%s is not %u-bit", WARNING_TRUNCATION,
"Value $%" PRIx32 "%s is not %u-bit",
value, value,
value < 0 ? " (maybe negative?)" : "", value < 0 ? " (may be negative?)" : "",
type.size * 8U type.size * 8U
); );
} }

View File

@@ -9,14 +9,43 @@
static uint32_t nbErrors = 0; static uint32_t nbErrors = 0;
// clang-format off: nested initializers
Diagnostics<WarningLevel, WarningID> warnings = {
.metaWarnings = {
{"all", LEVEL_ALL },
{"everything", LEVEL_EVERYTHING},
},
.warningFlags = {
{"assert", LEVEL_DEFAULT },
{"div", LEVEL_ALL },
{"obsolete", LEVEL_DEFAULT },
{"shift", LEVEL_ALL },
{"shift-amount", LEVEL_ALL },
{"truncation", LEVEL_DEFAULT },
},
.paramWarnings = {},
.state = DiagnosticsState<WarningID>(),
};
// clang-format on
static void printDiag( static void printDiag(
FileStackNode const *src, uint32_t lineNo, char const *fmt, va_list args, char const *type FileStackNode const *src,
uint32_t lineNo,
char const *fmt,
va_list args,
char const *type,
char const *flagfmt,
char const *flag
) { ) {
fprintf(stderr, "%s: ", type); fprintf(stderr, "%s: ", type);
if (src) { if (src) {
src->dump(lineNo); src->dump(lineNo);
fputs(": ", stderr); fputs(": ", stderr);
} }
if (flagfmt) {
fprintf(stderr, flagfmt, flag);
fputs("\n ", stderr);
}
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
putc('\n', stderr); putc('\n', stderr);
} }
@@ -42,21 +71,21 @@ static void abortLinking(char const *verb) {
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) { void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(src, lineNo, fmt, args, "warning"); printDiag(src, lineNo, fmt, args, "warning", nullptr, 0);
va_end(args); va_end(args);
} }
void warning(char const *fmt, ...) { void warning(char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(nullptr, 0, fmt, args, "warning"); printDiag(nullptr, 0, fmt, args, "warning", nullptr, 0);
va_end(args); va_end(args);
} }
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) { void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(src, lineNo, fmt, args, "error"); printDiag(src, lineNo, fmt, args, "error", nullptr, 0);
va_end(args); va_end(args);
incrementErrors(); incrementErrors();
@@ -65,7 +94,7 @@ void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
void error(char const *fmt, ...) { void error(char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(nullptr, 0, fmt, args, "error"); printDiag(nullptr, 0, fmt, args, "error", nullptr, 0);
va_end(args); va_end(args);
incrementErrors(); incrementErrors();
@@ -96,7 +125,7 @@ void argErr(char flag, char const *fmt, ...) {
void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) { void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(src, lineNo, fmt, args, "FATAL"); printDiag(src, lineNo, fmt, args, "FATAL", nullptr, 0);
va_end(args); va_end(args);
incrementErrors(); incrementErrors();
@@ -107,7 +136,7 @@ void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
void fatal(char const *fmt, ...) { void fatal(char const *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printDiag(nullptr, 0, fmt, args, "FATAL"); printDiag(nullptr, 0, fmt, args, "FATAL", nullptr, 0);
va_end(args); va_end(args);
incrementErrors(); incrementErrors();
@@ -119,3 +148,27 @@ void requireZeroErrors() {
abortLinking("failed"); abortLinking("failed");
} }
} }
void warning(FileStackNode const *src, uint32_t lineNo, 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(src, lineNo, fmt, args, "warning", "[-W%s]", flag);
break;
case WarningBehavior::ERROR:
printDiag(src, lineNo, fmt, args, "error", "[-Werror=%s]", flag);
incrementErrors();
break;
}
va_end(args);
}

View File

@@ -1,4 +1,5 @@
warning: assert.asm(5): Worry about me, but not too much. warning: assert.asm(5): [-Wassert]
Worry about me, but not too much.
error: assert.asm(6): Okay, this is getting serious! error: assert.asm(6): Okay, this is getting serious!
FATAL: assert.asm(7): It all ends now. FATAL: assert.asm(7): It all ends now.
Linking aborted with 2 errors Linking aborted with 2 errors

View File

@@ -1,5 +1,6 @@
error: invalid-patches.asm(10): JR target must be between -128 and 127 bytes away, not 190; use JP instead error: invalid-patches.asm(10): JR target must be between -128 and 127 bytes away, not 190; use JP instead
warning: invalid-patches.asm(9): LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF warning: invalid-patches.asm(9): [-Wobsolete]
LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF
error: invalid-patches.asm(8): Requested SIZEOF() of section "NonexistentSection", which was not found error: invalid-patches.asm(8): Requested SIZEOF() of section "NonexistentSection", which was not found
error: invalid-patches.asm(7): Requested STARTOF() of section "NonexistentSection", which was not found error: invalid-patches.asm(7): Requested STARTOF() of section "NonexistentSection", which was not found
error: invalid-patches.asm(6): Requested BANK() of section "NonexistentSection", which was not found error: invalid-patches.asm(6): Requested BANK() of section "NonexistentSection", which was not found

View File

@@ -0,0 +1,10 @@
def fzero equs "startof(\"test\")"
section "test", rom0
ld a, $8000_0000 / (fzero - 1)
ld a, 1 << (fzero - 1)
ld a, 1 << (fzero + 32)
ld a, (fzero - 1) >> 1
ld a, 1 >> (fzero - 1)
ld a, 1 >> (fzero + 32)
ld a, 1 >>> (fzero - 1)
ld a, 1 >>> (fzero + 32)

View File

@@ -0,0 +1,18 @@
warning: patch-diagnostics.asm(10): [-Wshift-amount]
Shifting right by large amount 32
warning: patch-diagnostics.asm(9): [-Wshift-amount]
Shifting right by negative amount -1
warning: patch-diagnostics.asm(8): [-Wshift-amount]
Shifting right by large amount 32
warning: patch-diagnostics.asm(7): [-Wshift-amount]
Shifting right by negative amount -1
warning: patch-diagnostics.asm(6): [-Wshift]
Shifting right negative value -1
warning: patch-diagnostics.asm(5): [-Wshift-amount]
Shifting left by large amount 32
warning: patch-diagnostics.asm(4): [-Wshift-amount]
Shifting left by negative amount -1
warning: patch-diagnostics.asm(3): [-Wdiv]
Division of -2147483648 by -1 yields -2147483648
warning: patch-diagnostics.asm(3): [-Wtruncation]
Value $80000000 (may be negative?) is not 8-bit

View File

@@ -1,3 +1,4 @@
error: patch-overflow.asm(3): Value 81920 is not 16-bit warning: patch-overflow.asm(3): [-Wtruncation]
error: patch-overflow.asm(2): Value 16384 is not 8-bit Value $14000 is not 16-bit
Linking failed with 2 errors warning: patch-overflow.asm(2): [-Wtruncation]
Value $4000 is not 8-bit

View File

@@ -71,7 +71,7 @@ tryCmpRomSize () {
} }
rgblinkQuiet () { rgblinkQuiet () {
out="$(env $RGBLINK "$@")" || return $? out="$(env $RGBLINK -Weverything "$@")" || return $?
if [[ -n "$out" ]]; then if [[ -n "$out" ]]; then
echo "$bold${red}Linking shouldn't produce anything on stdout!${rescolors}${resbold}" echo "$bold${red}Linking shouldn't produce anything on stdout!${rescolors}${resbold}"
false false