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(crate) 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
104impl<T: ToString> From<T> for Error {
105 fn from(err: T) -> Self {
106 Self {
107 span: Span::dummy(),
108 msg: err.to_string(),
109 }
110 }
111}
112
113pub fn display_unspanned_error(level: Level, msg: &str) {
115 use annotate_snippets::*;
116 let title = level.primary_title(msg);
117 let message = Renderer::styled()
118 .render(&[Group::with_title(title)])
119 .to_string();
120 anstream::eprintln!("{message}\n");
121}
122
123pub fn display_spanned_error(krate: &TranslatedCrate, span: Span, title: &str, label: &str) {
125 use annotate_snippets::*;
126
127 let span = span.data();
128 let mut group = Group::with_title(Level::ERROR.primary_title(title));
129 let origin;
130 if let Some(file) = krate.files.get(span.file_id) {
131 origin = file.name.to_string();
132 if let Some(source) = &file.contents {
133 let snippet = Snippet::source(source).path(&origin).annotation(
134 AnnotationKind::Primary
135 .span(span.to_byte_range(source))
136 .label(label),
137 );
138 group = group.element(snippet);
139 } else {
140 let origin = Origin::path(origin)
141 .line(span.beg.line as usize)
142 .char_column(span.beg.col as usize + 1);
143 group = group.element(origin);
144 }
145 }
146
147 let diagnostic = [group];
148 anstream::eprintln!("{}", Renderer::styled().render(&diagnostic));
149}
150
151#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
156pub struct DepSource {
157 pub src_id: ItemId,
158 pub span: Option<Span>,
161}
162
163#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
165#[derive(VariantIndexArity)]
166enum DepNode {
167 External(ItemId),
168 Local(ItemId, Span),
170}
171
172struct DepGraph {
174 edges: HashSet<(DepNode, DepNode)>,
175}
176
177impl DepGraph {
178 fn new() -> Self {
179 DepGraph {
180 edges: Default::default(),
181 }
182 }
183
184 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
185 self.edges.insert((from, to));
186 }
187
188 fn graph(&self) -> DiGraphMap<DepNode, (), rustc_hash::FxBuildHasher> {
189 DiGraphMap::from_edges(self.edges.iter().copied())
190 }
191}
192
193pub struct ErrorCtx {
195 pub continue_on_failure: bool,
197 pub error_on_warnings: bool,
199
200 items_with_errors: HashSet<ItemId>,
202 external_dep_graph: DepGraph,
206 pub def_id: Option<ItemId>,
208 pub def_id_is_local: bool,
210 pub error_count: usize,
212}
213
214impl ErrorCtx {
215 pub fn new() -> Self {
216 Self {
217 continue_on_failure: true,
218 error_on_warnings: false,
219 items_with_errors: HashSet::default(),
220 external_dep_graph: DepGraph::new(),
221 def_id: None,
222 def_id_is_local: false,
223 error_count: 0,
224 }
225 }
226
227 pub fn continue_on_failure(&self) -> bool {
228 self.continue_on_failure
229 }
230 pub fn has_errors(&self) -> bool {
231 self.error_count > 0
232 }
233 pub fn item_has_errors(&self, id: ItemId) -> bool {
234 self.items_with_errors.contains(&id)
235 }
236
237 pub fn display_error(
239 &self,
240 krate: &TranslatedCrate,
241 span: Span,
242 level: Level,
243 msg: String,
244 ) -> Error {
245 let error = Error { span, msg };
246 anstream::eprintln!("{}\n", error.render(krate, level));
247 if BACKTRACE_ON_ERR {
248 let backtrace = std::backtrace::Backtrace::force_capture();
249 eprintln!("{backtrace}\n");
250 }
251 error
252 }
253
254 pub fn span_err(
256 &mut self,
257 krate: &TranslatedCrate,
258 span: Span,
259 msg: &str,
260 level: Level,
261 ) -> Error {
262 let level = if level == Level::WARNING && self.error_on_warnings {
263 Level::ERROR
264 } else {
265 level
266 };
267 let err = self.display_error(krate, span, level, msg.to_string());
268 self.error_count += 1;
269 if let Some(id) = self.def_id
270 && self.items_with_errors.insert(id)
271 && !self.def_id_is_local
272 {
273 self.report_external_dep_error(krate, id);
276 }
277 if !self.continue_on_failure() {
278 panic!("{msg}");
279 }
280 err
281 }
282
283 pub fn register_dep_source(
285 &mut self,
286 src: &Option<DepSource>,
287 item_id: ItemId,
288 is_local: bool,
289 ) {
290 if let Some(src) = src
291 && src.src_id != item_id
292 && !is_local
293 {
294 let src_node = DepNode::External(item_id);
295 let tgt_node = match src.span {
296 Some(span) => DepNode::Local(src.src_id, span),
297 None => DepNode::External(src.src_id),
298 };
299 self.external_dep_graph.insert_edge(src_node, tgt_node)
300 }
301 }
302
303 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: ItemId) {
307 use annotate_snippets::*;
308
309 let graph = self.external_dep_graph.graph();
312 let reachable = dijkstra(&graph, DepNode::External(id), None, |_| 1);
313 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
314
315 let by_file: std::collections::HashMap<FileId, Vec<Span>> = reachable
317 .iter()
318 .filter_map(|(n, _)| match n {
319 DepNode::External(_) => None,
320 DepNode::Local(_, span) => Some(*span),
321 })
322 .into_group_map_by(|span| span.data().file_id);
323
324 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
327 .into_iter()
328 .filter_map(|(file_id, mut spans)| {
329 spans.sort(); let file = krate.files.get(file_id)?;
331 let source = file.contents.as_ref()?;
332 let file_name = file.name.to_string();
333 Some((file_id, file_name, source, spans))
334 })
335 .collect();
336 by_file.sort_by_key(|(file_id, ..)| *file_id);
338
339 let level = Level::NOTE;
340 let snippets =
341 by_file.iter().map(|(_, origin, source, spans)| {
342 Snippet::source(*source)
343 .path(origin)
344 .fold(true)
345 .annotations(spans.iter().map(|span| {
346 AnnotationKind::Context.span(span.data().to_byte_range(source))
347 }))
348 });
349
350 let msg = format!(
351 "the error occurred when translating `{}`, \
352 which is (transitively) used at the following location(s):",
353 id.with_ctx(&krate.into_fmt())
354 );
355 let message = Group::with_title(level.primary_title(&msg)).elements(snippets);
356 let out = Renderer::styled().render(&[message]).to_string();
357 anstream::eprintln!("{}", out);
358 }
359}