Skip to content

Commit e9e295f

Browse files
committed
Deduplicate errors
1 parent 2490578 commit e9e295f

File tree

1 file changed

+68
-6
lines changed

1 file changed

+68
-6
lines changed

src/parsing/parser.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,20 @@ pub fn parse_with_recovery<'i>(
2020
input.parse_collecting_errors()
2121
}
2222

23-
#[derive(Debug, Clone, PartialEq, Eq)]
23+
// Most general errors first, most specific last (for deduplication, we prefer
24+
// being able to give a more specific error message to the user)
25+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
2426
pub enum ParsingError {
27+
// lowest priority
2528
IllegalParserState(usize),
2629
Unimplemented(usize),
2730
Unrecognized(usize), // improve this
31+
UnexpectedEndOfInput(usize),
2832
Expected(usize, &'static str),
2933
ExpectedMatchingChar(usize, &'static str, char, char),
30-
UnclosedInterpolation(usize),
31-
InvalidHeader(usize),
34+
// more specific errors
3235
InvalidCharacter(usize, char),
33-
UnexpectedEndOfInput(usize),
36+
InvalidHeader(usize),
3437
InvalidIdentifier(usize, String),
3538
InvalidForma(usize),
3639
InvalidGenus(usize),
@@ -40,17 +43,19 @@ pub enum ParsingError {
4043
InvalidInvocation(usize),
4144
InvalidFunction(usize),
4245
InvalidCodeBlock(usize),
43-
InvalidMultiline(usize),
4446
InvalidStep(usize),
4547
InvalidSubstep(usize),
46-
InvalidForeach(usize),
4748
InvalidResponse(usize),
49+
InvalidMultiline(usize),
50+
InvalidForeach(usize),
4851
InvalidIntegral(usize),
4952
InvalidQuantity(usize),
5053
InvalidQuantityDecimal(usize),
5154
InvalidQuantityUncertainty(usize),
5255
InvalidQuantityMagnitude(usize),
5356
InvalidQuantitySymbol(usize),
57+
// highest priority when deduplicating
58+
UnclosedInterpolation(usize),
5459
}
5560

5661
impl ParsingError {
@@ -89,6 +94,35 @@ impl ParsingError {
8994
}
9095
}
9196

97+
/// Deduplicate errors, preferring more specific errors over generic ones at the same offset.
98+
/// When multiple errors occur at the same offset, keep only the most specific one.
99+
/// Since ParsingError derives Ord with general errors first and specific errors last,
100+
/// we use > to prefer the higher Ord value (more specific) errors.
101+
fn deduplicate_errors(errors: Vec<ParsingError>) -> Vec<ParsingError> {
102+
let mut deduped = Vec::new();
103+
104+
for error in errors {
105+
let error_offset = error.offset();
106+
107+
// Check if we have an existing error at this offset
108+
if let Some(existing_idx) = deduped
109+
.iter()
110+
.position(|e: &ParsingError| e.offset() == error_offset)
111+
{
112+
// Keep the more specific error (higher Ord value, since specific errors come last)
113+
if error > deduped[existing_idx] {
114+
deduped[existing_idx] = error;
115+
}
116+
// Otherwise, keep the existing more specific error
117+
} else {
118+
// No error at this offset yet, add it
119+
deduped.push(error);
120+
}
121+
}
122+
123+
deduped
124+
}
125+
92126
#[derive(Debug)]
93127
pub struct Parser<'i> {
94128
filename: &'i Path,
@@ -284,6 +318,10 @@ impl<'i> Parser<'i> {
284318
let document = Document { header, body };
285319
let errors = std::mem::take(&mut self.problems);
286320

321+
// Deduplicate errors, preferring more specific errors over generic
322+
// ones at the same offset
323+
let errors = deduplicate_errors(errors);
324+
287325
if errors.is_empty() {
288326
Ok(document)
289327
} else {
@@ -4790,6 +4828,30 @@ broken_proc2 : -> B
47904828
};
47914829
}
47924830

4831+
#[test]
4832+
fn test_error_deduplication_unclosed_interpolation() {
4833+
let mut input = Parser::new();
4834+
4835+
// Test that UnclosedInterpolation error takes precedence over generic
4836+
// ExpectedMatchingChar
4837+
input.initialize(r#"{ "string with {unclosed interpolation" }"#);
4838+
let result = input.read_code_block();
4839+
4840+
// Should get the specific UnclosedInterpolation error, not a generic
4841+
// one
4842+
match result {
4843+
Err(ParsingError::UnclosedInterpolation(_)) => {
4844+
// Good - we got the specific error
4845+
}
4846+
Err(other) => {
4847+
panic!("Expected UnclosedInterpolation error, got: {:?}", other);
4848+
}
4849+
Ok(_) => {
4850+
panic!("Expected error for unclosed interpolation, but parsing succeeded");
4851+
}
4852+
}
4853+
}
4854+
47934855
#[test]
47944856
fn multiline_code_inline() {
47954857
let mut input = Parser::new();

0 commit comments

Comments
 (0)