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
108#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
113pub struct DepSource {
114 pub src_id: AnyTransId,
115 pub span: Option<Span>,
118}
119
120#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
122enum DepNode {
123 External(AnyTransId),
124 Local(AnyTransId, Span),
126}
127
128struct DepGraph {
130 dgraph: DiGraphMap<DepNode, ()>,
131}
132
133impl DepGraph {
134 fn new() -> Self {
135 DepGraph {
136 dgraph: DiGraphMap::new(),
137 }
138 }
139
140 fn insert_node(&mut self, n: DepNode) {
141 if !self.dgraph.contains_node(n) {
143 self.dgraph.add_node(n);
144 }
145 }
146
147 fn insert_edge(&mut self, from: DepNode, to: DepNode) {
148 self.insert_node(from);
149 self.insert_node(to);
150 if !self.dgraph.contains_edge(from, to) {
151 self.dgraph.add_edge(from, to, ());
152 }
153 }
154}
155
156impl std::fmt::Display for DepGraph {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
158 for (from, to, _) in self.dgraph.all_edges() {
159 writeln!(f, "{from:?} -> {to:?}")?
160 }
161 Ok(())
162 }
163}
164
165pub struct ErrorCtx {
167 pub continue_on_failure: bool,
169 pub error_on_warnings: bool,
171
172 pub external_decls_with_errors: HashSet<AnyTransId>,
174 external_dep_graph: DepGraph,
178 pub def_id: Option<AnyTransId>,
180 pub def_id_is_local: bool,
182 pub error_count: usize,
184}
185
186impl ErrorCtx {
187 pub fn new(continue_on_failure: bool, error_on_warnings: bool) -> Self {
188 Self {
189 continue_on_failure,
190 error_on_warnings,
191 external_decls_with_errors: HashSet::new(),
192 external_dep_graph: DepGraph::new(),
193 def_id: None,
194 def_id_is_local: false,
195 error_count: 0,
196 }
197 }
198
199 pub fn continue_on_failure(&self) -> bool {
200 self.continue_on_failure
201 }
202 pub fn has_errors(&self) -> bool {
203 self.error_count > 0
204 }
205
206 pub fn display_error(
208 &self,
209 krate: &TranslatedCrate,
210 span: Span,
211 level: Level,
212 msg: String,
213 ) -> Error {
214 let error = Error { span, msg };
215 anstream::eprintln!("{}\n", error.render(krate, level));
216 error
217 }
218
219 pub fn span_err(
221 &mut self,
222 krate: &TranslatedCrate,
223 span: Span,
224 msg: &str,
225 level: Level,
226 ) -> Error {
227 let level = if level == Level::WARNING && self.error_on_warnings {
228 Level::ERROR
229 } else {
230 level
231 };
232 let err = self.display_error(krate, span, level, msg.to_string());
233 self.error_count += 1;
234 if !self.def_id_is_local
237 && let Some(id) = self.def_id
238 && self.external_decls_with_errors.insert(id)
239 {
240 self.report_external_dep_error(krate, id);
241 }
242 if !self.continue_on_failure() {
243 panic!("{msg}");
244 }
245 err
246 }
247
248 pub fn register_dep_source(
250 &mut self,
251 src: &Option<DepSource>,
252 item_id: AnyTransId,
253 is_local: bool,
254 ) {
255 if let Some(src) = src
256 && src.src_id != item_id
257 && !is_local
258 {
259 let src_node = DepNode::External(item_id);
260 self.external_dep_graph.insert_node(src_node);
261
262 let tgt_node = match src.span {
263 Some(span) => DepNode::Local(src.src_id, span),
264 None => DepNode::External(src.src_id),
265 };
266 self.external_dep_graph.insert_edge(src_node, tgt_node)
267 }
268 }
269
270 pub fn report_external_dep_error(&self, krate: &TranslatedCrate, id: AnyTransId) {
274 use annotate_snippets::*;
275
276 let graph = &self.external_dep_graph;
279 let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, |_| 1);
280 trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
281
282 let by_file: HashMap<FileId, Vec<Span>> = reachable
284 .iter()
285 .filter_map(|(n, _)| match n {
286 DepNode::External(_) => None,
287 DepNode::Local(_, span) => Some(*span),
288 })
289 .into_group_map_by(|span| span.span.file_id);
290
291 let mut by_file: Vec<(FileId, _, _, Vec<Span>)> = by_file
294 .into_iter()
295 .filter_map(|(file_id, mut spans)| {
296 spans.sort(); let file = krate.files.get(file_id)?;
298 let source = file.contents.as_ref()?;
299 let file_name = file.name.to_string();
300 Some((file_id, file_name, source, spans))
301 })
302 .collect();
303 by_file.sort_by_key(|(file_id, ..)| *file_id);
305
306 let level = Level::NOTE;
307 let snippets = by_file.iter().map(|(_, origin, source, spans)| {
308 Snippet::source(source)
309 .origin(&origin)
310 .fold(true)
311 .annotations(
312 spans
313 .iter()
314 .map(|span| AnnotationKind::Context.span(span.span.to_byte_range(source))),
315 )
316 });
317
318 let msg = format!(
319 "the error occurred when translating `{}`, \
320 which is (transitively) used at the following location(s):",
321 krate.into_fmt().format_object(id)
322 );
323 let message = level.header(&msg).group(Group::new().elements(snippets));
324 let out = Renderer::styled().render(message).to_string();
325 anstream::eprintln!("{}", out);
326 }
327}