1use crate::ast::*;
3use crate::formatter::{Formatter, IntoFormatter};
4pub use annotate_snippets::Level;
5use itertools::Itertools;
6use macros::VariantIndexArity;
7use petgraph::algo::dijkstra::dijkstra;
8use petgraph::prelude::DiGraphMap;
9use std::cmp::{Ord, PartialOrd};
10use std::collections::{HashMap, HashSet};
11
12#[macro_export]
13macro_rules! register_error {
14 ($ctx:expr, crate($krate:expr), $span: expr, $($fmt:tt)*) => {{
15 let msg = format!($($fmt)*);
16 $ctx.span_err($krate, $span, &msg, $crate::errors::Level::WARNING)
17 }};
18 ($ctx:expr, $span: expr, $($fmt:tt)*) => {{
19 let msg = format!($($fmt)*);
20 $ctx.span_err($span, &msg, $crate::errors::Level::WARNING)
21 }};
22}
23pub use register_error;
24
25#[macro_export]
27macro_rules! raise_error {
28 ($($tokens:tt)*) => {{
29 return Err(register_error!($($tokens)*));
30 }};
31}
32pub use raise_error;
33
34#[macro_export]
36macro_rules! error_assert {
37 ($ctx:expr, $span: expr, $b: expr) => {
38 if !$b {
39 $crate::errors::raise_error!($ctx, $span, "assertion failure: {:?}", stringify!($b));
40 }
41 };
42 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
43 if !$b {
44 $crate::errors::raise_error!($ctx, $span, $($fmt)*);
45 }
46 };
47}
48pub use error_assert;
49
50#[macro_export]
52macro_rules! sanity_check {
53 ($ctx:expr, $span: expr, $b: expr) => {
54 if !$b {
55 $crate::errors::register_error!(
56 $ctx,
57 $span,
58 "assertion failure: {:?}",
59 stringify!($b)
60 );
61 }
62 };
63 ($ctx:expr, $span: expr, $b: expr, $($fmt:tt)*) => {
64 if !$b {
65 $crate::errors::register_error!($ctx, $span, $($fmt)*);
66 }
67 };
68}
69pub use sanity_check;
70
71#[derive(Debug)]
73pub struct Error {
74 pub span: Span,
75 pub msg: String,
76}
77
78impl Error {
79 pub(crate) fn render(&self, krate: &TranslatedCrate, level: Level) -> String {
80 use annotate_snippets::*;
81 let span = self.span.span;
82
83 let mut group = Group::new();
84 let origin;
85 if let Some(file) = krate.files.get(span.file_id) {
86 origin = format!("{}", file.name);
87 if let Some(source) = &file.contents {
88 let snippet = Snippet::source(source)
89 .origin(&origin)
90 .fold(true)
91 .annotation(AnnotationKind::Primary.span(span.to_byte_range(source)));
92 group = group.element(snippet);
93 } else {
94 let origin = Origin::new(&origin)
96 .line(span.beg.line)
97 .char_column(span.beg.col + 1)
98 .primary(true);
99 group = group.element(origin);
100 }
101 }
102 let message = level.header(&self.msg).group(group);
103
104 Renderer::styled().render(message).to_string()
105 }
106}
107
108pub fn display_unspanned_error(level: Level, msg: &str) {
110 use annotate_snippets::*;
111 let message = level.header(msg);
112 let message = Renderer::styled().render(message).to_string();
113 anstream::eprintln!("{message}\n");
114}
115
116#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
121pub struct DepSource {
122 pub src_id: AnyTransId,
123 pub span: Option<Span>,
126}
127
128#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
130enum DepNode {
131 External(AnyTransId),
132 Local(AnyTransId, Span),
134}
135
136struct DepGraph {
138 dgraph: DiGraphMap<DepNode, ()>,
139}
140
141impl DepGraph {
142 fn new() -> Self {
143 DepGraph {
144 dgraph: DiGraphMap::new(),
145 }
146 }
147
148 fn insert_node(&mut self, n: DepNode) {
149 if !self.dgraph.contains_node(n) {
151 self.dgraph.add_node(n);
152 }
153 }
154
155 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
156 self.insert_node(from);
157 self.insert_node(to);
158 if !self.dgraph.contains_edge(from, to) {
159 self.dgraph.add_edge(from, to, ());
160 }
161 }
162}
163
164impl std::fmt::Display for DepGraph {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
166 for (from, to, _) in self.dgraph.all_edges() {
167 writeln!(f, "{from:?} -> {to:?}")?
168 }
169 Ok(())
170 }
171}
172
173pub struct ErrorCtx {
175 pub continue_on_failure: bool,
177 pub error_on_warnings: bool,
179
180 pub external_decls_with_errors: HashSet<AnyTransId>,
182 external_dep_graph: DepGraph,
186 pub def_id: Option<AnyTransId>,
188 pub def_id_is_local: bool,
190 pub error_count: usize,
192}
193
194impl ErrorCtx {
195 pub fn new(continue_on_failure: bool, error_on_warnings: bool) -> Self {
196 Self {
197 continue_on_failure,
198 error_on_warnings,
199 external_decls_with_errors: HashSet::new(),
200 external_dep_graph: DepGraph::new(),
201 def_id: None,
202 def_id_is_local: false,
203 error_count: 0,
204 }
205 }
206
207 pub fn continue_on_failure(&self) -> bool {
208 self.continue_on_failure
209 }
210 pub fn has_errors(&self) -> bool {
211 self.error_count > 0
212 }
213
214 pub fn display_error(
216 &self,
217 krate: &TranslatedCrate,
218 span: Span,
219 level: Level,
220 msg: String,
221 ) -> Error {
222 let error = Error { span, msg };
223 anstream::eprintln!("{}\n", error.render(krate, level));
224 error
225 }
226
227 pub fn span_err(
229 &mut self,
230 krate: &TranslatedCrate,
231 span: Span,
232 msg: &str,
233 level: Level,
234 ) -> Error {
235 let level = if level == Level::WARNING && self.error_on_warnings {
236 Level::ERROR
237 } else {
238 level
239 };
240 let err = self.display_error(krate, span, level, msg.to_string());
241 self.error_count += 1;
242 if !self.def_id_is_local
245 && let Some(id) = self.def_id
246 && self.external_decls_with_errors.insert(id)
247 {
248 self.report_external_dep_error(krate, id);
249 }
250 if !self.continue_on_failure() {
251 panic!("{msg}");
252 }
253 err
254 }
255
256 pub fn register_dep_source(
258 &mut self,
259 src: &Option<DepSource>,
260 item_id: AnyTransId,
261 is_local: bool,
262 ) {
263 if let Some(src) = src
264 && src.src_id != item_id
265 && !is_local
266 {
267 let src_node = DepNode::External(item_id);
268 self.external_dep_graph.insert_node(src_node);
269
270 let tgt_node = match src.span {
271 Some(span) => DepNode::Local(src.src_id, span),
272 None => DepNode::External(src.src_id),
273 };
274 self.external_dep_graph.insert_edge(src_node, tgt_node)
275 }
276 }
277
278 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: AnyTransId) {
282 use annotate_snippets::*;
283
284 let graph = &self.external_dep_graph;
287 let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, |_| 1);
288 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
289
290 let by_file: HashMap<FileId, Vec<Span>> = reachable
292 .iter()
293 .filter_map(|(n, _)| match n {
294 DepNode::External(_) => None,
295 DepNode::Local(_, span) => Some(*span),
296 })
297 .into_group_map_by(|span| span.span.file_id);
298
299 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
302 .into_iter()
303 .filter_map(|(file_id, mut spans)| {
304 spans.sort(); let file = krate.files.get(file_id)?;
306 let source = file.contents.as_ref()?;
307 let file_name = file.name.to_string();
308 Some((file_id, file_name, source, spans))
309 })
310 .collect();
311 by_file.sort_by_key(|(file_id, ..)| *file_id);
313
314 let level = Level::NOTE;
315 let snippets = by_file.iter().map(|(_, origin, source, spans)| {
316 Snippet::source(source)
317 .origin(&origin)
318 .fold(true)
319 .annotations(
320 spans
321 .iter()
322 .map(|span| AnnotationKind::Context.span(span.span.to_byte_range(source))),
323 )
324 });
325
326 let msg = format!(
327 "the error occurred when translating `{}`, \
328 which is (transitively) used at the following location(s):",
329 krate.into_fmt().format_object(id)
330 );
331 let message = level.header(&msg).group(Group::new().elements(snippets));
332 let out = Renderer::styled().render(message).to_string();
333 anstream::eprintln!("{}", out);
334 }
335}