|
1 | 1 | use codespan_reporting::diagnostic::{Diagnostic, Label}; |
2 | | -use codespan_reporting::files::SimpleFile; |
| 2 | +use codespan_reporting::files; |
3 | 3 | use codespan_reporting::term; |
4 | 4 | use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; |
5 | 5 | use ndc_interpreter::InterpreterError; |
6 | | -use ndc_lexer::Span; |
| 6 | +use ndc_lexer::{SourceDb, SourceId, Span}; |
| 7 | +use std::ops::Range; |
7 | 8 |
|
8 | | -fn span_to_range(span: Span) -> std::ops::Range<usize> { |
| 9 | +fn span_to_range(span: Span) -> Range<usize> { |
9 | 10 | span.offset()..span.end() |
10 | 11 | } |
11 | 12 |
|
12 | | -fn into_diagnostic(err: InterpreterError) -> Diagnostic<()> { |
| 13 | +struct DiagnosticFiles<'a>(&'a SourceDb); |
| 14 | + |
| 15 | +impl<'a> files::Files<'a> for DiagnosticFiles<'a> { |
| 16 | + type FileId = SourceId; |
| 17 | + type Name = &'a str; |
| 18 | + type Source = &'a str; |
| 19 | + |
| 20 | + fn name(&'a self, id: SourceId) -> Result<&'a str, files::Error> { |
| 21 | + if id == SourceId::SYNTHETIC { |
| 22 | + return Ok("<synthetic>"); |
| 23 | + } |
| 24 | + Ok(self.0.name(id)) |
| 25 | + } |
| 26 | + |
| 27 | + fn source(&'a self, id: SourceId) -> Result<&'a str, files::Error> { |
| 28 | + if id == SourceId::SYNTHETIC { |
| 29 | + return Ok(""); |
| 30 | + } |
| 31 | + Ok(self.0.source(id)) |
| 32 | + } |
| 33 | + |
| 34 | + fn line_index(&'a self, id: SourceId, byte_index: usize) -> Result<usize, files::Error> { |
| 35 | + let source = self.source(id)?; |
| 36 | + Ok(files::line_starts(source) |
| 37 | + .take_while(|&start| start <= byte_index) |
| 38 | + .count() |
| 39 | + .saturating_sub(1)) |
| 40 | + } |
| 41 | + |
| 42 | + fn line_range(&'a self, id: SourceId, line_index: usize) -> Result<Range<usize>, files::Error> { |
| 43 | + let source = self.source(id)?; |
| 44 | + let line_starts: Vec<usize> = files::line_starts(source).collect(); |
| 45 | + let start = *line_starts |
| 46 | + .get(line_index) |
| 47 | + .ok_or(files::Error::LineTooLarge { |
| 48 | + given: line_index, |
| 49 | + max: line_starts.len().saturating_sub(1), |
| 50 | + })?; |
| 51 | + let end = line_starts |
| 52 | + .get(line_index + 1) |
| 53 | + .copied() |
| 54 | + .unwrap_or(source.len()); |
| 55 | + Ok(start..end) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +fn into_diagnostic(err: InterpreterError) -> Diagnostic<SourceId> { |
13 | 60 | match err { |
14 | 61 | InterpreterError::Lexer { cause } => { |
| 62 | + let span = cause.span(); |
15 | 63 | let mut d = Diagnostic::error() |
16 | 64 | .with_code("lexer") |
17 | 65 | .with_message(cause.to_string()) |
18 | 66 | .with_labels(vec![ |
19 | | - Label::primary((), span_to_range(cause.span())).with_message("here"), |
| 67 | + Label::primary(span.source_id(), span_to_range(span)).with_message("here"), |
20 | 68 | ]); |
21 | 69 | if let Some(help) = cause.help_text() { |
22 | 70 | d = d.with_notes(vec![help.to_owned()]); |
23 | 71 | } |
24 | 72 | d |
25 | 73 | } |
26 | 74 | InterpreterError::Parser { cause } => { |
| 75 | + let span = cause.span(); |
27 | 76 | let mut d = Diagnostic::error() |
28 | 77 | .with_code("parser") |
29 | 78 | .with_message(cause.to_string()) |
30 | 79 | .with_labels(vec![ |
31 | | - Label::primary((), span_to_range(cause.span())).with_message("here"), |
| 80 | + Label::primary(span.source_id(), span_to_range(span)).with_message("here"), |
32 | 81 | ]); |
33 | 82 | if let Some(help) = cause.help_text() { |
34 | 83 | d = d.with_notes(vec![help.to_owned()]); |
35 | 84 | } |
36 | 85 | d |
37 | 86 | } |
38 | | - InterpreterError::Resolver { cause } => Diagnostic::error() |
39 | | - .with_code("resolver") |
40 | | - .with_message(cause.to_string()) |
41 | | - .with_labels(vec![ |
42 | | - Label::primary((), span_to_range(cause.span())).with_message("related to this"), |
43 | | - ]), |
44 | | - InterpreterError::Compiler { cause } => Diagnostic::error() |
45 | | - .with_code("compiler") |
46 | | - .with_message(cause.to_string()) |
47 | | - .with_labels(vec![ |
48 | | - Label::primary((), span_to_range(cause.span())).with_message("related to this"), |
49 | | - ]), |
| 87 | + InterpreterError::Resolver { cause } => { |
| 88 | + let span = cause.span(); |
| 89 | + Diagnostic::error() |
| 90 | + .with_code("resolver") |
| 91 | + .with_message(cause.to_string()) |
| 92 | + .with_labels(vec![ |
| 93 | + Label::primary(span.source_id(), span_to_range(span)) |
| 94 | + .with_message("related to this"), |
| 95 | + ]) |
| 96 | + } |
| 97 | + InterpreterError::Compiler { cause } => { |
| 98 | + let span = cause.span(); |
| 99 | + Diagnostic::error() |
| 100 | + .with_code("compiler") |
| 101 | + .with_message(cause.to_string()) |
| 102 | + .with_labels(vec![ |
| 103 | + Label::primary(span.source_id(), span_to_range(span)) |
| 104 | + .with_message("related to this"), |
| 105 | + ]) |
| 106 | + } |
50 | 107 | InterpreterError::Vm(err) => { |
51 | 108 | let mut d = Diagnostic::error() |
52 | 109 | .with_code("vm") |
53 | 110 | .with_message(&err.message); |
54 | 111 | if let Some(span) = err.span { |
55 | 112 | d = d.with_labels(vec![ |
56 | | - Label::primary((), span_to_range(span)).with_message("related to this"), |
| 113 | + Label::primary(span.source_id(), span_to_range(span)) |
| 114 | + .with_message("related to this"), |
57 | 115 | ]); |
58 | 116 | } |
59 | 117 | d |
60 | 118 | } |
61 | 119 | } |
62 | 120 | } |
63 | 121 |
|
64 | | -pub fn emit_error(filename: &str, source: &str, err: InterpreterError) { |
| 122 | +pub fn emit_error(source_db: &SourceDb, err: InterpreterError) { |
65 | 123 | let diagnostic = into_diagnostic(err); |
66 | | - let file = SimpleFile::new(filename, source); |
| 124 | + let files = DiagnosticFiles(source_db); |
67 | 125 | let writer = StandardStream::stderr(ColorChoice::Auto); |
68 | 126 | let config = term::Config::default(); |
69 | | - let _ = term::emit(&mut writer.lock(), &config, &file, &diagnostic); |
| 127 | + let _ = term::emit(&mut writer.lock(), &config, &files, &diagnostic); |
70 | 128 | } |
0 commit comments