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, $span: expr, $($fmt:tt)*) => {{
23 let msg = format!($($fmt)*);
24 $ctx.span_err($span, &msg, $crate::errors::Level::WARNING)
25 }};
26}
27pub use register_error;
28
29#[macro_export]
31macro_rules! raise_error {
32 ($($tokens:tt)*) => {{
33 return Err(register_error!($($tokens)*));
34 }};
35}
36pub use raise_error;
37
38#[macro_export]
40macro_rules! error_assert {
41 ($ctx:expr, $span: expr, $b: expr) => {
42 if !$b {
43 $crate::errors::raise_error!($ctx, $span, "assertion failure: {:?}", stringify!($b));
44 }
45 };
46 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
47 if !$b {
48 $crate::errors::raise_error!($ctx, $span, $($fmt)*);
49 }
50 };
51}
52pub use error_assert;
53
54#[macro_export]
56macro_rules! sanity_check {
57 ($ctx:expr, $span: expr, $b: expr) => {
58 if !$b {
59 $crate::errors::register_error!(
60 $ctx,
61 $span,
62 "assertion failure: {:?}",
63 stringify!($b)
64 );
65 }
66 };
67 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
68 if !$b {
69 $crate::errors::register_error!($ctx, $span, $($fmt)*);
70 }
71 };
72}
73pub use sanity_check;
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct Error {
78 pub span: Span,
79 pub msg: String,
80}
81
82impl Error {
83 pub(crate) fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
84 use annotate_snippets::*;
85 let span = self.span.data;
86
87 let mut group = Group::with_title(level.title(&self.msg));
88 let origin;
89 if let Some(file) = krate.files.get(span.file_id) {
90 origin = format!("{}", file.name);
91 if let Some(source) = &file.contents {
92 let snippet = Snippet::source(source)
93 .path(&origin)
94 .fold(true)
95 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
96 group = group.element(snippet);
97 } else {
98 let origin = Origin::path(&origin)
100 .line(span.beg.line)
101 .char_column(span.beg.col + 1);
102 group = group.element(origin);
103 }
104 }
105
106 Renderer::styled().render(&[group]).to_string()
107 }
108}
109
110impl<T: ToString> From<T> for Error {
111 fn from(err: T) -> Self {
112 Self {
113 span: Span::dummy(),
114 msg: err.to_string(),
115 }
116 }
117}
118
119pub fn display_unspanned_error(level: Level, msg: &str) {
121 use annotate_snippets::*;
122 let title = level.title(msg);
123 let message = Renderer::styled()
124 .render(&[Group::with_title(title)])
125 .to_string();
126 anstream::eprintln!("{message}\n");
127}
128
129#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
134pub struct DepSource {
135 pub src_id: ItemId,
136 pub span: Option<Span>,
139}
140
141#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
143enum DepNode {
144 External(ItemId),
145 Local(ItemId, Span),
147}
148
149struct DepGraph {
151 dgraph: DiGraphMap<DepNode, ()>,
152}
153
154impl DepGraph {
155 fn new() -> Self {
156 DepGraph {
157 dgraph: DiGraphMap::new(),
158 }
159 }
160
161 fn insert_node(&mut self, n: DepNode) {
162 if !self.dgraph.contains_node(n) {
164 self.dgraph.add_node(n);
165 }
166 }
167
168 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
169 self.insert_node(from);
170 self.insert_node(to);
171 if !self.dgraph.contains_edge(from, to) {
172 self.dgraph.add_edge(from, to, ());
173 }
174 }
175}
176
177impl std::fmt::Display for DepGraph {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
179 for (from, to, _) in self.dgraph.all_edges() {
180 writeln!(f, "{from:?} -> {to:?}")?
181 }
182 Ok(())
183 }
184}
185
186pub struct ErrorCtx {
188 pub continue_on_failure: bool,
190 pub error_on_warnings: bool,
192
193 pub external_decls_with_errors: HashSet<ItemId>,
195 external_dep_graph: DepGraph,
199 pub def_id: Option<ItemId>,
201 pub def_id_is_local: bool,
203 pub error_count: usize,
205}
206
207impl ErrorCtx {
208 pub fn new(continue_on_failure: bool, error_on_warnings: bool) -> Self {
209 Self {
210 continue_on_failure,
211 error_on_warnings,
212 external_decls_with_errors: HashSet::new(),
213 external_dep_graph: DepGraph::new(),
214 def_id: None,
215 def_id_is_local: false,
216 error_count: 0,
217 }
218 }
219
220 pub fn continue_on_failure(&self) -> bool {
221 self.continue_on_failure
222 }
223 pub fn has_errors(&self) -> bool {
224 self.error_count > 0
225 }
226
227 pub fn display_error(
229 &self,
230 krate: &TranslatedCrate,
231 span: Span,
232 level: Level,
233 msg: String,
234 ) -> Error {
235 let error = Error { span, msg };
236 anstream::eprintln!("{}\n", error.render(krate, level));
237 if BACKTRACE_ON_ERR {
238 let backtrace = std::backtrace::Backtrace::force_capture();
239 eprintln!("{backtrace}\n");
240 }
241 error
242 }
243
244 pub fn span_err(
246 &mut self,
247 krate: &TranslatedCrate,
248 span: Span,
249 msg: &str,
250 level: Level,
251 ) -> Error {
252 let level = if level == Level::WARNING && self.error_on_warnings {
253 Level::ERROR
254 } else {
255 level
256 };
257 let err = self.display_error(krate, span, level, msg.to_string());
258 self.error_count += 1;
259 if !self.def_id_is_local
262 && let Some(id) = self.def_id
263 && self.external_decls_with_errors.insert(id)
264 {
265 self.report_external_dep_error(krate, id);
266 }
267 if !self.continue_on_failure() {
268 panic!("{msg}");
269 }
270 err
271 }
272
273 pub fn register_dep_source(
275 &mut self,
276 src: &Option<DepSource>,
277 item_id: ItemId,
278 is_local: bool,
279 ) {
280 if let Some(src) = src
281 && src.src_id != item_id
282 && !is_local
283 {
284 let src_node = DepNode::External(item_id);
285 self.external_dep_graph.insert_node(src_node);
286
287 let tgt_node = match src.span {
288 Some(span) => DepNode::Local(src.src_id, span),
289 None => DepNode::External(src.src_id),
290 };
291 self.external_dep_graph.insert_edge(src_node, tgt_node)
292 }
293 }
294
295 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: ItemId) {
299 use annotate_snippets::*;
300
301 let graph = &self.external_dep_graph;
304 let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, |_| 1);
305 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
306
307 let by_file: HashMap<FileId, Vec<Span>> = reachable
309 .iter()
310 .filter_map(|(n, _)| match n {
311 DepNode::External(_) => None,
312 DepNode::Local(_, span) => Some(*span),
313 })
314 .into_group_map_by(|span| span.data.file_id);
315
316 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
319 .into_iter()
320 .filter_map(|(file_id, mut spans)| {
321 spans.sort(); let file = krate.files.get(file_id)?;
323 let source = file.contents.as_ref()?;
324 let file_name = file.name.to_string();
325 Some((file_id, file_name, source, spans))
326 })
327 .collect();
328 by_file.sort_by_key(|(file_id, ..)| *file_id);
330
331 let level = Level::NOTE;
332 let snippets = by_file.iter().map(|(_, origin, source, spans)| {
333 Snippet::source(*source)
334 .path(origin)
335 .fold(true)
336 .annotations(
337 spans
338 .iter()
339 .map(|span| AnnotationKind::Context.span(span.data.to_byte_range(source))),
340 )
341 });
342
343 let msg = format!(
344 "the error occurred when translating `{}`, \
345 which is (transitively) used at the following location(s):",
346 id.with_ctx(&krate.into_fmt())
347 );
348 let message = Group::with_title(level.title(&msg)).elements(snippets);
349 let out = Renderer::styled().render(&[message]).to_string();
350 anstream::eprintln!("{}", out);
351 }
352}