use crate::ast::*;
use macros::VariantIndexArity;
use petgraph::prelude::DiGraphMap;
use std::cmp::{Ord, PartialOrd};
use std::collections::HashSet;
#[derive(Debug)]
pub struct Error {
pub span: Span,
pub msg: String,
}
#[macro_export]
macro_rules! register_error_or_panic {
($ctx:expr, $span: expr, $msg: expr) => {{
$ctx.span_err($span, &$msg);
if !$ctx.continue_on_failure() {
panic!("{}", $msg);
}
}};
($ctx:expr, $krate:expr, $span: expr, $msg: expr) => {{
$ctx.span_err($krate, $span, &$msg);
if !$ctx.continue_on_failure() {
panic!("{}", $msg);
}
}};
}
pub use register_error_or_panic;
#[macro_export]
macro_rules! error_or_panic {
($ctx:expr, $span:expr, $msg:expr) => {{
$crate::errors::register_error_or_panic!($ctx, $span, $msg);
let e = $crate::errors::Error {
span: $span,
msg: $msg.to_string(),
};
return Err(e);
}};
($ctx:expr, $krate:expr, $span:expr, $msg:expr) => {{
$crate::errors::register_error_or_panic!($ctx, $krate, $span, $msg);
let e = $crate::errors::Error {
span: $span,
msg: $msg.to_string(),
};
return Err(e);
}};
}
pub use error_or_panic;
#[macro_export]
macro_rules! error_assert {
($ctx:expr, $span: expr, $b: expr) => {
if !$b {
let msg = format!("assertion failure: {:?}", stringify!($b));
$crate::errors::error_or_panic!($ctx, $span, msg);
}
};
($ctx:expr, $span: expr, $b: expr, $msg: expr) => {
if !$b {
$crate::errors::error_or_panic!($ctx, $span, $msg);
}
};
}
pub use error_assert;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct DepSource {
pub src_id: AnyTransId,
pub span: Option<Span>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
enum DepNode {
External(AnyTransId),
Local(AnyTransId, Span),
}
struct DepGraph {
dgraph: DiGraphMap<DepNode, ()>,
}
impl DepGraph {
fn new() -> Self {
DepGraph {
dgraph: DiGraphMap::new(),
}
}
fn insert_node(&mut self, n: DepNode) {
if !self.dgraph.contains_node(n) {
self.dgraph.add_node(n);
}
}
fn insert_edge(&mut self, from: DepNode, to: DepNode) {
self.insert_node(from);
self.insert_node(to);
if !self.dgraph.contains_edge(from, to) {
self.dgraph.add_edge(from, to, ());
}
}
}
impl std::fmt::Display for DepGraph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
for (from, to, _) in self.dgraph.all_edges() {
writeln!(f, "{from:?} -> {to:?}")?
}
Ok(())
}
}
pub struct ErrorCtx<'ctx> {
pub continue_on_failure: bool,
pub error_on_warnings: bool,
#[cfg(feature = "rustc")]
pub dcx: rustc_errors::DiagCtxtHandle<'ctx>,
#[cfg(not(feature = "rustc"))]
pub dcx: &'ctx (),
pub external_decls_with_errors: HashSet<AnyTransId>,
pub ignored_failed_decls: HashSet<AnyTransId>,
external_dep_graph: DepGraph,
pub def_id: Option<AnyTransId>,
pub def_id_is_local: bool,
pub error_count: usize,
}
impl<'ctx> ErrorCtx<'ctx> {
pub fn new(
continue_on_failure: bool,
error_on_warnings: bool,
#[cfg(feature = "rustc")] dcx: rustc_errors::DiagCtxtHandle<'ctx>,
#[cfg(not(feature = "rustc"))] dcx: &'ctx (),
) -> Self {
Self {
continue_on_failure,
error_on_warnings,
dcx,
external_decls_with_errors: HashSet::new(),
ignored_failed_decls: HashSet::new(),
external_dep_graph: DepGraph::new(),
def_id: None,
def_id_is_local: false,
error_count: 0,
}
}
pub fn continue_on_failure(&self) -> bool {
self.continue_on_failure
}
pub(crate) fn has_errors(&self) -> bool {
self.error_count > 0
}
#[cfg(feature = "rustc")]
pub fn span_err_no_register(
&self,
_krate: &TranslatedCrate,
span: impl Into<rustc_error_messages::MultiSpan>,
msg: &str,
) {
let msg = msg.to_string();
if self.error_on_warnings {
self.dcx.span_err(span, msg);
} else {
self.dcx.span_warn(span, msg);
}
}
#[cfg(not(feature = "rustc"))]
pub(crate) fn span_err_no_register(&self, _krate: &TranslatedCrate, _span: Span, msg: &str) {
let msg = msg.to_string();
if self.error_on_warnings {
error!("{}", msg);
} else {
warn!("{}", msg);
}
}
pub fn span_err(&mut self, krate: &TranslatedCrate, span: Span, msg: &str) {
self.span_err_no_register(krate, span, msg);
self.error_count += 1;
#[cfg(feature = "rustc")]
if !self.def_id_is_local
&& let Some(id) = self.def_id
&& self.external_decls_with_errors.insert(id)
{
use crate::formatter::IntoFormatter;
self.report_external_dep_error(&krate.into_fmt(), id);
}
}
pub fn ignore_failed_decl(&mut self, id: AnyTransId) {
self.ignored_failed_decls.insert(id);
}
pub fn register_dep_source(
&mut self,
src: &Option<DepSource>,
item_id: AnyTransId,
is_local: bool,
) {
if let Some(src) = src
&& src.src_id != item_id
&& !is_local
{
let src_node = DepNode::External(item_id);
self.external_dep_graph.insert_node(src_node);
let tgt_node = match src.span {
Some(span) => DepNode::Local(src.src_id, span),
None => DepNode::External(src.src_id),
};
self.external_dep_graph.insert_edge(src_node, tgt_node)
}
}
#[cfg(feature = "rustc")]
pub fn report_external_dep_error(&self, f: &crate::formatter::FmtCtx<'_>, id: AnyTransId) {
use crate::formatter::Formatter;
use petgraph::algo::dijkstra::dijkstra;
use rustc_error_messages::MultiSpan;
let graph = &self.external_dep_graph;
let reachable = dijkstra(&graph.dgraph, DepNode::External(id), None, &mut |_| 1);
trace!("id: {:?}\nreachable:\n{:?}", id, reachable);
let reachable: Vec<rustc_span::Span> = reachable
.iter()
.filter_map(|(n, _)| match n {
DepNode::External(_) => None,
DepNode::Local(_, span) => Some(span.rust_span()),
})
.collect();
let spans = MultiSpan::from_spans(reachable);
let msg = format!(
"the error occurred when translating `{}`, \
which is (transitively) used at the following location(s):",
f.format_object(id)
);
self.dcx.span_note(spans, msg);
}
}