Bugfix - avoid panics when displaying error snippets. (#2674)
This commit is contained in:
parent
5bfe598a96
commit
82e0d85da0
2 changed files with 20 additions and 17 deletions
|
@ -137,32 +137,35 @@ impl<I: Clone> ParseError<I> {
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Location {
|
||||
pub line: usize,
|
||||
/// In chars.
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
/// Returns the location of a substring in the larger string.
|
||||
pub fn of_in(substr: &str, s: &str) -> Self {
|
||||
let offset = s
|
||||
.len()
|
||||
.checked_sub(substr.len())
|
||||
/// Returns the location of the start of substring in the larger input string.
|
||||
///
|
||||
/// Assumption: substr must be a subslice of input.
|
||||
pub fn of_in(substr: &str, input: &str) -> Self {
|
||||
// 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");
|
||||
let lines = s.split('\n').enumerate();
|
||||
let mut total = 0;
|
||||
for (idx, line) in lines {
|
||||
// Bytes of input prior to line being iteratated.
|
||||
let mut bytes_prior = 0;
|
||||
for (line_idx, line) in input.split('\n').enumerate() {
|
||||
// +1 for the '\n'
|
||||
let new_total = total + line.len() + 1;
|
||||
if new_total > offset {
|
||||
let bytes_so_far = bytes_prior + line.len() + 1;
|
||||
if bytes_so_far > offset {
|
||||
// found line.
|
||||
let line_offset = offset - total;
|
||||
let line_offset = offset - bytes_prior;
|
||||
let column = line[..line_offset].chars().count();
|
||||
// +1 because line and column are 1 index.
|
||||
return Self {
|
||||
line: idx + 1,
|
||||
line: line_idx + 1,
|
||||
column: column + 1,
|
||||
};
|
||||
}
|
||||
total = new_total;
|
||||
bytes_prior = bytes_so_far;
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ pub struct Snippet {
|
|||
truncation: Truncation,
|
||||
/// The location of the snippet in the orignal source code.
|
||||
location: Location,
|
||||
/// The offset into the snippet where the location is.
|
||||
/// The offset, in chars, into the snippet where the location is.
|
||||
offset: usize,
|
||||
/// A possible explanation for this snippet.
|
||||
explain: Option<String>,
|
||||
|
@ -58,7 +58,7 @@ impl Snippet {
|
|||
explain: Option<&'static str>,
|
||||
) -> Self {
|
||||
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 {
|
||||
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.
|
||||
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();
|
||||
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();
|
||||
let mut truncation = Truncation::None;
|
||||
|
||||
|
|
Loading…
Reference in a new issue