use proc_macro2::{Ident, TokenStream, TokenTree};
use crate::ast::*;
use crate::codegen::impl_parts::ImplParts;
pub(crate) fn substitute_trait_generics(
parts: &mut ImplParts, trait_param_names: &[Ident],
) {
let Some(body) = parts.body.take() else {
return;
};
if trait_param_names.is_empty() || parts.trait_generic_names.is_empty() {
parts.body = Some(body);
return;
}
let map = trait_param_names
.iter()
.zip(parts.trait_generic_names.iter().filter(|ts| {
!matches!(
(*ts).clone().into_iter().next(),
Some(TokenTree::Punct(p)) if p.as_char() == '\''
)
}))
.map(|(name, arg)| (name.clone(), arg.clone()))
.collect::<Vec<_>>();
parts.body = Some(replace_idents(body, &map));
}
fn replace_idents(ts: TokenStream, map: &[(Ident, TokenStream)]) -> TokenStream {
ts.into_iter()
.flat_map(|tt| match &tt {
TokenTree::Ident(id) => map
.iter()
.find(|(name, _)| name == id)
.map(|(_, repl)| repl.clone())
.unwrap_or_else(|| TokenStream::from(tt.clone())),
TokenTree::Group(g) => {
let inner = replace_idents(g.stream(), map);
let mut ng = proc_macro2::Group::new(g.delimiter(), inner);
ng.set_span(g.span());
TokenStream::from(TokenTree::Group(ng))
}
other => TokenStream::from(other.clone()),
})
.collect()
}
pub(crate) fn expand_splat_elems(ty: Ty) -> Ty {
let Ty { span, kind } = ty;
match kind {
TyKind::Tuple(t) => {
let mut flat = vec![];
let mut decl = None;
for e in t.0 {
if matches!(e.kind, TyKind::Splat(_)) {
let (mut es, d) = splat_expand(e);
flat.append(&mut es);
decl = merge_decls(decl, d);
} else {
flat.push(expand_splat_elems(e));
}
}
let tuple = TyTuple(flat).to_ty().with_span(span);
match decl {
Some(d) => TyWithType(d, tuple.into()).to_ty().with_span(span),
None => tuple,
}
}
TyKind::Group(g) => {
TyGroup(Box::new(expand_splat_elems(*g.0))).to_ty().with_span(span)
}
TyKind::WithCode(wc) => {
let inner = wc.0.map(|e| Box::new(expand_splat_elems(*e)));
TyWithCode(inner, wc.1).to_ty().with_span(span)
}
TyKind::WithType(wt) => TyWithType(wt.0, Box::new(expand_splat_elems(*wt.1)))
.to_ty()
.with_span(span),
TyKind::WithTrait(wt) => {
let (tp, decl) = expand_tp(wt.0.1);
let trait_ty = TyTrait(wt.0.0, tp);
let inner = Box::new(expand_splat_elems(*wt.1));
match decl {
Some(d) => {
TyWithType(d, Box::new(TyWithTrait(trait_ty, inner).to_ty()))
.to_ty()
.with_span(span)
}
None => TyWithTrait(trait_ty, inner).to_ty().with_span(span),
}
}
TyKind::WithWhere(ww) => {
let inner = ww.0.map(|e| Box::new(expand_splat_elems(*e)));
TyWithWhere(inner, ww.1).to_ty().with_span(span)
}
TyKind::WithPrefix(wp) => {
let inner = wp.1.map(|e| Box::new(expand_splat_elems(*e)));
TyWithPrefix(wp.0, inner).to_ty().with_span(span)
}
TyKind::WithAttr(wa) => {
let inner = wa.1.map(|e| Box::new(expand_splat_elems(*e)));
TyWithAttr(wa.0, inner).to_ty().with_span(span)
}
TyKind::Generic(g) => {
let (tp, decl) = expand_tp(g.1);
let generic = TyGeneric(Box::new(expand_splat_elems(*g.0)), tp)
.to_ty()
.with_span(span);
match decl {
Some(d) => TyWithType(d, Box::new(generic)).to_ty().with_span(span),
None => generic,
}
}
TyKind::Trait(t) => {
let (tp, decl) = expand_tp(t.1);
let trait_ty = TyTrait(t.0, tp).to_ty().with_span(span);
match decl {
Some(d) => TyWithType(d, Box::new(trait_ty)).to_ty().with_span(span),
None => trait_ty,
}
}
other => Ty { span, kind: other },
}
}
fn expand_tp(tp: TyTypeParam) -> (TyTypeParam, Option<TyTypeParam>) {
let (flat, decl) = flat_splat_params(tp.params);
let params = flat
.into_iter()
.map(|(name, bound)| {
let name = expand_splat_elems(*name);
let bound = bound.map(expand_splat_elems);
(Box::new(name), bound)
})
.collect();
let bindings = tp
.bindings
.into_iter()
.map(|(n, v)| {
(
Box::new(expand_splat_elems(*n)),
Box::new(expand_splat_elems(*v)),
)
})
.collect();
(TyTypeParam { params, bindings }, decl)
}