Section 04: Diagnostic Ordering & Suppression
Status: Not Started
Goal: Make diagnostic output deterministic and suppress cascading errors. Currently DiagnosticQueue in ori_diagnostic/src/queue/mod.rs (394 lines) deduplicates by (line, message_prefix_hash) for non-syntax errors and (line) for syntax errors. It has follow-on filtering for “invalid operand”/“invalid type”/<error> strings and error-limit capping. Missing: (error_code, span) dedup, child-span TyError suppression, and cross-file deterministic ordering.
Success Criteria:
- Dedup uses (error_code, primary_span) — satisfies mission criterion “deterministic diagnostics”
- Child-span suppression eliminates 50%+ of cascading errors in error-heavy test files
-
diagnostic.mdcontains the ordering rule — satisfies mission criterion “rules documented” - All diagnostic snapshot tests pass or are blessed with objectively better output
Context: Research found that DiagnosticQueue already sorts labels within a single diagnostic by span (sort_by_key(|l| l.span.start) at emitter/terminal/mod.rs:361). The top-level diagnostic emission sorts by (line, column). But dedup uses line+message_prefix_hash instead of (error_code, span) — this means structurally identical errors at different columns on the same line can be deduplicated when they shouldn’t be (false positive), and identical errors across different scopes may not be (false negative). The child-span suppression referenced in typeck.md §ER-4 and impl-hygiene.md §Aspirational Patterns is documented as a goal but not fully implemented.
Reference implementations:
- Rust
rustc_errors: dedup by(error_code, primary_span),DelayedBugfor follow-on suppression - Gleam
test-helpers-rs/src/lib.rs:72-115: sort warnings before rendering for snapshot stability
Depends on: Section 01.
04.1 Upgrade Dedup to (ErrorCode, Span)
File(s): compiler/ori_diagnostic/src/queue/mod.rs
-
Write failing test: two diagnostics with same error code and span but different messages — should be deduplicated. Two diagnostics with same line but different error codes — should NOT be deduplicated.
-
Modify
DiagnosticQueueto use(ErrorCode, Span)as the dedup key instead of(line, message_prefix_hash):ErrorCodeis the numeric code (E2001, E2003, etc.)Spanis the primary label’s span (start + end positions)- Keep the existing error-limit cap and soft-error suppression logic
- The existing
(line)dedup for syntax errors should use(E1xxx, span)instead
-
Verify all existing diagnostic tests pass — bless any that produce genuinely better output
-
Subsection close-out (04.1) — MANDATORY before starting 04.2:
- All tasks above are
[x] - Update this subsection’s
statusin section frontmatter tocomplete - Run
/improve-toolingretrospectively on THIS subsection - Run
/sync-claudeto check if code changes affect CLAUDE.md or rules files
- All tasks above are
04.2 Child-Span TyError Suppression
File(s): compiler/ori_diagnostic/src/queue/mod.rs, compiler/ori_types/src/type_error/
Implement Rust-style child-span suppression: when an error at span S produces TyError, suppress subsequent errors at spans contained within S that mention TyError. Users see the root cause, not the cascade.
-
Write failing test: program with one type error that currently produces 3+ cascading errors — after fix, should produce 1
-
Add span-containment check to
DiagnosticQueue:- Track a
suppressed_spans: Vec<Span>— spans where TyError was the root cause - Before adding a new diagnostic, check if its primary span is contained within any suppressed span AND the diagnostic’s message references
<error>or TyError - If yes: drop the diagnostic (don’t add to queue)
typeck.md §ER-4already specifies this behavior — this implements it
- Track a
-
Count suppressed diagnostics in test output (for verification — “suppressed N cascading errors”)
-
Verify error-heavy test files produce fewer, more focused diagnostics
-
Subsection close-out (04.2) — MANDATORY before starting 04.3:
- All tasks above are
[x] - Update this subsection’s
statusin section frontmatter tocomplete - Run
/improve-toolingretrospectively on THIS subsection - Run
/sync-claudeto check if code changes affect CLAUDE.md or rules files
- All tasks above are
04.3 Deterministic Ordering Rule
File(s): .claude/rules/diagnostic.md, compiler/ori_diagnostic/src/queue/mod.rs
-
Add rule to
diagnostic.mdrequiring:- Diagnostics SHALL be emitted in a canonical order: primary span start position (ascending), then error code (ascending) for same-position ties
- Labels and notes within a diagnostic SHALL be ordered by span position (already true —
sort_by_key(|l| l.span.start)at terminal/mod.rs:361) - Cross-file diagnostics SHALL be ordered by file path (lexicographic), then by span within file
- Snapshot tests SHALL compare normalized, deterministic diagnostic output
-
Verify
DiagnosticQueue::flush()already sorts by (line, column) — if yes, upgrade to (file, line, column, error_code) for full determinism -
Add a semantic pin test: compile a multi-error program and assert the exact error order in the output
-
Subsection close-out (04.3) — MANDATORY before completing section:
- All tasks above are
[x] - Update this subsection’s
statusin section frontmatter tocomplete - Run
/improve-toolingretrospectively on THIS subsection - Run
/sync-claudeto check if code changes affect CLAUDE.md or rules files
- All tasks above are
04.R Third Party Review Findings
- None.
04.N Completion Checklist
- All subsections (04.1-04.3) complete
- Dedup upgraded to (error_code, span)
- Child-span suppression implemented and tested
- Deterministic ordering verified with semantic pin test
-
timeout 150 ./test-all.shpasses - Debug AND release builds pass
-
/tpr-review— independent dual-source review clean -
/impl-hygiene-review— implementation hygiene clean -
/improve-tooling— section-close sweep -
/sync-claude— section-close doc sync