1#![allow(dead_code)]
2
3use swc_macros_common::prelude::*;
4use syn::{parse::Parse, spanned::Spanned, *};
5
6struct MyField {
7 pub ident: Option<Ident>,
9 pub ty: Type,
11
12 pub lo: bool,
14 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)), 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 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 return simple_field(&bindings[0]);
153 }
154 }
155
156 if let Some(f) = bindings
158 .iter()
159 .find(|b| has_empty_span_attr(&b.field().attrs))
160 {
161 return simple_field(f);
163 }
164
165 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 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 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
211fn 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}