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

@@ -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;

View File

@@ -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 &section, 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 &section, 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
);
}

View File

@@ -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);
}