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
13#[macro_export]
14macro_rules! register_error {
15 ($ctx:expr, crate($krate:expr), $span: expr, $($fmt:tt)*) => {{
16 let msg = format!($($fmt)*);
17 $ctx.span_err($krate, $span, &msg, $crate::errors::Level::WARNING)
18 }};
19 ($ctx:expr, $span: expr, $($fmt:tt)*) => {{
20 let msg = format!($($fmt)*);
21 $ctx.span_err($span, &msg, $crate::errors::Level::WARNING)
22 }};
23}
24pub use register_error;
25
26#[macro_export]
28macro_rules! raise_error {
29 ($($tokens:tt)*) => {{
30 return Err(register_error!($($tokens)*));
31 }};
32}
33pub use raise_error;
34
35#[macro_export]
37macro_rules! error_assert {
38 ($ctx:expr, $span: expr, $b: expr) => {
39 if !$b {
40 $crate::errors::raise_error!($ctx, $span, "assertion failure: {:?}", stringify!($b));
41 }
42 };
43 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
44 if !$b {
45 $crate::errors::raise_error!($ctx, $span, $($fmt)*);
46 }
47 };
48}
49pub use error_assert;
50
51#[macro_export]
53macro_rules! sanity_check {
54 ($ctx:expr, $span: expr, $b: expr) => {
55 if !$b {
56 $crate::errors::register_error!(
57 $ctx,
58 $span,
59 "assertion failure: {:?}",
60 stringify!($b)
61 );
62 }
63 };
64 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
65 if !$b {
66 $crate::errors::register_error!($ctx, $span, $($fmt)*);
67 }
68 };
69}
70pub use sanity_check;
71
72#[derive(Debug)]
74pub struct Error {
75 pub span: Span,
76 pub msg: String,
77}
78
79impl Error {
80 pub(crate) fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
81 use annotate_snippets::*;
82 let span = self.span.span;
83
84 let mut group = Group::with_title(level.title(&self.msg));
85 let origin;
86 if let Some(file) = krate.files.get(span.file_id) {
87 origin = format!("{}", file.name);
88 if let Some(source) = &file.contents {
89 let snippet = Snippet::source(source)
90 .path(&origin)
91 .fold(true)
92 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
93 group = group.element(snippet);
94 } else {
95 let origin = Origin::path(&origin)
97 .line(span.beg.line)
98 .char_column(span.beg.col + 1);
99 group = group.element(origin);
100 }
101 }
102
103 Renderer::styled().render(&[group]).to_string()
104 }
105}
106
107impl<T: ToString> From<T> for Error {
108 fn from(err: T) -> Self {
109 Self {
110 span: Span::dummy(),
111 msg: err.to_string(),
112 }
113 }
114}
115
116pub fn display_unspanned_error(level: Level, msg: &str) {
118 use annotate_snippets::*;
119 let title = level.title(msg);
120 let message = Renderer::styled()
121 .render(&[Group::with_title(title)])
122 .to_string();
123 anstream::eprintln!("{message}\n");
124}
125
126#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
131pub struct DepSource {
132 pub src_id: AnyTransId,
133 pub span: Option<Span>,
136}
137
138#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
140enum DepNode {
141 External(AnyTransId),
142 Local(AnyTransId, Span),
144}
145
146struct DepGraph {
148 dgraph: DiGraphMap<DepNode, ()>,
149}
150
151impl DepGraph {
152 fn new() -> Self {
153 DepGraph {
154 dgraph: DiGraphMap::new(),
155 }
156 }
157
158 fn insert_node(&mut self, n: DepNode) {
159 if !self.dgraph.contains_node(n) {
161 self.dgraph.add_node(n);
162 }
163 }
164
165 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
166 self.insert_node(from);
167 self.insert_node(to);
168 if !self.dgraph.contains_edge(from, to) {
169 self.dgraph.add_edge(from, to, ());
170 }
171 }
172}
173
174impl std::fmt::Display for DepGraph {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
176 for (from, to, _) in self.dgraph.all_edges() {
177 writeln!(f, "{from:?} -> {to:?}")?
178 }
179 Ok(())
180 }
181}
182
183pub struct ErrorCtx {
185 pub continue_on_failure: bool,
187 pub error_on_warnings: bool,
189
190 pub external_decls_with_errors: HashSet<AnyTransId>,
192 external_dep_graph: DepGraph,
196 pub def_id: Option<AnyTransId>,
198 pub def_id_is_local: bool,
200 pub error_count: usize,
202}
203
204impl ErrorCtx {
205 pub fn new(continue_on_failure: bool, error_on_warnings: bool) -> Self {
206 Self {
207 continue_on_failure,
208 error_on_warnings,
209 external_decls_with_errors: HashSet::new(),
210 external_dep_graph: DepGraph::new(),
211 def_id: None,
212 def_id_is_local: false,
213 error_count: 0,
214 }
215 }
216
217 pub fn continue_on_failure(&self) -> bool {
218 self.continue_on_failure
219 }
220 pub fn has_errors(&self) -> bool {
221 self.error_count > 0
222 }
223
224 pub fn display_error(
226 &self,
227 krate: &TranslatedCrate,
228 span: Span,
229 level: Level,
230 msg: String,
231 ) -> Error {
232 let error = Error { span, msg };
233 anstream::eprintln!("{}\n", error.render(krate, level));
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: AnyTransId,
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: AnyTransId) {
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.span.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.span.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.title(&msg)).elements(snippets);
342 let out = Renderer::styled().render(&[message]).to_string();
343 anstream::eprintln!("{}", out);
344 }
345}