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