Skip to content

cli+syntax: name the right file + line in multi-file diagnostics#76

Merged
rileyr merged 1 commit into
mainfrom
diag-multifile-spans
Jun 9, 2026
Merged

cli+syntax: name the right file + line in multi-file diagnostics#76
rileyr merged 1 commit into
mainfrom
diag-multifile-spans

Conversation

@rileyr

@rileyr rileyr commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

The bug (reported by the fathom session)

A hale build <dir> (or file-with-imports) merges every file's items into one program, but each file's spans are byte offsets into its own source. Diagnostics were rendered against an arbitrary sources.values().next() source, so an error in an imported file printed a bogus line with no filename — fathom saw 317:1 for an error that was really at apps/mdgw/bybit/main.hl:322, and lost hours localizing it by diffing gateways and grepping shared imports by line count.

The fix

Parse each file at a distinct virtual base offset so merged spans are globally unique, then demultiplex a diagnostic's span back to its file at render time. No combined source string is built — base is virtual.

  • hale-syntax: parse_source_at(source, base) lexes, shifts every token (and diagnostic) span by base, then parses, so AST spans land in a process-wide coordinate space. Adds Pos/Span/Diag::shifted + Diag::render_located(path, source, base) (un-shifts + prefixes the path).
  • hale-cli: parse_files / parse_with_imports assign each file a base (running total) and return a (base, path, len) table; both build paths thread it. render_located(diag, file_bases, sources) finds the file whose range contains the span and prints path:line:col: kind: message.

Result

Before: 317:1: type error: error not addressed: … (wrong line, no file).
After: apps/mdgw/bybit/main.hl:322:17: type error: error not addressed: … — and /tmp/bug2test/lib.hl:7:9 for an error in an imported file (verified). Single-file builds are unchanged (base 0 = no shift) but now also show the filename.

Tests

crates/hale-syntax/tests/parse_source_at.rs — shift + un-shift round-trip. Full hale-syntax suite (82 span-sensitive tests) + hale-types/codegen suites stay green.

Context

This is bug #2 of a fathom gateway report. Bug #1 ("error not addressed" on the gateways) was not a compiler regression — the gateways called () fallible(WsError) WS methods without an or disposition (alpaca, same lib, built fine because it addressed them); the fathom session has since cleaned up their code with or discard. This PR only fixes the diagnostic-quality bug that made it hard to find.

🤖 Generated with Claude Code

A `hale build <dir>` (or file-with-imports) merges every file's items
into one program, but each file's spans are byte offsets into its OWN
source. Diagnostics were rendered against an arbitrary `sources.values()
.next()` source, so an error in an imported file printed a bogus line
with no filename (fathom saw `317:1` for an error that was really in a
different file — hours lost localizing it by diffing gateways).

Fix: parse each file at a distinct virtual `base` offset so merged spans
are globally unique, then demultiplex a diagnostic's span back to its
file at render time.

  - hale-syntax: `parse_source_at(source, base)` lexes, shifts every
    token (and diagnostic) span by `base`, then parses — so the AST
    spans land in a process-wide coordinate space. `Pos/Span/Diag::
    shifted` + `Diag::render_located(path, source, base)` (un-shifts and
    prefixes the path). No combined source string; `base` is virtual.
  - hale-cli: `parse_files` / `parse_with_imports` assign each file a
    base (running total) and return a `(base, path, len)` table; both
    build paths thread it. `render_located(diag, file_bases, sources)`
    finds the file whose range contains the span and renders
    `path:line:col: kind: message`. Single-file builds are unchanged
    (base 0 = no shift) but now also show the filename.

Now an imported-file error reads e.g. `lib/foo.hl:7:9: type error: ...`.
Regression test in crates/hale-syntax/tests/parse_source_at.rs (shift +
un-shift round-trip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rileyr rileyr merged commit ad91b7a into main Jun 9, 2026
14 checks passed
@rileyr rileyr deleted the diag-multifile-spans branch June 9, 2026 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant