Refactor warnings and errors (#1728)

* Remove `err` and `warn`, keep `errx` and `warnx`, using them in RGBGFX too

* Separate RGBGFX and RGBLINK warnings/errors from main options

* Separate `report` function into `error` and `fatal` messages

* Implicit newlines for most RGBASM errors
This commit is contained in:
Rangi
2025-07-08 12:58:23 -04:00
committed by GitHub
parent 991b74dd0d
commit 35962dedc4
39 changed files with 753 additions and 757 deletions

View File

@@ -19,6 +19,7 @@
#include "link/output.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
struct MemoryLocation {
uint16_t address;

View File

@@ -23,6 +23,7 @@
#include "link/patch.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
bool isDmgMode; // -d
char const *linkerScriptName; // -l
@@ -44,8 +45,6 @@ bool disablePadding; // -x
FILE *linkerScript;
static uint32_t nbErrors = 0;
std::string const &FileStackNode::dump(uint32_t curLineNo) const {
if (data.holds<std::vector<uint32_t>>()) {
assume(parent); // REPT nodes use their parent's name
@@ -69,72 +68,6 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
}
}
void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
where->dump(lineNo);
fputs(": ", stderr);
}
vfprintf(stderr, fmt, args);
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "warning", where, lineNo);
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "error", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[gnu::format(printf, 2, 3)]]
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "FATAL", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
// Short options
static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvwx";
@@ -185,6 +118,19 @@ static void printUsage() {
}
// LCOV_EXCL_STOP
[[gnu::format(printf, 1, 2), noreturn]]
static void fatalWithUsage(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
printUsage();
exit(1);
}
enum ScrambledRegion {
SCRAMBLE_ROMX,
SCRAMBLE_SRAM,
@@ -327,14 +273,6 @@ next: // Can't `continue` a `for` loop with this nontrivial iteration logic
}
}
[[noreturn]]
void reportErrors() {
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
int main(int argc, char *argv[]) {
// Parse options
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
@@ -429,11 +367,7 @@ int main(int argc, char *argv[]) {
// If no input files were specified, the user must have screwed up
if (curArgIndex == argc) {
fputs(
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
);
printUsage();
exit(1);
fatalWithUsage("Please specify an input file (pass `-` to read from standard input)");
}
// Patch the size array depending on command-line options
@@ -461,23 +395,17 @@ int main(int argc, char *argv[]) {
script_ProcessScript(linkerScriptName);
// If the linker script produced any errors, some sections may be in an invalid state
if (nbErrors != 0) {
reportErrors();
}
requireZeroErrors();
}
// then process them,
sect_DoSanityChecks();
if (nbErrors != 0) {
reportErrors();
}
requireZeroErrors();
assign_AssignSections();
patch_CheckAssertions();
// and finally output the result.
patch_ApplyPatches();
if (nbErrors != 0) {
reportErrors();
}
requireZeroErrors();
out_WriteFiles();
}

View File

@@ -24,6 +24,7 @@
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
static std::deque<std::vector<Symbol>> symbolLists;
static std::vector<std::vector<FileStackNode>> nodes;
@@ -445,7 +446,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
file = stdin;
}
if (!file) {
err("Failed to open file \"%s\"", fileName);
errx("Failed to open file \"%s\": %s", fileName, strerror(errno));
}
Defer closeFile{[&] { fclose(file); }};

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include <deque>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,6 +21,7 @@
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
static constexpr size_t BANK_SIZE = 0x4000;
@@ -211,7 +213,7 @@ static void writeROM() {
outputFile = stdout;
}
if (!outputFile) {
err("Failed to open output file \"%s\"", outputFileName);
errx("Failed to open output file \"%s\": %s", outputFileName, strerror(errno));
}
}
Defer closeOutputFile{[&] {
@@ -229,7 +231,7 @@ static void writeROM() {
overlayFile = stdin;
}
if (!overlayFile) {
err("Failed to open overlay file \"%s\"", overlayFileName);
errx("Failed to open overlay file \"%s\": %s", overlayFileName, strerror(errno));
}
}
Defer closeOverlayFile{[&] {
@@ -572,7 +574,7 @@ static void writeSym() {
symFile = stdout;
}
if (!symFile) {
err("Failed to open sym file \"%s\"", symFileName);
errx("Failed to open sym file \"%s\": %s", symFileName, strerror(errno));
}
Defer closeSymFile{[&] { fclose(symFile); }};
@@ -623,7 +625,7 @@ static void writeMap() {
mapFile = stdout;
}
if (!mapFile) {
err("Failed to open map file \"%s\"", mapFileName);
errx("Failed to open map file \"%s\": %s", mapFileName, strerror(errno));
}
Defer closeMapFile{[&] { fclose(mapFile); }};

View File

@@ -14,6 +14,7 @@
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
std::deque<Assertion> assertions;

View File

@@ -30,6 +30,7 @@
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/warning.hpp"
using namespace std::literals;

View File

@@ -17,6 +17,7 @@
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/warning.hpp"
enum NumberType {
HEX = 16, // X
@@ -307,7 +308,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
getToken(nullptr, "'A' line is too short");
tmp = parseNumber(where, lineNo, token, numberType);
if (tmp & (1 << AREA_PAGING)) {
fatal(&where, lineNo, "Internal error: paging is not supported");
fatal(&where, lineNo, "Paging is not supported");
}
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
curSection->isBankFixed = curSection->isAddressFixed;
@@ -430,11 +431,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|| (symbolSection && !symbolSection->isAddressFixed)) {
sym_AddSymbol(symbol); // This will error out
} else if (otherValue != symbolValue) {
fprintf(
stderr,
"error: \"%s\" is defined as %" PRId32 " at ",
symbol.name.c_str(),
symbolValue
errorNoDump(
"\"%s\" is defined as %" PRId32 " at ", symbol.name.c_str(), symbolValue
);
symbol.src->dump(symbol.lineNo);
fprintf(stderr, ", but as %" PRId32 " at ", otherValue);

View File

@@ -10,6 +10,8 @@
#include "error.hpp"
#include "helpers.hpp"
#include "link/warning.hpp"
std::vector<std::unique_ptr<Section>> sectionList;
std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
@@ -22,9 +24,8 @@ void sect_ForEach(void (*callback)(Section &)) {
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
if (target.isAddressFixed) {
if (target.org != org) {
fprintf(
stderr,
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
errorNoDump(
"Section \"%s\" is defined with address $%04" PRIx16 " at ",
target.name.c_str(),
target.org
);
@@ -36,9 +37,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
}
} else if (target.isAlignFixed) {
if ((org - target.alignOfs) & target.alignMask) {
fprintf(
stderr,
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
errorNoDump(
"Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
target.name.c_str(),
target.alignMask + 1,
target.alignOfs
@@ -55,9 +55,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) {
if (target.isAddressFixed) {
if ((target.org - ofs) & other.alignMask) {
fprintf(
stderr,
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
errorNoDump(
"Section \"%s\" is defined with address $%04" PRIx16 " at ",
target.name.c_str(),
target.org
);
@@ -75,9 +74,8 @@ static bool checkAgainstFixedAlign(Section const &target, Section const &other,
return false;
} else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
fprintf(
stderr,
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
errorNoDump(
"Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
target.name.c_str(),
target.alignMask + 1,
target.alignOfs
@@ -131,9 +129,8 @@ static void checkFragmentCompat(Section &target, Section &other) {
static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
if (target.modifier != other->modifier) {
fprintf(
stderr,
"error: Section \"%s\" is defined as SECTION %s at ",
errorNoDump(
"Section \"%s\" is defined as SECTION %s at ",
target.name.c_str(),
sectionModNames[target.modifier]
);
@@ -143,16 +140,15 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
putc('\n', stderr);
exit(1);
} else if (other->modifier == SECTION_NORMAL) {
fprintf(stderr, "error: Section \"%s\" is defined at ", target.name.c_str());
errorNoDump("Section \"%s\" is defined at ", target.name.c_str());
target.src->dump(target.lineNo);
fputs(", but also at ", stderr);
other->src->dump(other->lineNo);
putc('\n', stderr);
exit(1);
} else if (target.type != other->type) {
fprintf(
stderr,
"error: Section \"%s\" is defined with type %s at ",
errorNoDump(
"Section \"%s\" is defined with type %s at ",
target.name.c_str(),
sectionTypeInfo[target.type].name.c_str()
);
@@ -168,9 +164,8 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
target.isBankFixed = true;
target.bank = other->bank;
} else if (target.bank != other->bank) {
fprintf(
stderr,
"error: Section \"%s\" is defined with bank %" PRIu32 " at ",
errorNoDump(
"Section \"%s\" is defined with bank %" PRIu32 " at ",
target.name.c_str(),
target.bank
);

View File

@@ -11,6 +11,7 @@
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/warning.hpp"
std::unordered_map<std::string, Symbol *> symbols;
std::unordered_map<std::string, std::vector<Symbol *>> localSymbols;
@@ -36,7 +37,7 @@ void sym_AddSymbol(Symbol &symbol) {
// Check if the symbol already exists with a different value
if (other && !(symValue && otherValue && *symValue == *otherValue)) {
fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str());
errorNoDump("\"%s\" is defined as ", symbol.name.c_str());
if (symValue) {
fprintf(stderr, "%" PRId32, *symValue);
} else {

92
src/link/warning.cpp Normal file
View File

@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
#include "link/warning.hpp"
#include <inttypes.h>
#include <stdarg.h>
#include "link/main.hpp"
static uint32_t nbErrors = 0;
static void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
where->dump(lineNo);
fputs(": ", stderr);
}
vfprintf(stderr, fmt, args);
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "warning", where, lineNo);
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "error", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
void errorNoDump(char const *fmt, ...) {
va_list args;
fputs("error: ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "FATAL", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
void requireZeroErrors() {
if (nbErrors != 0) {
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
}