Bugfix - avoid panics when displaying error snippets. (#2674)

This commit is contained in:
Finn Bear 2023-09-12 03:34:17 -07:00 committed by GitHub
parent 5bfe598a96
commit 82e0d85da0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 17 deletions

View file

@ -137,32 +137,35 @@ impl<I: Clone> ParseError<I> {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Location { pub struct Location {
pub line: usize, pub line: usize,
/// In chars.
pub column: usize, pub column: usize,
} }
impl Location { impl Location {
/// Returns the location of a substring in the larger string. /// Returns the location of the start of substring in the larger input string.
pub fn of_in(substr: &str, s: &str) -> Self { ///
let offset = s /// Assumption: substr must be a subslice of input.
.len() pub fn of_in(substr: &str, input: &str) -> Self {
.checked_sub(substr.len()) // Bytes of input before substr.
let offset = (substr.as_ptr() as usize)
.checked_sub(input.as_ptr() as usize)
.expect("tried to find location of substring in unrelated string"); .expect("tried to find location of substring in unrelated string");
let lines = s.split('\n').enumerate(); // Bytes of input prior to line being iteratated.
let mut total = 0; let mut bytes_prior = 0;
for (idx, line) in lines { for (line_idx, line) in input.split('\n').enumerate() {
// +1 for the '\n' // +1 for the '\n'
let new_total = total + line.len() + 1; let bytes_so_far = bytes_prior + line.len() + 1;
if new_total > offset { if bytes_so_far > offset {
// found line. // found line.
let line_offset = offset - total; let line_offset = offset - bytes_prior;
let column = line[..line_offset].chars().count(); let column = line[..line_offset].chars().count();
// +1 because line and column are 1 index. // +1 because line and column are 1 index.
return Self { return Self {
line: idx + 1, line: line_idx + 1,
column: column + 1, column: column + 1,
}; };
} }
total = new_total; bytes_prior = bytes_so_far;
} }
unreachable!() unreachable!()
} }

View file

@ -40,7 +40,7 @@ pub struct Snippet {
truncation: Truncation, truncation: Truncation,
/// The location of the snippet in the orignal source code. /// The location of the snippet in the orignal source code.
location: Location, location: Location,
/// The offset into the snippet where the location is. /// The offset, in chars, into the snippet where the location is.
offset: usize, offset: usize,
/// A possible explanation for this snippet. /// A possible explanation for this snippet.
explain: Option<String>, explain: Option<String>,
@ -58,7 +58,7 @@ impl Snippet {
explain: Option<&'static str>, explain: Option<&'static str>,
) -> Self { ) -> Self {
let line = source.split('\n').nth(location.line - 1).unwrap(); let line = source.split('\n').nth(location.line - 1).unwrap();
let (line, truncation, offset) = Self::truncate_line(line, location.column); let (line, truncation, offset) = Self::truncate_line(line, location.column - 1);
Snippet { Snippet {
source: line.to_owned(), source: line.to_owned(),
@ -71,9 +71,9 @@ impl Snippet {
/// Trims whitespace of an line and additionally truncates a string if it is too long. /// Trims whitespace of an line and additionally truncates a string if it is too long.
fn truncate_line(mut line: &str, around_offset: usize) -> (&str, Truncation, usize) { fn truncate_line(mut line: &str, around_offset: usize) -> (&str, Truncation, usize) {
let full_line_length = line.len(); let full_line_length = line.chars().count();
line = line.trim_start(); line = line.trim_start();
let mut offset = around_offset - (full_line_length - line.len()); let mut offset = around_offset - (full_line_length - line.chars().count());
line = line.trim_end(); line = line.trim_end();
let mut truncation = Truncation::None; let mut truncation = Truncation::None;