1use crate::ast::*;
3use crate::formatter::IntoFormatter;
4use crate::pretty::FmtWithCtx;
5pub use annotate_snippets::Level;
6use itertools::Itertools;
7use macros::VariantIndexArity;
8use petgraph::algo::dijkstra::dijkstra;
9use petgraph::prelude::DiGraphMap;
10use rustc_hash::FxHashSet as HashSet;
11use serde::{Deserialize, Serialize};
12use std::cmp::{Ord, PartialOrd};
13
14const BACKTRACE_ON_ERR: bool = false;
15
16#[macro_export]
17macro_rules! register_error {
18 ($ctx:expr, crate($krate:expr), $span: expr, $($fmt:tt)*) => {{
19 let msg = format!($($fmt)*);
20 $ctx.span_err($krate, $span, &msg, $crate::errors::Level::WARNING)
21 }};
22 ($ctx:expr, no_crate, $($fmt:tt)*) => {{
23 let msg = format!($($fmt)*);
24 $ctx.span_err(&Default::default(), Default::default(), &msg, $crate::errors::Level::WARNING)
25 }};
26 ($ctx:expr, $span: expr, $($fmt:tt)*) => {{
27 let msg = format!($($fmt)*);
28 $ctx.span_err($span, &msg, $crate::errors::Level::WARNING)
29 }};
30}
31pub use register_error;
32
33#[macro_export]
35macro_rules! raise_error {
36 ($($tokens:tt)*) => {{
37 return Err(register_error!($($tokens)*));
38 }};
39}
40pub use raise_error;
41
42#[macro_export]
44macro_rules! error_assert {
45 ($ctx:expr, $span: expr, $b: expr) => {
46 if !$b {
47 $crate::errors::raise_error!($ctx, $span, "assertion failure: {:?}", stringify!($b));
48 }
49 };
50 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
51 if !$b {
52 $crate::errors::raise_error!($ctx, $span, $($fmt)*);
53 }
54 };
55}
56pub use error_assert;
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60#[derive(Serialize, Deserialize)]
61pub struct Error {
62 pub span: Span,
63 pub msg: String,
64}
65
66impl Error {
67 pub fn new(span: Span, msg: String) -> Self {
68 Self { span, msg }
69 }
70 pub fn dummy() -> Self {
71 Self {
72 span: Span::dummy(),
73 msg: String::new(),
74 }
75 }
76
77 pub fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
78 use annotate_snippets::*;
79 let span = self.span.data();
80
81 let mut group = Group::with_title(level.primary_title(&self.msg));
82 let origin;
83 if let Some(file) = krate.files.get(span.file_id) {
84 origin = format!("{}", file.name);
85 if let Some(source) = &file.contents {
86 let snippet = Snippet::source(source)
87 .path(&origin)
88 .fold(true)
89 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
90 group = group.element(snippet);
91 } else {
92 let origin = Origin::path(&origin)
94 .line(span.beg.line as usize)
95 .char_column(span.beg.col as usize + 1);
96 group = group.element(origin);
97 }
98 }
99
100 Renderer::styled().render(&[group]).to_string()
101 }
102}
103
104pub trait ErrorContext<T> {
106 fn context(self, span: Span) -> Result<T, Error>;
107}
108
109impl<T, E: ToString> ErrorContext<T> for Result<T, E> {
110 fn context(self, span: Span) -> Result<T, Error> {
111 self.map_err(|error| Error::new(span, error.to_string()))
112 }
113}
114
115impl<T: ToString> From<T> for Error {
116 fn from(err: T) -> Self {
117 Self {
118 span: Span::dummy(),
119 msg: err.to_string(),
120 }
121 }
122}
123
124pub fn display_unspanned_error(level: Level, msg: &str) {
126 use annotate_snippets::*;
127 let title = level.primary_title(msg);
128 let message = Renderer::styled()
129 .render(&[Group::with_title(title)])
130 .to_string();
131 anstream::eprintln!("{message}\n");
132}
133
134pub fn display_spanned_error(krate: &TranslatedCrate, span: Span, title: &str, label: &str) {
136 use annotate_snippets::*;
137
138 let span = span.data();
139 let mut group = Group::with_title(Level::ERROR.primary_title(title));
140 let origin;
141 if let Some(file) = krate.files.get(span.file_id) {
142 origin = file.name.to_string();
143 if let Some(source) = &file.contents {
144 let snippet = Snippet::source(source).path(&origin).annotation(
145 AnnotationKind::Primary
146 .span(span.to_byte_range(source))
147 .label(label),
148 );
149 group = group.element(snippet);
150 } else {
151 let origin = Origin::path(origin)
152 .line(span.beg.line as usize)
153 .char_column(span.beg.col as usize + 1);
154 group = group.element(origin);
155 }
156 }
157
158 let diagnostic = [group];
159 anstream::eprintln!("{}", Renderer::styled().render(&diagnostic));
160}
161
162#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
167pub struct DepSource {
168 pub src_id: ItemId,
169 pub span: Option<Span>,
172}
173
174#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
176#[derive(VariantIndexArity)]
177enum DepNode {
178 External(ItemId),
179 Local(ItemId, Span),
181}
182
183struct DepGraph {
185 edges: HashSet<(DepNode, DepNode)>,
186}
187
188impl DepGraph {
189 fn new() -> Self {
190 DepGraph {
191 edges: Default::default(),
192 }
193 }
194
195 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
196 self.edges.insert((from, to));
197 }
198
199 fn graph(&self) -> DiGraphMap<DepNode, (), rustc_hash::FxBuildHasher> {
200 DiGraphMap::from_edges(self.edges.iter().copied())
201 }
202}
203
204pub struct ErrorCtx {
206 pub continue_on_failure: bool,
208 pub error_on_warnings: bool,
210
211 items_with_errors: HashSet<ItemId>,
213 external_dep_graph: DepGraph,
217 pub def_id: Option<ItemId>,
219 pub def_id_is_local: bool,
221 pub error_count: usize,
223}
224
225impl ErrorCtx {
226 pub fn new() -> Self {
227 Self {
228 continue_on_failure: true,
229 error_on_warnings: false,
230 items_with_errors: HashSet::default(),
231 external_dep_graph: DepGraph::new(),
232 def_id: None,
233 def_id_is_local: false,
234 error_count: 0,
235 }
236 }
237
238 pub fn continue_on_failure(&self) -> bool {
239 self.continue_on_failure
240 }
241 pub fn has_errors(&self) -> bool {
242 self.error_count > 0
243 }
244 pub fn item_has_errors(&self, id: ItemId) -> bool {
245 self.items_with_errors.contains(&id)
246 }
247
248 pub fn display_error(
250 &self,
251 krate: &TranslatedCrate,
252 span: Span,
253 level: Level,
254 msg: String,
255 ) -> Error {
256 let error = Error { span, msg };
257 anstream::eprintln!("{}\n", error.render(krate, level));
258 if BACKTRACE_ON_ERR {
259 let backtrace = std::backtrace::Backtrace::force_capture();
260 eprintln!("{backtrace}\n");
261 }
262 error
263 }
264
265 pub fn span_err(
267 &mut self,
268 krate: &TranslatedCrate,
269 span: Span,
270 msg: &str,
271 level: Level,
272 ) -> Error {
273 let level = if level == Level::WARNING && self.error_on_warnings {
274 Level::ERROR
275 } else {
276 level
277 };
278 let err = self.display_error(krate, span, level, msg.to_string());
279 self.error_count += 1;
280 if let Some(id) = self.def_id
281 && self.items_with_errors.insert(id)
282 && !self.def_id_is_local
283 {
284 self.report_external_dep_error(krate, id);
287 }
288 if !self.continue_on_failure() {
289 panic!("{msg}");
290 }
291 err
292 }
293
294 pub fn register_dep_source(
296 &mut self,
297 src: &Option<DepSource>,
298 item_id: ItemId,
299 is_local: bool,
300 ) {
301 if let Some(src) = src
302 && src.src_id != item_id
303 && !is_local
304 {
305 let src_node = DepNode::External(item_id);
306 let tgt_node = match src.span {
307 Some(span) => DepNode::Local(src.src_id, span),
308 None => DepNode::External(src.src_id),
309 };
310 self.external_dep_graph.insert_edge(src_node, tgt_node)
311 }
312 }
313
314 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: ItemId) {
318 use annotate_snippets::*;
319
320 let graph = self.external_dep_graph.graph();
323 let reachable = dijkstra(&graph, DepNode::External(id), None, |_| 1);
324 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
325
326 let by_file: std::collections::HashMap<FileId, Vec<Span>> = reachable
328 .iter()
329 .filter_map(|(n, _)| match n {
330 DepNode::External(_) => None,
331 DepNode::Local(_, span) => Some(*span),
332 })
333 .into_group_map_by(|span| span.data().file_id);
334
335 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
338 .into_iter()
339 .filter_map(|(file_id, mut spans)| {
340 spans.sort(); let file = krate.files.get(file_id)?;
342 let source = file.contents.as_ref()?;
343 let file_name = file.name.to_string();
344 Some((file_id, file_name, source, spans))
345 })
346 .collect();
347 by_file.sort_by_key(|(file_id, ..)| *file_id);
349
350 let level = Level::NOTE;
351 let snippets =
352 by_file.iter().map(|(_, origin, source, spans)| {
353 Snippet::source(*source)
354 .path(origin)
355 .fold(true)
356 .annotations(spans.iter().map(|span| {
357 AnnotationKind::Context.span(span.data().to_byte_range(source))
358 }))
359 });
360
361 let msg = format!(
362 "the error occurred when translating `{}`, \
363 which is (transitively) used at the following location(s):",
364 id.with_ctx(&krate.into_fmt())
365 );
366 let message = Group::with_title(level.primary_title(&msg)).elements(snippets);
367 let out = Renderer::styled().render(&[message]).to_string();
368 anstream::eprintln!("{}", out);
369 }
370}