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