Summary
When rustc emits multiple diagnostics for related errors (e.g., missing semicolon causing "unexpected token" on the next line), IDEs and developer tools cannot programmatically determine which diagnostic is the root cause and which are cascade effects. This information exists in rustc's internal diagnostic structure but is lost when converted to LSP format.
Problem Description
The Cascade Error Problem
Consider this Rust code with a missing semicolon:
fn main() {
let x = 10 // Missing semicolon
let y = x + 1; // Cascade error: "unexpected token"
}
rustc produces multiple diagnostics:
expected ';', found keyword 'let' (line 2) - ROOT CAUSE
unexpected token (line 3) - CASCADE EFFECT
If the developer fixes error #1, error #2 disappears automatically.
Current LSP Behavior
rust-analyzer correctly provides relatedInformation in diagnostics:
{
"message": "borrow of moved value: `x`",
"severity": 1,
"relatedInformation": [
{
"location": { "uri": "file:///...", "range": {...} },
"message": "value moved here"
},
{
"location": { "uri": "file:///...", "range": {...} },
"message": "move occurs because `x` has type `String`, which does not implement `Copy`"
}
]
}
The problem: There's no way to programmatically know which related information is:
- The cause of the error
- Additional context (e.g., "defined here")
- A suggestion for fixing
- A cascade effect that will disappear when the root is fixed
Current Workaround (Fragile)
Tools must infer relationship types by parsing message text:
fn infer_relationship(message: &str) -> RelationshipType {
let msg = message.to_lowercase();
if msg.contains("defined here") { RelationshipType::Context }
else if msg.contains("unexpected token") { RelationshipType::Effect }
else if msg.contains("consider") { RelationshipType::Suggestion }
else { RelationshipType::Unknown }
}
This breaks when rust-analyzer/rustc changes message wording.
Proposed Solution
Option 1: Add relationship field to relatedInformation (LSP Extension)
{
"relatedInformation": [
{
"location": {...},
"message": "value moved here",
"relationship": "cause"
},
{
"location": {...},
"message": "consider borrowing here: `&`",
"relationship": "suggestion"
}
]
}
Possible values:
"cause" - This location caused the diagnostic
"effect" - This is a cascade effect (will disappear when root is fixed)
"context" - Additional context (e.g., "defined here", "first borrow occurs here")
"suggestion" - A fix suggestion
Option 2: Preserve rustc's children structure
rustc's JSON output already has this information via the level field:
{
"children": [
{ "message": "...", "level": "note" }, // context
{ "message": "...", "level": "help" } // suggestion
]
}
This could be mapped to LSP more explicitly.
Use Cases
- Error Deduplication in IDEs - Show only root causes by default
- AI-Assisted Debugging - AI tools can focus on root causes rather than cascade noise
- Error Prioritization - Sort errors by "fixability"
- Learning Tools - Help beginners understand error relationships
- CI/CD Error Reporting - Reduce noise in build logs
Implementation Notes
Data Already Exists
rustc internally tracks:
is_primary on spans (primary vs secondary)
children array with level (note/help/error)
- Suggestion applicability
Backward Compatibility
- New field is optional
- Existing clients ignore unknown fields
- No breaking changes
Related Work
- Clang: Uses
-ferror-limit and internal error recovery
- TypeScript: Uses
ErrorType poisoning to prevent cascade diagnostics
- rustc JSON diagnostic format - Shows the rich structure available
Questions
- Is there appetite for LSP extensions, or should this be proposed to LSP spec?
- Would simply exposing rustc's
level field be sufficient?
- Are there concerns about increased diagnostic payload size?
This feature would significantly improve developer experience by helping tools distinguish between errors that need fixing and errors that will fix themselves.
Summary
When rustc emits multiple diagnostics for related errors (e.g., missing semicolon causing "unexpected token" on the next line), IDEs and developer tools cannot programmatically determine which diagnostic is the root cause and which are cascade effects. This information exists in rustc's internal diagnostic structure but is lost when converted to LSP format.
Problem Description
The Cascade Error Problem
Consider this Rust code with a missing semicolon:
rustc produces multiple diagnostics:
expected ';', found keyword 'let'(line 2) - ROOT CAUSEunexpected token(line 3) - CASCADE EFFECTIf the developer fixes error #1, error #2 disappears automatically.
Current LSP Behavior
rust-analyzer correctly provides
relatedInformationin diagnostics:{ "message": "borrow of moved value: `x`", "severity": 1, "relatedInformation": [ { "location": { "uri": "file:///...", "range": {...} }, "message": "value moved here" }, { "location": { "uri": "file:///...", "range": {...} }, "message": "move occurs because `x` has type `String`, which does not implement `Copy`" } ] }The problem: There's no way to programmatically know which related information is:
Current Workaround (Fragile)
Tools must infer relationship types by parsing message text:
This breaks when rust-analyzer/rustc changes message wording.
Proposed Solution
Option 1: Add
relationshipfield torelatedInformation(LSP Extension){ "relatedInformation": [ { "location": {...}, "message": "value moved here", "relationship": "cause" }, { "location": {...}, "message": "consider borrowing here: `&`", "relationship": "suggestion" } ] }Possible values:
"cause"- This location caused the diagnostic"effect"- This is a cascade effect (will disappear when root is fixed)"context"- Additional context (e.g., "defined here", "first borrow occurs here")"suggestion"- A fix suggestionOption 2: Preserve rustc's
childrenstructurerustc's JSON output already has this information via the
levelfield:{ "children": [ { "message": "...", "level": "note" }, // context { "message": "...", "level": "help" } // suggestion ] }This could be mapped to LSP more explicitly.
Use Cases
Implementation Notes
Data Already Exists
rustc internally tracks:
is_primaryon spans (primary vs secondary)childrenarray withlevel(note/help/error)Backward Compatibility
Related Work
-ferror-limitand internal error recoveryErrorTypepoisoning to prevent cascade diagnosticsQuestions
levelfield be sufficient?This feature would significantly improve developer experience by helping tools distinguish between errors that need fixing and errors that will fix themselves.