diff --git a/crates/hcl-edit/src/parser/error.rs b/crates/hcl-edit/src/parser/error.rs index 82e1463c..d3a2bea8 100644 --- a/crates/hcl-edit/src/parser/error.rs +++ b/crates/hcl-edit/src/parser/error.rs @@ -115,21 +115,20 @@ impl Location { self.offset } } + fn locate_error<'a>(err: &'a ParseError, ContextError>) -> (&'a [u8], Location) { - let offset = err.offset(); let input = err.input().as_bytes(); - let remaining_input = &input[offset..]; - let consumed_input = &input[..offset]; + let offset = err.offset().min(input.len() - 1); + let column_offset = err.offset() - offset; - // Find the line that includes the subslice: - // Find the *last* newline before the remaining input starts - let line_begin = consumed_input + // Find the start of the line containing the error. + let line_begin = input[..offset] .iter() .rev() .position(|&b| b == b'\n') .map_or(0, |pos| offset - pos); - // Find the full line after that newline + // Use the full line containing the error as context for later printing. let line_context = input[line_begin..] .iter() .position(|&b| b == b'\n') @@ -137,11 +136,16 @@ fn locate_error<'a>(err: &'a ParseError, ContextError>) -> (&'a [u8], &input[line_begin..line_begin + pos] }); - // Count the number of newlines in the first `offset` bytes of input - let line = consumed_input.iter().filter(|&&b| b == b'\n').count() + 1; + // Count the number of newlines in the input before the line containing the error to calculate + // the line number. + let line = input[..line_begin].iter().filter(|&&b| b == b'\n').count() + 1; // The (1-indexed) column number is the offset of the remaining input into that line. - let column = remaining_input.offset_from(&line_context) + 1; + // This also takes multi-byte unicode characters into account. + let column = std::str::from_utf8(&input[line_begin..=offset]) + .map(|s| s.chars().count()) + .unwrap_or_else(|_| offset - line_begin + 1) + + column_offset; ( line_context, diff --git a/crates/hcl-edit/tests/parse.rs b/crates/hcl-edit/tests/parse.rs index fe4f3005..2064b692 100644 --- a/crates/hcl-edit/tests/parse.rs +++ b/crates/hcl-edit/tests/parse.rs @@ -150,4 +150,15 @@ fn invalid_exprs() { | = expected `)` or expression"#} ); + + assert_error!( + "unicodé_ident = '4", + indoc! {r#" + --> HCL parse error in line 1, column 17 + | + 1 | unicodé_ident = '4 + | ^--- + | + = invalid expression; expected `"`, `[`, `{`, `-`, `!`, `(`, `_`, `<`, letter or digit"#} + ); }