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 serde::{Deserialize, Serialize};
11use std::cmp::{Ord, PartialOrd};
12use std::collections::{HashMap, HashSet};
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#[macro_export]
60macro_rules! sanity_check {
61 ($ctx:expr, $span: expr, $b: expr) => {
62 if !$b {
63 $crate::errors::register_error!(
64 $ctx,
65 $span,
66 "assertion failure: {:?}",
67 stringify!($b)
68 );
69 }
70 };
71 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
72 if !$b {
73 $crate::errors::register_error!($ctx, $span, $($fmt)*);
74 }
75 };
76}
77pub use sanity_check;
78
79#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
81pub struct Error {
82 pub span: Span,
83 pub msg: String,
84}
85
86impl Error {
87 pub fn new(span: Span, msg: String) -> Self {
88 Self { span, msg }
89 }
90 pub fn dummy() -> Self {
91 Self {
92 span: Span::dummy(),
93 msg: String::new(),
94 }
95 }
96
97 pub(crate) fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
98 use annotate_snippets::*;
99 let span = self.span.data;
100
101 let mut group = Group::with_title(level.primary_title(&self.msg));
102 let origin;
103 if let Some(file) = krate.files.get(span.file_id) {
104 origin = format!("{}", file.name);
105 if let Some(source) = &file.contents {
106 let snippet = Snippet::source(source)
107 .path(&origin)
108 .fold(true)
109 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
110 group = group.element(snippet);
111 } else {
112 let origin = Origin::path(&origin)
114 .line(span.beg.line)
115 .char_column(span.beg.col + 1);
116 group = group.element(origin);
117 }
118 }
119
120 Renderer::styled().render(&[group]).to_string()
121 }
122}
123
124impl<T: ToString> From<T> for Error {
125 fn from(err: T) -> Self {
126 Self {
127 span: Span::dummy(),
128 msg: err.to_string(),
129 }
130 }
131}
132
133pub fn display_unspanned_error(level: Level, msg: &str) {
135 use annotate_snippets::*;
136 let title = level.primary_title(msg);
137 let message = Renderer::styled()
138 .render(&[Group::with_title(title)])
139 .to_string();
140 anstream::eprintln!("{message}\n");
141}
142
143#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
148pub struct DepSource {
149 pub src_id: ItemId,
150 pub span: Option<Span>,
153}
154
155#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
157enum DepNode {
158 External(ItemId),
159 Local(ItemId, Span),
161}
162
163struct DepGraph {
165 dgraph: DiGraphMap<DepNode, ()>,
166}
167
168impl DepGraph {
169 fn new() -> Self {
170 DepGraph {
171 dgraph: DiGraphMap::new(),
172 }
173 }
174
175 fn insert_node(&mut self, n: DepNode) {
176 if !self.dgraph.contains_node(n) {
178 self.dgraph.add_node(n);
179 }
180 }
181
182 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
183 self.insert_node(from);
184 self.insert_node(to);
185 if !self.dgraph.contains_edge(from, to) {
186 self.dgraph.add_edge(from, to, ());
187 }
188 }
189}
190
191impl std::fmt::Display for DepGraph {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
193 for (from, to, _) in self.dgraph.all_edges() {
194 writeln!(f, "{from:?} -> {to:?}")?
195 }
196 Ok(())
197 }
198}
199
200pub struct ErrorCtx {
202 pub continue_on_failure: bool,
204 pub error_on_warnings: bool,
206
207 pub external_decls_with_errors: HashSet<ItemId>,
209 external_dep_graph: DepGraph,
213 pub def_id: Option<ItemId>,
215 pub def_id_is_local: bool,
217 pub error_count: usize,
219}
220
221impl ErrorCtx {
222 pub fn new() -> Self {
223 Self {
224 continue_on_failure: true,
225 error_on_warnings: false,
226 external_decls_with_errors: HashSet::new(),
227 external_dep_graph: DepGraph::new(),
228 def_id: None,
229 def_id_is_local: false,
230 error_count: 0,
231 }
232 }
233
234 pub fn continue_on_failure(&self) -> bool {
235 self.continue_on_failure
236 }
237 pub fn has_errors(&self) -> bool {
238 self.error_count > 0
239 }
240
241 pub fn display_error(
243 &self,
244 krate: &TranslatedCrate,
245 span: Span,
246 level: Level,
247 msg: String,
248 ) -> Error {
249 let error = Error { span, msg };
250 anstream::eprintln!("{}\n", error.render(krate, level));
251 if BACKTRACE_ON_ERR {
252 let backtrace = std::backtrace::Backtrace::force_capture();
253 eprintln!("{backtrace}\n");
254 }
255 error
256 }
257
258 pub fn span_err(
260 &mut self,
261 krate: &TranslatedCrate,
262 span: Span,
263 msg: &str,
264 level: Level,
265 ) -> Error {
266 let level = if level == Level::WARNING && self.error_on_warnings {
267 Level::ERROR
268 } else {
269 level
270 };
271 let err = self.display_error(krate, span, level, msg.to_string());
272 self.error_count += 1;
273 if !self.def_id_is_local
276 && let Some(id) = self.def_id
277 && self.external_decls_with_errors.insert(id)
278 {
279 self.report_external_dep_error(krate, id);
280 }
281 if !self.continue_on_failure() {
282 panic!("{msg}");
283 }
284 err
285 }
286
287 pub fn register_dep_source(
289 &mut self,
290 src: &Option<DepSource>,
291 item_id: ItemId,
292 is_local: bool,
293 ) {
294 if let Some(src) = src
295 && src.src_id != item_id
296 && !is_local
297 {
298 let src_node = DepNode::External(item_id);
299 self.external_dep_graph.insert_node(src_node);
300
301 let tgt_node = match src.span {
302 Some(span) => DepNode::Local(src.src_id, span),
303 None => DepNode::External(src.src_id),
304 };
305 self.external_dep_graph.insert_edge(src_node, tgt_node)
306 }
307 }
308
309 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: ItemId) {
313 use annotate_snippets::*;
314
315 let graph = &self.external_dep_graph;
318 let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, |_| 1);
319 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
320
321 let by_file: HashMap<FileId, Vec<Span>> = reachable
323 .iter()
324 .filter_map(|(n, _)| match n {
325 DepNode::External(_) => None,
326 DepNode::Local(_, span) => Some(*span),
327 })
328 .into_group_map_by(|span| span.data.file_id);
329
330 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
333 .into_iter()
334 .filter_map(|(file_id, mut spans)| {
335 spans.sort(); let file = krate.files.get(file_id)?;
337 let source = file.contents.as_ref()?;
338 let file_name = file.name.to_string();
339 Some((file_id, file_name, source, spans))
340 })
341 .collect();
342 by_file.sort_by_key(|(file_id, ..)| *file_id);
344
345 let level = Level::NOTE;
346 let snippets = by_file.iter().map(|(_, origin, source, spans)| {
347 Snippet::source(*source)
348 .path(origin)
349 .fold(true)
350 .annotations(
351 spans
352 .iter()
353 .map(|span| AnnotationKind::Context.span(span.data.to_byte_range(source))),
354 )
355 });
356
357 let msg = format!(
358 "the error occurred when translating `{}`, \
359 which is (transitively) used at the following location(s):",
360 id.with_ctx(&krate.into_fmt())
361 );
362 let message = Group::with_title(level.primary_title(&msg)).elements(snippets);
363 let out = Renderer::styled().render(&[message]).to_string();
364 anstream::eprintln!("{}", out);
365 }
366}