#![allow(dead_code)]
use swc_macros_common::prelude::*;
use syn::{parse::Parse, *};
struct MyField {
pub ident: Option<Ident>,
pub ty: Type,
pub lo: bool,
pub hi: bool,
}
struct InputFieldAttr {
kinds: Punctuated<Ident, Token![,]>,
}
impl Parse for InputFieldAttr {
fn parse(input: parse::ParseStream) -> Result<Self> {
let kinds = input.call(Punctuated::parse_terminated)?;
Ok(Self { kinds })
}
}
impl MyField {
fn from_field(f: &Field) -> Self {
let mut lo = false;
let mut hi = false;
for attr in &f.attrs {
if !is_attr_name(attr, "span") {
continue;
}
match &attr.meta {
Meta::Path(..) => {}
Meta::List(list) => {
let input = parse2::<InputFieldAttr>(list.tokens.clone())
.expect("failed to parse as `InputFieldAttr`");
for kind in input.kinds {
if kind == "lo" {
lo = true
} else if kind == "hi" {
hi = true
} else {
panic!("Unknown span attribute: {:?}", kind)
}
}
}
_ => panic!("Unknown span attribute"),
}
}
Self {
ident: f.ident.clone(),
ty: f.ty.clone(),
lo,
hi,
}
}
}
pub fn derive(input: DeriveInput) -> ItemImpl {
let arms = Binder::new_from(&input)
.variants()
.into_iter()
.map(|v| {
let (pat, bindings) = v.bind("_", Some(Token![ref](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: Default::default(),
comma: Some(Token ! [ , ](def_site())),
}
})
.collect();
let body = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: Default::default(),
brace_token: Default::default(),
expr: Box::new(parse_quote!(self)),
arms,
});
let ty = &input.ident;
let item: ItemImpl = parse_quote! {
#[automatically_derived]
impl swc_common::Spanned for #ty {
#[inline]
fn span(&self) -> swc_common::Span {
#body
}
}
};
item.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(parse_quote_spanned! (def_site() => {
swc_common::Spanned::span(#field)
}))
}
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().into_token_stream()
)
});
return simple_field(span_field);
}
let fields: Vec<_> = bindings
.iter()
.map(|b| (b, MyField::from_field(b.field())))
.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(parse_quote!(swc_common::Spanned::span(#lo_field)
.with_hi(swc_common::Spanned::span(#hi_field).hi())))
}
_ => 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;
}
match &attr.meta {
Meta::Path(..) => true,
Meta::List(t) => t.tokens.is_empty(),
_ => false,
}
})
}