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 fn new(span: Span, msg: String) -> Self {
84 Self { span, msg }
85 }
86
87 pub(crate) fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
88 use annotate_snippets::*;
89 let span = self.span.data;
90
91 let mut group = Group::with_title(level.title(&self.msg));
92 let origin;
93 if let Some(file) = krate.files.get(span.file_id) {
94 origin = format!("{}", file.name);
95 if let Some(source) = &file.contents {
96 let snippet = Snippet::source(source)
97 .path(&origin)
98 .fold(true)
99 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
100 group = group.element(snippet);
101 } else {
102 let origin = Origin::path(&origin)
104 .line(span.beg.line)
105 .char_column(span.beg.col + 1);
106 group = group.element(origin);
107 }
108 }
109
110 Renderer::styled().render(&[group]).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.title(msg);
127 let message = Renderer::styled()
128 .render(&[Group::with_title(title)])
129 .to_string();
130 anstream::eprintln!("{message}\n");
131}
132
133#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
138pub struct DepSource {
139 pub src_id: ItemId,
140 pub span: Option<Span>,
143}
144
145#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
147enum DepNode {
148 External(ItemId),
149 Local(ItemId, Span),
151}
152
153struct DepGraph {
155 dgraph: DiGraphMap<DepNode, ()>,
156}
157
158impl DepGraph {
159 fn new() -> Self {
160 DepGraph {
161 dgraph: DiGraphMap::new(),
162 }
163 }
164
165 fn insert_node(&mut self, n: DepNode) {
166 if !self.dgraph.contains_node(n) {
168 self.dgraph.add_node(n);
169 }
170 }
171
172 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
173 self.insert_node(from);
174 self.insert_node(to);
175 if !self.dgraph.contains_edge(from, to) {
176 self.dgraph.add_edge(from, to, ());
177 }
178 }
179}
180
181impl std::fmt::Display for DepGraph {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
183 for (from, to, _) in self.dgraph.all_edges() {
184 writeln!(f, "{from:?} -> {to:?}")?
185 }
186 Ok(())
187 }
188}
189
190pub struct ErrorCtx {
192 pub continue_on_failure: bool,
194 pub error_on_warnings: bool,
196
197 pub external_decls_with_errors: HashSet<ItemId>,
199 external_dep_graph: DepGraph,
203 pub def_id: Option<ItemId>,
205 pub def_id_is_local: bool,
207 pub error_count: usize,
209}
210
211impl ErrorCtx {
212 pub fn new(continue_on_failure: bool, error_on_warnings: bool) -> Self {
213 Self {
214 continue_on_failure,
215 error_on_warnings,
216 external_decls_with_errors: HashSet::new(),
217 external_dep_graph: DepGraph::new(),
218 def_id: None,
219 def_id_is_local: false,
220 error_count: 0,
221 }
222 }
223
224 pub fn continue_on_failure(&self) -> bool {
225 self.continue_on_failure
226 }
227 pub fn has_errors(&self) -> bool {
228 self.error_count > 0
229 }
230
231 pub fn display_error(
233 &self,
234 krate: &TranslatedCrate,
235 span: Span,
236 level: Level,
237 msg: String,
238 ) -> Error {
239 let error = Error { span, msg };
240 anstream::eprintln!("{}\n", error.render(krate, level));
241 if BACKTRACE_ON_ERR {
242 let backtrace = std::backtrace::Backtrace::force_capture();
243 eprintln!("{backtrace}\n");
244 }
245 error
246 }
247
248 pub fn span_err(
250 &mut self,
251 krate: &TranslatedCrate,
252 span: Span,
253 msg: &str,
254 level: Level,
255 ) -> Error {
256 let level = if level == Level::WARNING && self.error_on_warnings {
257 Level::ERROR
258 } else {
259 level
260 };
261 let err = self.display_error(krate, span, level, msg.to_string());
262 self.error_count += 1;
263 if !self.def_id_is_local
266 && let Some(id) = self.def_id
267 && self.external_decls_with_errors.insert(id)
268 {
269 self.report_external_dep_error(krate, id);
270 }
271 if !self.continue_on_failure() {
272 panic!("{msg}");
273 }
274 err
275 }
276
277 pub fn register_dep_source(
279 &mut self,
280 src: &Option<DepSource>,
281 item_id: ItemId,
282 is_local: bool,
283 ) {
284 if let Some(src) = src
285 && src.src_id != item_id
286 && !is_local
287 {
288 let src_node = DepNode::External(item_id);
289 self.external_dep_graph.insert_node(src_node);
290
291 let tgt_node = match src.span {
292 Some(span) => DepNode::Local(src.src_id, span),
293 None => DepNode::External(src.src_id),
294 };
295 self.external_dep_graph.insert_edge(src_node, tgt_node)
296 }
297 }
298
299 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: ItemId) {
303 use annotate_snippets::*;
304
305 let graph = &self.external_dep_graph;
308 let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, |_| 1);
309 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
310
311 let by_file: HashMap<FileId, Vec<Span>> = reachable
313 .iter()
314 .filter_map(|(n, _)| match n {
315 DepNode::External(_) => None,
316 DepNode::Local(_, span) => Some(*span),
317 })
318 .into_group_map_by(|span| span.data.file_id);
319
320 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
323 .into_iter()
324 .filter_map(|(file_id, mut spans)| {
325 spans.sort(); let file = krate.files.get(file_id)?;
327 let source = file.contents.as_ref()?;
328 let file_name = file.name.to_string();
329 Some((file_id, file_name, source, spans))
330 })
331 .collect();
332 by_file.sort_by_key(|(file_id, ..)| *file_id);
334
335 let level = Level::NOTE;
336 let snippets = by_file.iter().map(|(_, origin, source, spans)| {
337 Snippet::source(*source)
338 .path(origin)
339 .fold(true)
340 .annotations(
341 spans
342 .iter()
343 .map(|span| AnnotationKind::Context.span(span.data.to_byte_range(source))),
344 )
345 });
346
347 let msg = format!(
348 "the error occurred when translating `{}`, \
349 which is (transitively) used at the following location(s):",
350 id.with_ctx(&krate.into_fmt())
351 );
352 let message = Group::with_title(level.title(&msg)).elements(snippets);
353 let out = Renderer::styled().render(&[message]).to_string();
354 anstream::eprintln!("{}", out);
355 }
356}