aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-21 14:03:31 +0000
committer_ov4 <ov4@ov4.org>2026-10-21 14:03:31 +0000
commitb4ffb2d9498560557821714e2d4651279dd6817c (patch)
tree995ad02bdb8cc461f48a53683c4fd7f2ddd8656b /include
parentf1f5fa394a67a789914ccf2b24d534a0e466ae1b (diff)
feat(diagnostics): print source diagnostics in gcc file:line: layout
Lexer, parser, driver and codegen diagnostics now read "<path>:<line>: error: <message>" instead of "error: <message> at line N in <path>". 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 <noreply@anthropic.com>
Diffstat (limited to 'include')
-rw-r--r--include/cps/CodeGen.h2
-rw-r--r--include/cps/Diagnostics.h66
-rw-r--r--include/cps/FunctionGen.h2
3 files changed, 36 insertions, 34 deletions
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 <cstdarg>
#include <cstdio>
#include <string>
-#include <vector>
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: "<locus>: <label>: "; an empty locus prints the label alone.
+inline std::string diagnosticPrefix(Severity S, const std::string &Locus = "cpsc") {
+ const char *Label = S == Severity::Error ? "error: "
+ : S == Severity::Warning ? "warning: "
+ : S == Severity::Note ? "note: "
+ : "fatal error: ";
+ return (Locus.empty() ? "" : Locus + ": ") + Label;
}
-inline void sourceDiagnostic(int Line, const char *Format, ...) {
- va_list Args, Copy;
+
+// Prints "<path>:<line>: <label>: <message>\n" (just "<path>:" when the line is
+// unknown, "cpsc:" when no source is set).
+inline void vsourceDiagnostic(Severity S, int Line, const char *Format, va_list Args) {
+ std::string Locus = DiagnosticSource.empty() ? "cpsc" : DiagnosticSource;
+ if (!DiagnosticSource.empty() && Line > 0) Locus += ":" + std::to_string(Line);
+ std::fputs(diagnosticPrefix(S, Locus).c_str(), stderr);
+ std::vfprintf(stderr, Format, Args);
+ std::fputc('\n', stderr);
+}
+
+__attribute__((format(printf, 2, 3)))
+inline void sourceError(int Line, const char *Format, ...) {
+ va_list Args;
+ va_start(Args, Format);
+ vsourceDiagnostic(Severity::Error, Line, Format, Args);
+ va_end(Args);
+}
+
+__attribute__((format(printf, 2, 3)))
+inline void sourceWarning(int Line, const char *Format, ...) {
+ va_list Args;
va_start(Args, Format);
- va_copy(Copy, Args);
- int Size = std::vsnprintf(nullptr, 0, Format, Copy);
- va_end(Copy);
- if (Size < 0) { va_end(Args); return; }
- std::vector<char> Buffer(Size + 1);
- std::vsnprintf(Buffer.data(), Buffer.size(), Format, Args);
+ vsourceDiagnostic(Severity::Warning, Line, Format, Args);
va_end(Args);
- std::string Message(Buffer.data(), Size);
- if (!DiagnosticSource.empty()) {
- auto At = Message.find("at line ");
- if (At != std::string::npos) {
- auto End = Message.find_first_not_of("0123456789", At + 8);
- Message.insert(End == std::string::npos ? Message.size() : End,
- " in " + DiagnosticSource);
- } else {
- auto Old = Message.find("(line ");
- if (Old != std::string::npos) {
- auto End = Message.find(')', Old);
- Message.replace(Old, End - Old + 1, lineSuffix(Line).substr(1));
- } else {
- auto End = Message.find_last_not_of("\n");
- Message.insert(End == std::string::npos ? 0 : End + 1, lineSuffix(Line));
- }
- }
- }
- std::fputs(Message.c_str(), stderr);
}
}
diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h
index a50e70b..a176e84 100644
--- a/include/cps/FunctionGen.h
+++ b/include/cps/FunctionGen.h
@@ -72,7 +72,7 @@ private:
IdentifierMap<SymbolInfo> &Symbols;
bool &HadError;
int &DiagnosticLine;
- void reportError(const char *Fmt, ...);
+ __attribute__((format(printf, 2, 3))) void reportError(const char *Fmt, ...);
IdentifierMap<std::deque<FuncSig>> Signatures;
IdentifierMap<std::pair<std::string, size_t>> LinkIndex;