ast_node/
spanned.rs

1#![allow(dead_code)]
2
3use swc_macros_common::prelude::*;
4use syn::{parse::Parse, spanned::Spanned, *};
5
6struct MyField {
7    /// Name of the field.
8    pub ident: Option<Ident>,
9    /// Type of the field.
10    pub ty: Type,
11
12    /// `#[span(lo)]`
13    pub lo: bool,
14    /// `#[span(hi)]`
15    pub hi: bool,
16}
17
18struct InputFieldAttr {
19    kinds: Punctuated<Ident, Token![,]>,
20}
21
22impl Parse for InputFieldAttr {
23    fn parse(input: parse::ParseStream) -> Result<Self> {
24        let kinds = input.call(Punctuated::parse_terminated)?;
25
26        Ok(Self { kinds })
27    }
28}
29
30fn is_unknown(attrs: &[syn::Attribute]) -> bool {
31    attrs
32        .iter()
33        .filter(|attr| attr.path().is_ident("span"))
34        .any(|attr| {
35            let mut is_unknown = false;
36            attr.parse_nested_meta(|meta| {
37                is_unknown |= meta.path.is_ident("unknown");
38                Ok(())
39            })
40            .unwrap();
41            is_unknown
42        })
43}
44
45impl MyField {
46    fn from_field(f: &Field) -> Self {
47        let mut lo = false;
48        let mut hi = false;
49
50        for attr in &f.attrs {
51            if !is_attr_name(attr, "span") {
52                continue;
53            }
54
55            match &attr.meta {
56                Meta::Path(..) => {}
57                Meta::List(list) => {
58                    let input = parse2::<InputFieldAttr>(list.tokens.clone())
59                        .expect("failed to parse as `InputFieldAttr`");
60
61                    for kind in input.kinds {
62                        if kind == "lo" {
63                            lo = true
64                        } else if kind == "hi" {
65                            hi = true
66                        } else {
67                            panic!("Unknown span attribute: {kind:?}")
68                        }
69                    }
70                }
71                _ => panic!("Unknown span attribute"),
72            }
73        }
74
75        Self {
76            ident: f.ident.clone(),
77            ty: f.ty.clone(),
78            lo,
79            hi,
80        }
81    }
82}
83
84pub fn derive(input: DeriveInput) -> ItemImpl {
85    let arms = Binder::new_from(&input)
86        .variants()
87        .into_iter()
88        .map(|v| {
89            let (pat, bindings) = v.bind("_", Some(Token![ref](def_site())), None);
90
91            let body = make_body_for_variant(&v, bindings);
92
93            Arm {
94                body,
95                attrs: v
96                    .attrs()
97                    .iter()
98                    .filter(|attr| is_attr_name(attr, "cfg"))
99                    .cloned()
100                    .collect(),
101                pat,
102                guard: None,
103                fat_arrow_token: Default::default(),
104                comma: Some(Token ! [ , ](def_site())),
105            }
106        })
107        .collect();
108
109    let body = Expr::Match(ExprMatch {
110        attrs: Default::default(),
111        match_token: Default::default(),
112        brace_token: Default::default(),
113        expr: Box::new(parse_quote!(self)),
114        arms,
115    });
116
117    let ty = &input.ident;
118
119    let item: ItemImpl = parse_quote! {
120        #[automatically_derived]
121        impl swc_common::Spanned for #ty {
122            #[inline]
123            fn span(&self) -> swc_common::Span {
124                #body
125            }
126        }
127    };
128    item.with_generics(input.generics)
129}
130
131fn make_body_for_variant(v: &VariantBinder<'_>, bindings: Vec<BindedField<'_>>) -> Box<Expr> {
132    /// `swc_common::Spanned::span(#field)`
133    fn simple_field(field: &dyn ToTokens) -> Box<Expr> {
134        Box::new(parse_quote_spanned! (def_site() => {
135            swc_common::Spanned::span(#field)
136        }))
137    }
138
139    if is_unknown(v.attrs()) {
140        return Box::new(parse_quote_spanned! { v.data().span() => {
141            swc_common::DUMMY_SP
142        }});
143    }
144
145    if bindings.is_empty() {
146        panic!("#[derive(Spanned)] requires a field to get span from")
147    }
148
149    if bindings.len() == 1 {
150        if let Fields::Unnamed(..) = *v.data() {
151            // Call self.0.span()
152            return simple_field(&bindings[0]);
153        }
154    }
155
156    //  Handle #[span] attribute.
157    if let Some(f) = bindings
158        .iter()
159        .find(|b| has_empty_span_attr(&b.field().attrs))
160    {
161        //TODO: Verify that there's no more #[span]
162        return simple_field(f);
163    }
164
165    // If all fields do not have `#[span(..)]`, check for field named `span`.
166    let has_any_span_attr = bindings.iter().any(|b| {
167        b.field()
168            .attrs
169            .iter()
170            .any(|attr| is_attr_name(attr, "span"))
171    });
172    if !has_any_span_attr {
173        let span_field = bindings
174            .iter()
175            .find(|b| {
176                b.field()
177                    .ident
178                    .as_ref()
179                    .map(|ident| ident == "span")
180                    .unwrap_or(false)
181            })
182            .unwrap_or_else(|| {
183                panic!(
184                    "#[derive(Spanned)]: cannot determine span field to use for {}",
185                    v.qual_path().into_token_stream()
186                )
187            });
188
189        return simple_field(span_field);
190    }
191
192    let fields: Vec<_> = bindings
193        .iter()
194        .map(|b| (b, MyField::from_field(b.field())))
195        .collect();
196
197    // TODO: Only one field should be `#[span(lo)]`.
198    let lo = fields.iter().find(|&(_, f)| f.lo);
199    let hi = fields.iter().find(|&(_, f)| f.hi);
200
201    match (lo, hi) {
202        (Some((lo_field, _)), Some((hi_field, _))) => {
203            // Create a new span from lo_field.lo(), hi_field.hi()
204            Box::new(parse_quote!(swc_common::Spanned::span(#lo_field)
205                .with_hi(swc_common::Spanned::span(#hi_field).hi())))
206        }
207        _ => panic!("#[derive(Spanned)]: #[span(lo)] and #[span(hi)] is required"),
208    }
209}
210
211/// Search for `#[span]`
212fn has_empty_span_attr(attrs: &[Attribute]) -> bool {
213    attrs.iter().any(|attr| {
214        if !is_attr_name(attr, "span") {
215            return false;
216        }
217
218        match &attr.meta {
219            Meta::Path(..) => true,
220            Meta::List(t) => t.tokens.is_empty(),
221            _ => false,
222        }
223    })
224}