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