Skip to content

Commit 361316d

Browse files
authored
Merge pull request #564 from ratmice/spanned_header_errors
Add line/column info to nimbleparses printing of `HeaderError`.
2 parents 483f3e0 + 6912702 commit 361316d

File tree

3 files changed

+84
-62
lines changed

3 files changed

+84
-62
lines changed

cfgrammar/src/lib/header.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
yacc::{
44
parser::SpansKind, YaccGrammarError, YaccGrammarErrorKind, YaccKind, YaccOriginalActionKind,
55
},
6-
Location, Span,
6+
Location, Span, Spanned,
77
};
88
use lazy_static::lazy_static;
99
use regex::{Regex, RegexBuilder};
@@ -38,6 +38,15 @@ impl From<HeaderError<Span>> for YaccGrammarError {
3838
}
3939
}
4040

41+
impl Spanned for HeaderError<Span> {
42+
fn spans(&self) -> &[Span] {
43+
self.locations.as_slice()
44+
}
45+
fn spanskind(&self) -> SpansKind {
46+
self.spanskind()
47+
}
48+
}
49+
4150
// This is essentially a tuple that needs a newtype so we can implement `From` for it.
4251
// Thus we aren't worried about it being `pub`.
4352
#[derive(Debug, PartialEq)]

nimbleparse/src/diagnostics.rs

+32-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{error::Error, fmt::Display, path::Path, str::FromStr};
1+
use std::{cell::OnceCell, error::Error, fmt::Display, path::Path};
22

33
use cfgrammar::{
44
newlinecache::NewlineCache,
@@ -16,7 +16,7 @@ use unicode_width::UnicodeWidthStr;
1616
pub struct SpannedDiagnosticFormatter<'a> {
1717
src: &'a str,
1818
path: &'a Path,
19-
nlc: NewlineCache,
19+
nlc: OnceCell<NewlineCache>,
2020
}
2121

2222
fn pidx_prods_data<StorageT>(ast: &GrammarAST, pidx: PIdx<StorageT>) -> (Vec<String>, Vec<Span>)
@@ -36,11 +36,19 @@ where
3636

