From b4ffb2d9498560557821714e2d4651279dd6817c Mon Sep 17 00:00:00 2001 From: _ov4 Date: Wed, 21 Oct 2026 14:03:31 +0000 Subject: feat(diagnostics): print source diagnostics in gcc file:line: layout Lexer, parser, driver and codegen diagnostics now read ":: error: " instead of "error: at line N in ". The path is always printed as given on the command line. Call sites pass the bare message to cps::sourceError/sourceWarning, which carry printf format checking; reportError routes through the same helper. The suite iii harness parses the new layout and matches the reason against the message only. iii/template-class-pack-field passed only because its reason regex matched the case path, so it is now case-insensitive. Co-Authored-By: Claude Opus 5.5 --- AGENTS/ARCH/DRIVER.md | 5 +- AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md | 9 ++- AGENTS/TESTING.md | 2 +- doc/cpsc.1.md | 2 +- include/cps/CodeGen.h | 2 +- include/cps/Diagnostics.h | 66 +++++++++--------- include/cps/FunctionGen.h | 2 +- lib/CodeGen/ArithmeticHandler.cc | 8 +-- lib/CodeGen/CodeGen.cc | 7 +- lib/CodeGen/ConstantGen.cc | 4 +- lib/CodeGen/FunctionGen.cc | 7 +- lib/Lexer/Lexer.cc | 14 ++-- lib/Parser/Parser.cc | 34 +++++----- lib/Parser/ParserControlFlow.cc | 34 +++++----- lib/Parser/ParserExpression.cc | 37 +++++----- lib/Parser/ParserFile.cc | 15 ++--- lib/Parser/ParserFunction.cc | 97 +++++++++++++-------------- lib/Parser/ParserType.cc | 97 +++++++++++++-------------- tests/iii/template-class-pack-field/case.json | 2 +- tests/run.pl | 13 ++-- tests/test_runner.pl | 27 +++++--- tools/driver/main.cc | 28 ++++---- 22 files changed, 257 insertions(+), 255 deletions(-) diff --git a/AGENTS/ARCH/DRIVER.md b/AGENTS/ARCH/DRIVER.md index af3925a..d571509 100644 --- a/AGENTS/ARCH/DRIVER.md +++ b/AGENTS/ARCH/DRIVER.md @@ -34,8 +34,9 @@ style is in [CONVENTIONS/SYMBOLS_AND_ERRORS.md](../CONVENTIONS/SYMBOLS_AND_ERROR also return 1. Ordinary compiler errors use `error:`; failures that prevent compilation, such as file I/O errors, use `fatal error:`. The driver prints `compilation terminated.` once on compilation failure, including argument errors. - Multi-input lexer/parser/codegen diagnostics append the source path after the line number; - single-input diagnostics (Diagnostics.h) do not append it. + Lexer/parser/codegen diagnostics use the gcc layout `:: error: ` + (Diagnostics.h), with the path as given on the command line; driver-level errors + use `cpsc: error:` / `cpsc: fatal error:`. Execution-stage failures do not print that line. Check the exit code rather than grepping stderr. - Successful compilation overwrites the output, but paths aliasing any input source diff --git a/AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md b/AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md index 3e3c3ad..e21bed1 100644 --- a/AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md +++ b/AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md @@ -42,8 +42,13 @@ keep compiling.** No exceptions, no exit mid-compile. - `Lexer`/`Parser`/`CodeGen` each expose `hadError()`; AST expressions/statements share source-line metadata. Parser entry points record token locations. CodeGen - saves/restores its diagnostic line across recursive emission and coercion; - `reportError` appends `at line N`. + saves/restores its diagnostic line across recursive emission and coercion. +- Diagnostics use the gcc layout `:: error: ` (`:` when + the line is unknown). Lexer/parser/driver call `cps::sourceError(Line, fmt, ...)` + (`sourceWarning` for warnings) from `Diagnostics.h` with the bare message: no + `error:` label, location or newline. `reportError` routes through the same helper + with `DiagnosticLine`. `DiagnosticSource` holds the unit path as given on the + command line. - `CodeGen::reportError(fmt, ...)` is the helper for codegen-side diagnostics (`ArrayHandler` reaches it through its `CodeGen &` parameter, `FunctionGen`/`ArithmeticHandler` hold a `bool &HadError` reference). diff --git a/AGENTS/TESTING.md b/AGENTS/TESTING.md index a43d0fb..f3628d2 100644 --- a/AGENTS/TESTING.md +++ b/AGENTS/TESTING.md @@ -2,7 +2,7 @@ Compiler tests live in `tests/`; run instructions are in its README. `BUILD_TESTING` defaults to OFF and uses the compiler build's Perl interpreter with core modules when enabled. CMake builds independent C++ reference executables with the same host ABI and registers one CTest entry per `case.json`, plus harness and generator checks. -Each case directory has `program.cps` and `case.json`. Suite i adds `expected.txt`, optional `input.txt`, input files under `files/`, expected text-file contents under `expected-files/`, and independently written trace notes with human-review status. Suite ii adds `reference.cc` and `generate.pl`; the generator accepts seed and random count, emits the total record count followed by one ASCII record per line, and each program emits one result line per record. Suite iii stores an `errors` list of `{reason: regex, line: integer}` in its manifest. A cause and its line must appear on the same error diagnostic. EOF after a final newline is the following source line. +Each case directory has `program.cps` and `case.json`. Suite i adds `expected.txt`, optional `input.txt`, input files under `files/`, expected text-file contents under `expected-files/`, and independently written trace notes with human-review status. Suite ii adds `reference.cc` and `generate.pl`; the generator accepts seed and random count, emits the total record count followed by one ASCII record per line, and each program emits one result line per record. Suite iii stores an `errors` list of `{reason: regex, line: integer}` in its manifest. A cause and its line must appear on the same `:: error: ` diagnostic; the reason is matched against the message. EOF after a final newline is the following source line. Multi-file cases specify `sources` as an ordered array of paths relative to the case directory, defaulting to `["program.cps"]`. Library sources can be in nested diff --git a/doc/cpsc.1.md b/doc/cpsc.1.md index 8359a25..b156d6f 100644 --- a/doc/cpsc.1.md +++ b/doc/cpsc.1.md @@ -117,7 +117,7 @@ An uncaught program exception returns 1 after printing `Unhandled exception. Pro # NOTES -Compiler diagnostics go to stderr. With multiple input files, lexer, parser and code generation diagnostics include the source path after the line number. Failed source compilation does not write output; failed native linking does not replace an existing output file. Successful compilation overwrites an existing output file, but an output path that refers to any input source file (including a symbolic or hard link) is rejected. Runtime diagnostics of the compiled program (for example `[Fatal] line N: ...` and `Unhandled exception. Program terminated.`) are also written to stderr, so `OUTPUT` text alone appears on stdout. +Compiler diagnostics go to stderr in the gcc layout `file:line: error: message`, where `file` is the source path as given on the command line; diagnostics that are not tied to a source file start with `cpsc:`. Failed source compilation does not write output; failed native linking does not replace an existing output file. Successful compilation overwrites an existing output file, but an output path that refers to any input source file (including a symbolic or hard link) is rejected. Runtime diagnostics of the compiled program (for example `[Fatal] line N: ...` and `Unhandled exception. Program terminated.`) are also written to stderr, so `OUTPUT` text alone appears on stdout. Missing input, including invoking `cpsc` without arguments, reports `cpsc: fatal error: no input files`. Ordinary lexical, syntax and semantic errors use `error:`; failures that prevent compilation, such as file I/O errors, use `fatal error:`. Compilation and argument failures end with one `compilation terminated.` line. Execution-stage failures do not print that line. diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index 4730585..6477406 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -240,7 +240,7 @@ public: void print(llvm::raw_ostream &Output); const llvm::Module &getModule() const { return *TheModule; } bool hadError() const { return HadError; } - void reportError(const char *Fmt, ...); + __attribute__((format(printf, 2, 3))) void reportError(const char *Fmt, ...); static bool isBuiltinName(const std::string &Name); llvm::Value *emitExpr(ExprAST *Expr); diff --git a/include/cps/Diagnostics.h b/include/cps/Diagnostics.h index 6f26292..3492f46 100644 --- a/include/cps/Diagnostics.h +++ b/include/cps/Diagnostics.h @@ -2,43 +2,45 @@ #include #include #include -#include namespace cps { -// Empty for single-source builds to preserve existing diagnostic text. +enum class Severity { Error, Warning, Note, Fatal }; + +// Path of the unit being diagnosed, as given on the command line. inline std::string DiagnosticSource; -inline std::string lineSuffix(int Line) { - return " at line " + std::to_string(Line) + - (DiagnosticSource.empty() ? "" : " in " + DiagnosticSource); + +// gcc layout: ":