abi_stable_derive/sabi_trait/
replace_self_path.rsuse as_derive_utils::spanned_err;
use syn::visit_mut::VisitMut;
use syn::{Ident, TraitItemType, TypePath};
use std::mem;
use crate::utils::{LinearResult, SynResultExt};
#[derive(Debug, Clone)]
pub(crate) enum ReplaceWith {
Ident(Ident),
Remove,
Keep,
}
pub(crate) trait VisitMutWith {
fn visit_mut_with<F>(&mut self, other: &mut SelfReplacer<F>)
where
F: FnMut(&Ident) -> Option<ReplaceWith>;
}
macro_rules! impl_visit_mut_with {
(
$( ($self_:ty,$method:path) ),*
$(,)*
) => (
$(
impl VisitMutWith for $self_{
#[inline]
fn visit_mut_with<F>(&mut self,other:&mut SelfReplacer<F>)
where
F: FnMut(&Ident) -> Option<ReplaceWith>,
{
$method(other,self);
}
}
)*
)
}
impl_visit_mut_with! {
(syn::WherePredicate,VisitMut::visit_where_predicate_mut),
(TraitItemType,VisitMut::visit_trait_item_type_mut),
(syn::Type,VisitMut::visit_type_mut),
}
pub(crate) fn replace_self_path<V, F>(
value: &mut V,
replace_with: ReplaceWith,
is_assoc_type: F,
) -> Result<(), syn::Error>
where
V: VisitMutWith,
F: FnMut(&Ident) -> Option<ReplaceWith>,
{
let mut replacer = SelfReplacer {
is_assoc_type,
buffer: Vec::with_capacity(2),
replace_with,
errors: LinearResult::ok(()),
};
value.visit_mut_with(&mut replacer);
replacer.errors.into()
}
pub(crate) struct SelfReplacer<F> {
is_assoc_type: F,
buffer: Vec<ReplaceWith>,
replace_with: ReplaceWith,
errors: LinearResult<()>,
}
impl<F> VisitMut for SelfReplacer<F>
where
F: FnMut(&Ident) -> Option<ReplaceWith>,
{
fn visit_type_path_mut(&mut self, i: &mut TypePath) {
if let Some(qself) = i.qself.as_mut() {
self.visit_type_mut(&mut qself.ty);
}
let segments = &mut i.path.segments;
for segment in &mut *segments {
self.visit_path_arguments_mut(&mut segment.arguments);
}
let is_self = segments[0].ident == "Self";
match (segments.len(), is_self) {
(0, true) | (1, true) => {
self.errors.push_err(spanned_err!(
segments,
"Self can't be used in a parameter,return type,or associated type.",
));
return;
}
(2, true) => {}
(_, true) => {
self.errors.push_err(spanned_err!(
segments,
"Paths with 3 or more components are currently unsupported",
));
return;
}
(_, false) => return,
}
let is_replaced = (self.is_assoc_type)(&segments[1].ident);
if let Some(replace_assoc_with) = is_replaced {
let mut prev_segments = mem::take(segments).into_iter();
self.buffer.clear();
self.buffer.push(self.replace_with.clone());
self.buffer.push(replace_assoc_with);
for replace_with in self.buffer.drain(..) {
let prev_segment = prev_segments.next();
match replace_with {
ReplaceWith::Ident(ident) => {
segments.push(ident.into());
}
ReplaceWith::Remove => {}
ReplaceWith::Keep => {
segments.extend(prev_segment);
}
}
}
}
}
}