3737
impl<'a> SpannedDiagnosticFormatter<'a> {
3838
#[allow(clippy::result_unit_err)]
39-
pub fn new(src: &'a str, path: &'a Path) -> Result<Self, ()> {
40-
Ok(Self {
39+
pub fn new(src: &'a str, path: &'a Path) -> Self {
40+
Self {
4141
src,
4242
path,
43-
nlc: NewlineCache::from_str(src)?,
43+
nlc: OnceCell::new(),
44+
}
45+
}
46+
47+
pub fn nlc(&self) -> &NewlineCache {
48+
self.nlc.get_or_init(|| {
49+
let mut nlc = NewlineCache::new();
50+
nlc.feed(self.src);
51+
nlc
4452
})
4553
}
4654

@@ -60,7 +68,7 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
6068
pub fn file_location_msg(&self, msg: &str, span: Option<Span>) -> String {
6169
if let Some(span) = span {
6270
let (line, col) = self
63-
.nlc
71+
.nlc()
6472
.byte_to_line_num_and_col_num(self.src, span.start())
6573
.unwrap_or((0, 0));
6674
format!("{} at {}:{line}:{col}", msg, self.path.display())
@@ -85,11 +93,11 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
8593
underline_c: char,
8694
) -> String {
8795
let mut out = String::new();
88-
let (start_byte, end_byte) = self.nlc.span_line_bytes(span);
96+
let (start_byte, end_byte) = self.nlc().span_line_bytes(span);
8997
// Produce an underline underneath a span which may cover multiple lines, and a message on the last line.
9098
let mut source_lines = self.src[start_byte..end_byte].lines().peekable();
9199
while let Some(source_line) = source_lines.next() {
92-
let (line_start_byte, _) = self.nlc.span_line_bytes(span);
100+
let (line_start_byte, _) = self.nlc().span_line_bytes(span);
93101
let span_offset_from_start = span.start() - line_start_byte;
94102

95103
// An underline bounded by the current line.
@@ -99,7 +107,7 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
99107
.min(span.start() + (source_line.len() - span_offset_from_start)),
100108
);
101109
let (line_num, _) = self
102-
.nlc
110+
.nlc()
103111
.byte_to_line_num_and_col_num(self.src, span.start())
104112
.expect("Span must correlate to a line in source");
105113
// Print the line_num/source text for the line.
@@ -137,13 +145,13 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
137145
let mut spans = e.spans().iter().enumerate().peekable();
138146
while let Some((span_num, span)) = spans.next() {
139147
let (line, _) = self
140-
.nlc
148+
.nlc()
141149
.byte_to_line_num_and_col_num(self.src, span.start())
142150
.unwrap_or((0, 0));
143151
let next_line = spans
144152
.peek()
145153
.map(|(_, span)| span)
146-
.map(|s| self.nlc.byte_to_line_num(s.start()).unwrap_or(line))
154+
.map(|s| self.nlc().byte_to_line_num(s.start()).unwrap_or(line))
147155
.unwrap_or(line);
148156
// Is this line contiguous with the next, if not prefix it with dots.
149157
let dots = if next_line - line > 1 { "..." } else { "" };
@@ -180,7 +188,7 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
180188
) -> String {
181189
let mut lines = spans
182190
.clone()
183-
.map(|span| self.nlc.span_line_bytes(span))
191+
.map(|span| self.nlc().span_line_bytes(span))
184192
.collect::<Vec<_>>();
185193
lines.dedup();
186194
assert!(lines.len() == 1);
@@ -192,10 +200,10 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
192200
// ____ ___
193201
// indent indent
194202
let (line_at_start, _) = self
195-
.nlc
203+
.nlc()
196204
.byte_to_line_num_and_col_num(self.src, first_span.start())
197205
.expect("Span should correlate to a line in source");
198-
let (line_start_byte, end_byte) = self.nlc.span_line_bytes(*first_span);
206+
let (line_start_byte, end_byte) = self.nlc().span_line_bytes(*first_span);
199207
// Print the line_num/source text for the line.
200208
out.push_str(&format!(
201209
"{}| {}\n",
@@ -304,15 +312,15 @@ impl<'a> SpannedDiagnosticFormatter<'a> {
304312

305313
let mut prod_lines = r_prod_spans
306314
.iter()
307-
.map(|span| self.nlc.span_line_bytes(*span))
315+
.map(|span| self.nlc().span_line_bytes(*span))
308316
.collect::<Vec<_>>();
309317
prod_lines.sort();
310318
prod_lines.dedup();
311319

312320
for lines in &prod_lines {
313321
let mut spans_on_line = Vec::new();
314322
for span in &r_prod_spans {
315-
if lines == &self.nlc.span_line_bytes(*span) {
323+
if lines == &self.nlc().span_line_bytes(*span) {
316324
spans_on_line.push(*span)
317325
}
318326
}
@@ -375,7 +383,7 @@ mod test {
375383
fn underline_multiline_span_test() {
376384
let s = "\naaaaaabbb\nbbb\nbbbb\n";
377385
let test_path = PathBuf::from("test");
378-
let formatter = SpannedDiagnosticFormatter::new(s, &test_path).unwrap();
386+
let formatter = SpannedDiagnosticFormatter::new(s, &test_path);
379387

380388
let span = Span::new(7, 7 + 12);
381389
let out = format!(
@@ -413,7 +421,7 @@ mod test {
413421
fn underline_single_line_span_test() {
414422
let s = "\naaaaaabbb bbb bbbb\n";
415423
let test_path = PathBuf::from("test");
416-
let formatter = SpannedDiagnosticFormatter::new(s, &test_path).unwrap();
424+
let formatter = SpannedDiagnosticFormatter::new(s, &test_path);
417425

418426
let span = Span::new(7, 7 + 12);
419427
let out = format!(
@@ -442,7 +450,7 @@ mod test {
442450
fn span_prefix() {
443451
let s = "\naaaaaabbb\nbbb\nbbbb\n";
444452
let test_path = PathBuf::from("test");
445-
let formatter = SpannedDiagnosticFormatter::new(s, &test_path).unwrap();
453+
let formatter = SpannedDiagnosticFormatter::new(s, &test_path);
446454
// For raw string alignment.
447455
let mut out = String::from("\n");
448456
// On occasion we want dots to signal that the lines are not contiguous.
@@ -473,7 +481,7 @@ mod test {
473481
fn span_prefix_2() {
474482
let s = "\n\n\n\n\n\n\n\n\n\n\naaaaaabbb\nbbb\nbbbb\n";
475483
let test_path = PathBuf::from("test");
476-
let formatter = SpannedDiagnosticFormatter::new(s, &test_path).unwrap();
484+
let formatter = SpannedDiagnosticFormatter::new(s, &test_path);
477485
let mut out = String::from("\n");
478486
// On occasion we want dots to signal that the lines are not contiguous.
479487
out.push_str(&formatter.prefixed_underline_span_with_text(
@@ -504,7 +512,7 @@ mod test {
504512
let crabs = " 🦀🦀🦀 ";
505513
let crustaceans = format!("\"{crabs}\n{crabs}\"");
506514
let test_path = PathBuf::from("test");
507-
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path).unwrap();
515+
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path);
508516
// For raw string alignment.
509517
let mut out = String::from("\n");
510518
out.push_str(&formatter.underline_span_with_text(
@@ -528,7 +536,7 @@ mod test {
528536
let lobster = "🦞";
529537
let crustaceans = format!("{crab}{lobster}{crab}{crab}{lobster}");
530538
let test_path = PathBuf::from("test");
531-
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path).unwrap();
539+
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path);
532540
// For raw string alignment.
533541
let mut out = String::from("\n");
534542
out.push_str(&formatter.prefixed_underline_span_with_text(
@@ -560,7 +568,7 @@ mod test {
560568
fn underline_single_line_spans_test() {
561569
let s = "\naaaaaabbb bbb bbbb\n";
562570
let test_path = PathBuf::from("test");
563-
let formatter = SpannedDiagnosticFormatter::new(s, &test_path).unwrap();
571+
let formatter = SpannedDiagnosticFormatter::new(s, &test_path);
564572
let spans = [(7, 10), (11, 14), (15, 19)]
565573
.iter()
566574
.map(|(i, j): &(usize, usize)| Span::new(*i, *j));
@@ -617,7 +625,7 @@ mod test {
617625
let lobster = "🦞";
618626
let crustaceans = format!("{crab}{lobster}{crab}{lobster}");
619627
let test_path = PathBuf::from("test");
620-
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path).unwrap();
628+
let formatter = SpannedDiagnosticFormatter::new(&crustaceans, &test_path);
621629
// For raw string alignment.
622630
let mut out = String::from("\n");
623631
let spans = [

0 commit comments

Comments
 (0)