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