use crate::crate_prelude::*;
use crate::func_args::{FuncArg, FuncArgList};
use std::{collections::HashMap, sync::Arc};
#[derive(Debug)]
pub struct CallMapping<'a> {
pub span: Span,
pub args: Vec<CallArgMapping<'a>>,
error: bool,
}
impl<'a> CallMapping<'a> {
pub fn error(span: Span) -> Self {
Self {
span,
args: Default::default(),
error: true,
}
}
pub fn is_error(&self) -> bool {
self.error
}
}
#[derive(Debug, Clone, Copy)]
pub struct CallArgMapping<'a> {
pub span: Span,
pub decl: &'a FuncArg<'a>,
pub call: CallArgSource<'a>,
pub expr: &'a ast::Expr<'a>,
pub env: ParamEnv,
}
#[derive(Debug, Clone, Copy)]
pub enum CallArgSource<'a> {
Call(&'a ast::CallArg<'a>),
Default(&'a ast::Expr<'a>),
}
impl Eq for CallArgSource<'_> {}
impl PartialEq for CallArgSource<'_> {
fn eq(&self, other: &Self) -> bool {
match (*self, *other) {
(Self::Call(a), Self::Call(b)) => std::ptr::eq(a, b),
(Self::Default(a), Self::Default(b)) => std::ptr::eq(a, b),
_ => false,
}
}
}
impl std::hash::Hash for CallArgSource<'_> {
fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
match *self {
Self::Call(x) => std::ptr::hash(x, h),
Self::Default(x) => std::ptr::hash(x, h),
}
}
}
#[moore_derive::query]
pub(crate) fn call_mapping<'a>(
cx: &impl Context<'a>,
Ref(decl_args): Ref<'a, FuncArgList<'a>>,
Ref(call_args): Ref<'a, [ast::CallArg<'a>]>,
call_span: Span,
) -> Arc<CallMapping<'a>> {
debug!("Computing argument mapping");
if call_args.len() > decl_args.args.len() {
cx.emit(
DiagBuilder2::error(format!(
"argument mismatch: {} only has {} arguments, but {} provided",
decl_args.func,
decl_args.args.len(),
call_args.len(),
))
.span(call_span),
);
return Arc::new(CallMapping::error(call_span));
}
let decl_args_by_name: HashMap<_, _> = decl_args
.args
.iter()
.flat_map(|a| a.name.map(|n| (n.value, a)))
.collect();
trace!("Declaration args lookup: {:?}", decl_args_by_name);
let mut seen_named = false;
let mut failed = false;
let mut partial_mapping = HashMap::<Ref<FuncArg>, &ast::CallArg>::new();
for (decl_arg, call_arg) in decl_args.args.iter().zip(call_args.iter()) {
trace!("Matching up `{}`", call_arg.span().extract());
let matched_decl_arg = match call_arg.name {
Some(call_name) => {
seen_named = true;
if let Some(decl_arg) = decl_args_by_name.get(&call_name.value).copied() {
decl_arg
} else {
cx.emit(
DiagBuilder2::error(format!("unknown argument: `{}`", call_name))
.span(call_name.span)
.add_note(format!(
"Subroutine `{}` was declared here:",
decl_args.func.prototype.name
))
.span(decl_args.func.span()),
);
failed = true;
continue;
}
}
None if seen_named => {
cx.emit(
DiagBuilder2::error("positional argument after named")
.span(call_arg.span())
.add_note(
"IEEE 1800-2017 requires all positional arguments to appear before \
named arguments.",
),
);
failed = true;
continue;
}
None => decl_arg,
};
if let Some(previous) = partial_mapping.get(&Ref(matched_decl_arg)) {
cx.emit(
DiagBuilder2::error(format!(
"argument assigned multiple times: `{}`",
matched_decl_arg
.name
.map(|x| x.to_string())
.unwrap_or_else(|| "<unnamed>".to_string())
))
.span(call_arg.span())
.add_note("Previous assignment was here:")
.span(previous.span()),
);
continue;
} else {
partial_mapping.insert(Ref(matched_decl_arg), call_arg);
}
}
trace!("Mapping before assigning defaults: {:#?}", partial_mapping);
if failed {
return Arc::new(CallMapping::error(call_span));
}
let mut mapping = vec![];
for decl_arg in &decl_args.args {
let src = match partial_mapping.get(&Ref(decl_arg)) {
Some(call_arg) => match &call_arg.expr {
Some(expr) => Some((expr, call_arg)),
None => None,
},
None => None,
};
let span = match src {
Some((_, call_arg)) => call_arg.span(),
None => decl_arg.span,
};
let (expr, src) = match src {
Some((expr, call_arg)) => (expr, CallArgSource::Call(call_arg)),
None => match decl_arg.default {
Some(default) => (default, CallArgSource::Default(default)),
None => {
cx.emit(
DiagBuilder2::error(format!(
"argument without default: `{}` must be passed a value",
decl_arg
.name
.map(|x| x.to_string())
.unwrap_or_else(|| "<unnamed>".to_string())
))
.span(call_span)
.add_note("Argument was declared here:")
.span(decl_arg.span),
);
failed = true;
continue;
}
},
};
let arg = CallArgMapping {
span,
decl: decl_arg,
call: src,
expr,
env: cx.default_param_env(), };
mapping.push(arg);
}
Arc::new(CallMapping {
span: call_span,
args: mapping,
error: failed,
})
}
pub struct CallMappingVerbosityVisitor<'cx, C> {
cx: &'cx C,
}
impl<'cx, C> CallMappingVerbosityVisitor<'cx, C> {
pub fn new(cx: &'cx C) -> Self {
CallMappingVerbosityVisitor { cx }
}
}
impl<'a, 'cx, C> ast::Visitor<'a> for CallMappingVerbosityVisitor<'cx, C>
where
C: Context<'a>,
'a: 'cx,
{
fn pre_visit_expr(&mut self, node: &'a ast::Expr<'a>) -> bool {
let (target, args) = match self.cx.hir_of_expr(Ref(node)) {
Ok(hir::Expr {
kind: hir::ExprKind::FunctionCall(target, args),
..
}) => (target, args),
_ => return true,
};
let decl_args = self.cx.canonicalize_func_args(Ref(target));
let mapping = self.cx.call_mapping(Ref(decl_args), Ref(args), node.span());
if mapping.is_error() {
return true;
}
let mut d = DiagBuilder2::note("call argument mapping")
.span(node.span())
.add_note(format!(
"Call to subroutine `{}` has the following argument mapping:",
target.prototype.name
));
for m in &mapping.args {
let name = match m.decl.name {
Some(name) => format!(" {}", name),
None => format!(""),
};
let ty = self.cx.unpacked_type_from_ast(
Ref(m.decl.ty),
Ref(m.decl.unpacked_dims),
m.env,
None,
);
let value = format!(
"{} {}",
m.expr.span().extract(),
match m.call {
CallArgSource::Call(..) => "(call)",
CallArgSource::Default(..) => "(default)",
}
);
d = d.add_note(format!("{} {}{} = {}", m.decl.dir, ty, name, value));
}
self.cx.emit(d);
true
}
}