#![allow(dead_code)]
use darling::FromField;
use pmutil::{smart_quote, Quote, ToTokensExt};
use swc_macros_common::prelude::*;
use syn::*;
#[derive(FromField)]
#[darling(attributes(span))]
struct MyField {
pub ident: Option<Ident>,
pub ty: Type,
#[darling(default)]
pub lo: bool,
#[darling(default)]
pub hi: bool,
}
pub fn derive(input: DeriveInput) -> ItemImpl {
let arms = Binder::new_from(&input)
.variants()
.into_iter()
.map(|v| {
let (pat, bindings) = v.bind("_", Some(def_site()), None);
let body = make_body_for_variant(&v, bindings);
Arm {
body,
attrs: v
.attrs()
.iter()
.filter(|attr| is_attr_name(attr, "cfg"))
.cloned()
.collect(),
pat,
guard: None,
fat_arrow_token: def_site(),
comma: Some(def_site()),
}
})
.collect();
let body = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: def_site(),
brace_token: def_site(),
expr: Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars {}, { self }))
.parse(),
),
arms,
});
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(
Vars {
Type: &input.ident,
body,
},
{
impl swc_common::Spanned for Type {
#[inline]
fn span(&self) -> swc_common::Span {
body
}
}
}
))
.parse::<ItemImpl>()
.with_generics(input.generics)
}
fn make_body_for_variant(v: &VariantBinder<'_>, bindings: Vec<BindedField<'_>>) -> Box<Expr> {
fn simple_field(field: &dyn ToTokens) -> Box<Expr> {
Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars { field }, {
swc_common::Spanned::span(field)
}))
.parse(),
)
}
if bindings.is_empty() {
panic!("#[derive(Spanned)] requires a field to get span from")
}
if bindings.len() == 1 {
if let Fields::Unnamed(..) = *v.data() {
return simple_field(&bindings[0]);
}
}
if let Some(f) = bindings
.iter()
.find(|b| has_empty_span_attr(&b.field().attrs))
{
return simple_field(f);
}
let has_any_span_attr = bindings
.iter()
.map(|b| {
b.field()
.attrs
.iter()
.any(|attr| is_attr_name(attr, "span"))
})
.any(|b| b);
if !has_any_span_attr {
let span_field = bindings
.iter()
.find(|b| {
b.field()
.ident
.as_ref()
.map(|ident| ident == "span")
.unwrap_or(false)
})
.unwrap_or_else(|| {
panic!(
"#[derive(Spanned)]: cannot determine span field to use for {}",
v.qual_path().dump()
)
});
return simple_field(span_field);
}
let fields: Vec<_> = bindings
.iter()
.map(|b| (b, MyField::from_field(b.field()).unwrap()))
.collect();
let lo = fields.iter().find(|&(_, f)| f.lo);
let hi = fields.iter().find(|&(_, f)| f.hi);
match (lo, hi) {
(Some((lo_field, _)), Some((hi_field, _))) => {
Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars { lo_field, hi_field }, {
swc_common::Spanned::span(lo_field)
.with_hi(swc_common::Spanned::span(hi_field).hi())
}))
.parse(),
)
}
_ => panic!("#[derive(Spanned)]: #[span(lo)] and #[span(hi)] is required"),
}
}
fn has_empty_span_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
if !is_attr_name(attr, "span") {
return false;
}
attr.tokens.is_empty()
})
}