feat(diagnostics): runtime-error carets + token-precise LSP ranges (#407 residual)#541
Merged
Merged
Conversation
residual) Closes #407 Uncaught runtime errors now print the same one-line source excerpt + ^ caret as parse errors, column-attributed to the failing token: $ eigenscript bad.eigs Error line 2: index 9 out of range (list length 2) 2 | print of x[9] | ^ at <module> (line 2) Mechanism (zero dispatch-loop cost, no OP_LINE/tape change): - Per-byte cols[] table beside the chunk's lines[], stamped by a save/set/restore position cursor in compile_node — post-order child emission can't clobber the parent's column. - Fallible-node columns anchored in the parser: subscripts at '[', binops/relations/unaries at the operator token (captured before p_advance), dots at the key token. - Refcounted per-unit source blob (EigsSrcBuf) on chunks, shared by nested fn/lambda chunks — REPL lines, eval'd strings, and imported modules each excerpt their own source after the caller frees its buffer. - Uncaught-error printing deferred from rt_error/builtin_throw raise time to the dispatch loop's CHECK_ERROR, where the live ip gives the failing instruction's offset. lines[off] == g_error_line guards the caret: if the position can't be attributed confidently (JIT-advanced ip), the caret is suppressed rather than misplaced. Message and stack-trace lines stay byte-unchanged; carets are additive. LSP diagnostics are now token-precise: E002 (parse) squiggles the recorded token span (new first_error_len), E003 (undefined name) squiggles the identifier via new col/len fields on LintDiag/ lint_error_at. The 0..1000 whole-line span remains only as the no-position fallback for unmigrated W-rules. Pinned: examples/errors/* runtime cases declare their caret column via a new '# expect-caret:' directive in test_error_examples.sh; EM26/EM27 pin the runtime excerpt + caret; tests/test_lsp.py pins E002/E003 ranges (71 checks). docs/DIAGNOSTICS.md + docs/SPEC.md updated in the same change. Validation: release suite 2731/2731; ASan+UBSan with detect_leaks=1 green through every executable section ([87] closure-cycle strict gate leak-clean, zero LeakSanitizer reports); LSP suite 71/71. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdDAVfZHgoQ2UNP63FYUXm
The #407 compile_ast signature change (src param for caret excerpts) missed fuzz/fuzz_stdin.c and fuzz/fuzz_eigenscript.c — the CI fuzz smoke's make fuzz failed to compile. Pass each harness's input buffer so fuzzed programs exercise the caret path too. Also size LintDiag.severity to LintWarning.level (16) — the fuzz build's -Wall flagged the 16-into-12 snprintf copy in lint_collect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdDAVfZHgoQ2UNP63FYUXm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #407
The two residual halves of #407, in one change:
1. Runtime-error carets
Uncaught runtime errors now print the same one-line source excerpt +
^caret as parse errors, with the column attributed to the failing token — the[of an out-of-range subscript, the operator of a type mismatch, the undefined identifier, theofof a bad call:Mechanism (zero dispatch-loop cost, no OP_LINE policy or tape change):
cols[]table beside the chunk'slines[], stamped by a save/set/restore position cursor incompile_node(post-order child emission can't clobber the parent's column).[, binops/relations/unaries at the operator token (captured beforep_advance— they were previously stamped with whatever token followed the right operand), dots at the key token.EigsSrcBuf) on chunks, shared by nested fn/lambda chunks, so REPL lines, eval'd strings, and imported modules each excerpt their own source even after the caller frees its buffer.rt_error/builtin_throw) to the dispatch loop'sCHECK_ERROR, where the liveipgives the failing instruction's byte offset. Alines[off] == g_error_lineguard suppresses the caret whenever the position can't be attributed confidently (e.g. JIT-advanced ip) — a caret never points at the wrong line. TheError line N:message andat fn (line N)trace lines are byte-unchanged; the excerpt block is purely additive.Verified precise across: index/type/undefined/call errors, function bodies (trace order preserved), imported modules (module's own source),
evalstrings, REPL lines, task-death reports,throw, and errors raised inside a 90k-iteration OSR-hot loop.2. Token-precise LSP ranges
first_error_len); the squiggle spans exactly the offending token.LintDiag/LintWarninggaincol/len, populated by a newlint_error_atat the E003 sites; the squiggle lands on the identifier.0..1000whole-line span remains only as the fallback for diagnostics without a tracked position (unmigrated W-rules).Pins & docs
examples/errors/*runtime cases declare their caret column via a new# expect-caret:directive (checker extended); a column-attribution regression now fails the corpus.EM26/EM27pin the runtime excerpt + caret in the main suite;tests/test_lsp.pypins the E002/E003 ranges (71 checks).docs/DIAGNOSTICS.md(byte-exact contract) +docs/SPEC.mdupdated in the same change.Validation
detect_leaks=1: green through every executable section — [87] closure-cycle strict gate leak-clean, all 14 error examples, zero LeakSanitizer reports. (The box's background runner kept reaping the run at section [97]; the remaining [99]/[99b] are mechanical doc greps, green in the release run.)🤖 Generated with Claude Code
https://claude.ai/code/session_01GdDAVfZHgoQ2UNP63FYUXm