fix(diagnostics): locate import parse errors in the imported file

A parse error raised while resolving an `#import` was rendered against the
ROOT file's source — the caret landed on an unrelated line (often a comment)
even though the message named the correct imported file.

Two compounding causes:
- core.zig wired `diagnostics.import_sources` only AFTER import resolution
  returned, but a parse error aborts mid-resolution (before that wiring), so
  the renderer had no imported sources and fell back to the root file. Wire it
  (and seed the main-file source) BEFORE resolving.
- imports.zig emitted the diagnostic at the importer's `#import` span instead
  of the parser's actual error offset inside the imported file, and didn't pin
  the diagnostic's source_file to that file.

parser.zig now records `err_end` alongside `err_offset` for a proper caret
width. New `DiagnosticList.addFmtInFile` renders against an explicit source
file; imports.zig uses it with `importErrSpan(&p)`.

Regression test: examples/1176-diagnostics-import-parse-error-location
(importer + deliberately-broken companion; caret must land in the companion).
This commit is contained in:
agra
2026-06-15 15:09:40 +03:00
parent fe9bd75e09
commit d6a9c4f0c4
9 changed files with 66 additions and 7 deletions

View File

@@ -106,6 +106,16 @@ pub const Compilation = struct {
var chain = std.StringHashMap(void).init(self.allocator);
var cache = imports.ModuleCache.init(self.allocator);
const base_dir = imports.dirName(self.file_path);
// Wire import_sources to diagnostics BEFORE resolving imports, so a parse
// error in an imported file (reported mid-resolution, which then aborts
// before the post-resolution wiring below) can resolve its caret against
// the imported file's OWN source. The map pointer is stable; per-file
// entries fill in as imports load. The main-file source is seeded here
// too so a root-file diagnostic resolves identically.
self.import_sources.put(self.file_path, self.source) catch {};
self.diagnostics.import_sources = &self.import_sources;
const mod = imports.resolveImports(
self.allocator,
self.io,
@@ -145,11 +155,8 @@ pub const Compilation = struct {
// `namespace_edges` in place to record each target's member ids.
self.decl_table = try imports.buildDeclTable(self.allocator, self.file_path, mod, &cache, &self.module_decls, &self.namespace_edges);
// Store main file source in import_sources so error reporting can find it
self.import_sources.put(self.file_path, self.source) catch {};
// Wire import_sources to diagnostics for file-aware error rendering
self.diagnostics.import_sources = &self.import_sources;
// (import_sources ↔ diagnostics wiring + main-file seed now done before
// resolution, above, so mid-resolution parse errors render correctly.)
// Build a root node from the resolved module's decls
const new_root = try self.allocator.create(Node);