use crate::syn_ext::*;
use proc_macro2::Span;
use syn::spanned::Spanned;
use syn::*;
mod rewrite_self {
use super::*;
use std::collections::HashSet;
#[derive(Clone, Debug)]
struct SpanWrapper(Span);
const _: () = {
impl Eq for SpanWrapper {}
impl PartialEq for SpanWrapper {
fn eq(&self, other: &Self) -> bool {
format!("{self:?}") == format!("{other:?}")
}
}
use std::hash::*;
impl Hash for SpanWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
format!("{self:?}").hash(state)
}
}
};
#[must_use]
pub struct RewriteSelf {
typ: Option<Type>,
ident: Ident,
self_spans: HashSet<SpanWrapper>,
}
impl RewriteSelf {
pub fn get_error(self) -> Option<proc_macro2::TokenStream> {
if self.typ.is_some() || self.self_spans.is_empty() {
return None;
}
let mut error = Error::new(Span::call_site(), "This macro doesn't work on trait or impl items: you need to add a `#[hax_lib::attributes]` on the enclosing impl block or trait.");
for SpanWrapper(span) in self.self_spans {
let use_site = Error::new(
span,
"Here, the function you are trying to annotate has a `Self`.",
);
error.combine(use_site);
}
Some(error.to_compile_error())
}
fn self_detected(&mut self, span: Span) {
self.self_spans.insert(SpanWrapper(span));
}
pub fn self_ident(&mut self, span: Span) -> &Ident {
self.self_detected(span);
&self.ident
}
pub fn self_ty(&mut self, span: Span) -> Type {
self.self_detected(span);
self.typ.clone().unwrap_or_else(|| {
parse_quote! {Self}
})
}
pub fn new(ident: Ident, typ: Option<Type>) -> Self {
Self {
typ,
ident,
self_spans: HashSet::new(),
}
}
}
}
pub use rewrite_self::*;
impl visit_mut::VisitMut for RewriteSelf {
fn visit_expr_mut(&mut self, e: &mut Expr) {
visit_mut::visit_expr_mut(self, e);
if e.is_ident("self") {
let into = self.self_ident(e.span()).clone();
*e = parse_quote! {#into}
}
}
fn visit_type_mut(&mut self, ty: &mut Type) {
visit_mut::visit_type_mut(self, ty);
if ty.is_ident("Self") {
*ty = self.self_ty(ty.span())
}
}
fn visit_fn_arg_mut(&mut self, arg: &mut FnArg) {
visit_mut::visit_fn_arg_mut(self, arg);
let arg_span = arg.span();
if let FnArg::Receiver(r) = arg {
let span = r.self_token.span();
*arg = FnArg::Typed(PatType {
attrs: r.attrs.clone(),
pat: Box::new(Pat::Ident(PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: self.self_ident(span).clone(),
subpat: None,
})),
colon_token: token::Colon(arg_span),
ty: Box::new({
let ty = self.self_ty(span);
let (reference, lt) = r
.reference
.clone()
.map(|(r, lt)| (Some(r), lt))
.unwrap_or((None, None));
let mutability = reference.and(r.mutability.clone());
parse_quote! {#reference #lt #mutability #ty}
}),
});
}
}
fn visit_item_impl_mut(&mut self, _i: &mut ItemImpl) {
}
}