mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement warning diagnostic flags for RGBLINK (#1754)
This commit is contained in:
@@ -69,7 +69,7 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
||||
}
|
||||
|
||||
// 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
|
||||
// Please keep in the same order as short opts.
|
||||
@@ -92,6 +92,7 @@ static option const longopts[] = {
|
||||
{"tiny", no_argument, nullptr, 't'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"verbose", no_argument, nullptr, 'v'},
|
||||
{"warning", required_argument, nullptr, 'W'},
|
||||
{"wramx", no_argument, nullptr, 'w'},
|
||||
{"nopad", no_argument, nullptr, 'x'},
|
||||
{nullptr, no_argument, nullptr, 0 }
|
||||
@@ -347,6 +348,9 @@ int main(int argc, char *argv[]) {
|
||||
beVerbose = true;
|
||||
break;
|
||||
// LCOV_EXCL_STOP
|
||||
case 'W':
|
||||
warnings.processWarningFlag(musl_optarg);
|
||||
break;
|
||||
case 'w':
|
||||
isWRAM0Mode = true;
|
||||
break;
|
||||
|
||||
@@ -33,6 +33,25 @@ static void pushRPN(int32_t value, bool comesFromError) {
|
||||
// has popped any values with the error flag set.
|
||||
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) {
|
||||
if (rpnStack.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:
|
||||
value = popRPN(patch);
|
||||
if (value == 0) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Division by 0");
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Division by 0");
|
||||
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 {
|
||||
value = op_divide(popRPN(patch), value);
|
||||
value = op_divide(lval, value);
|
||||
}
|
||||
break;
|
||||
case RPN_MOD:
|
||||
value = popRPN(patch);
|
||||
if (value == 0) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Modulo by 0");
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Modulo by 0");
|
||||
popRPN(patch);
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -127,10 +149,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_EXP:
|
||||
value = popRPN(patch);
|
||||
if (value < 0) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Exponent by negative value %" PRId32, value);
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Exponent by negative value %" PRId32, value);
|
||||
popRPN(patch);
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -204,14 +223,49 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
|
||||
case RPN_SHL:
|
||||
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);
|
||||
break;
|
||||
case RPN_SHR:
|
||||
case RPN_SHR: {
|
||||
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;
|
||||
}
|
||||
case RPN_USHR:
|
||||
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);
|
||||
break;
|
||||
|
||||
@@ -324,14 +378,13 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_HRAM:
|
||||
value = popRPN(patch);
|
||||
if (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Address $%" PRIx32 " for LDH is not in HRAM range", value);
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Address $%" PRIx32 " for LDH is not in HRAM range", value);
|
||||
value = 0;
|
||||
} else if (value >= 0 && value <= 0xFF) {
|
||||
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;
|
||||
@@ -341,10 +394,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
value = popRPN(patch);
|
||||
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
||||
if (value & ~0x38) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Value $%" PRIx32 " is not a RST vector", value);
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Value $%" PRIx32 " is not a RST vector", value);
|
||||
value = 0;
|
||||
}
|
||||
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);
|
||||
// Acceptable values are 0 to 7
|
||||
if (value & ~0x07) {
|
||||
if (!isError) {
|
||||
errorAt(patch, "Value $%" PRIx32 " is not a bit index", value);
|
||||
isError = true;
|
||||
}
|
||||
firstErrorAt(patch, "Value $%" PRIx32 " is not a bit index", value);
|
||||
value = 0;
|
||||
}
|
||||
value = mask | (value << 3);
|
||||
@@ -437,6 +484,7 @@ void patch_CheckAssertions() {
|
||||
case ASSERT_WARN:
|
||||
warningAt(
|
||||
assert.patch,
|
||||
WARNING_ASSERT,
|
||||
"%s",
|
||||
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
||||
);
|
||||
@@ -486,8 +534,8 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
||||
uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
|
||||
int16_t jumpOffset = value - address;
|
||||
|
||||
if (!isError && (jumpOffset < -128 || jumpOffset > 127)) {
|
||||
errorAt(
|
||||
if (jumpOffset < -128 || jumpOffset > 127) {
|
||||
firstErrorAt(
|
||||
patch,
|
||||
"JR target must be between -128 and 127 bytes away, not %" PRId16
|
||||
"; use JP instead",
|
||||
@@ -497,12 +545,13 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
||||
dataSection.data[offset] = jumpOffset & 0xFF;
|
||||
} else {
|
||||
// Patch a certain number of bytes
|
||||
if (!isError && (value < type.min || value > type.max)) {
|
||||
errorAt(
|
||||
if (value < type.min || value > type.max) {
|
||||
diagnosticAt(
|
||||
patch,
|
||||
"Value %" PRId32 "%s is not %u-bit",
|
||||
WARNING_TRUNCATION,
|
||||
"Value $%" PRIx32 "%s is not %u-bit",
|
||||
value,
|
||||
value < 0 ? " (maybe negative?)" : "",
|
||||
value < 0 ? " (may be negative?)" : "",
|
||||
type.size * 8U
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,43 @@
|
||||
|
||||
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(
|
||||
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);
|
||||
if (src) {
|
||||
src->dump(lineNo);
|
||||
fputs(": ", stderr);
|
||||
}
|
||||
if (flagfmt) {
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
fputs("\n ", stderr);
|
||||
}
|
||||
vfprintf(stderr, fmt, args);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
@@ -42,21 +71,21 @@ static void abortLinking(char const *verb) {
|
||||
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(src, lineNo, fmt, args, "warning");
|
||||
printDiag(src, lineNo, fmt, args, "warning", nullptr, 0);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void warning(char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(nullptr, 0, fmt, args, "warning");
|
||||
printDiag(nullptr, 0, fmt, args, "warning", nullptr, 0);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(src, lineNo, fmt, args, "error");
|
||||
printDiag(src, lineNo, fmt, args, "error", nullptr, 0);
|
||||
va_end(args);
|
||||
|
||||
incrementErrors();
|
||||
@@ -65,7 +94,7 @@ void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
|
||||
void error(char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(nullptr, 0, fmt, args, "error");
|
||||
printDiag(nullptr, 0, fmt, args, "error", nullptr, 0);
|
||||
va_end(args);
|
||||
|
||||
incrementErrors();
|
||||
@@ -96,7 +125,7 @@ void argErr(char flag, char const *fmt, ...) {
|
||||
void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(src, lineNo, fmt, args, "FATAL");
|
||||
printDiag(src, lineNo, fmt, args, "FATAL", nullptr, 0);
|
||||
va_end(args);
|
||||
|
||||
incrementErrors();
|
||||
@@ -107,7 +136,7 @@ void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...) {
|
||||
void fatal(char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printDiag(nullptr, 0, fmt, args, "FATAL");
|
||||
printDiag(nullptr, 0, fmt, args, "FATAL", nullptr, 0);
|
||||
va_end(args);
|
||||
|
||||
incrementErrors();
|
||||
@@ -119,3 +148,27 @@ void requireZeroErrors() {
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user