syn_ext/
item.rs

1use crate::ident::GetIdent;
2use syn::{
3    spanned::Spanned, Attribute, Ident, ImplItem, ImplItemFn, Item, ItemFn, ItemMod, Result,
4    TraitItem, TraitItemFn,
5};
6
7/// Extension for [syn::Item]
8pub trait ItemLike: Spanned {
9    /// Returns reference of inner attrs if not verbatim; otherwise `Err`
10    fn attrs(&self) -> Result<&[Attribute]>;
11    /// Returns mutable reference of inner attrs if not verbatim; otherwise `Err`
12    fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>>;
13    /// Returns function-like trait object of Item::Fn, ImplItem::Method, or TraitItem::Method
14    fn function_or_method(&self) -> Result<&dyn FunctionLike>;
15    /// Returns const-like trait object of Item::Const, ImplItem::Const, or TraitItem::Const
16    fn constant(&self) -> Result<&dyn ConstLike>;
17
18    /// Returns `true` if self matches `*ItemType`
19    fn is_type(&self) -> bool;
20    /// Returns `true` if self matches `*ItemMacro`
21    fn is_macro(&self) -> bool;
22}
23
24impl ItemLike for Item {
25    fn attrs(&self) -> Result<&[Attribute]> {
26        use syn::Item::*;
27        use syn::*;
28        let attrs = match self {
29            Const(ItemConst { ref attrs, .. }) => attrs,
30            Enum(ItemEnum { ref attrs, .. }) => attrs,
31            ExternCrate(ItemExternCrate { ref attrs, .. }) => attrs,
32            Fn(ItemFn { ref attrs, .. }) => attrs,
33            ForeignMod(ItemForeignMod { ref attrs, .. }) => attrs,
34            Impl(ItemImpl { ref attrs, .. }) => attrs,
35            Macro(ItemMacro { ref attrs, .. }) => attrs,
36            Mod(ItemMod { ref attrs, .. }) => attrs,
37            Static(ItemStatic { ref attrs, .. }) => attrs,
38            Struct(ItemStruct { ref attrs, .. }) => attrs,
39            Trait(ItemTrait { ref attrs, .. }) => attrs,
40            TraitAlias(ItemTraitAlias { ref attrs, .. }) => attrs,
41            Type(ItemType { ref attrs, .. }) => attrs,
42            Union(ItemUnion { ref attrs, .. }) => attrs,
43            Use(ItemUse { ref attrs, .. }) => attrs,
44            other => {
45                return Err(Error::new_spanned(
46                    other,
47                    "this kind of item doesn't have attrs",
48                ))
49            }
50        };
51        Ok(attrs)
52    }
53
54    fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
55        use syn::Item::*;
56        use syn::*;
57        let attrs = match self {
58            Const(ItemConst { ref mut attrs, .. }) => attrs,
59            Enum(ItemEnum { ref mut attrs, .. }) => attrs,
60            ExternCrate(ItemExternCrate { ref mut attrs, .. }) => attrs,
61            Fn(ItemFn { ref mut attrs, .. }) => attrs,
62            ForeignMod(ItemForeignMod { ref mut attrs, .. }) => attrs,
63            Impl(ItemImpl { ref mut attrs, .. }) => attrs,
64            Macro(ItemMacro { ref mut attrs, .. }) => attrs,
65            Mod(ItemMod { ref mut attrs, .. }) => attrs,
66            Static(ItemStatic { ref mut attrs, .. }) => attrs,
67            Struct(ItemStruct { ref mut attrs, .. }) => attrs,
68            Trait(ItemTrait { ref mut attrs, .. }) => attrs,
69            TraitAlias(ItemTraitAlias { ref mut attrs, .. }) => attrs,
70            Type(ItemType { ref mut attrs, .. }) => attrs,
71            Union(ItemUnion { ref mut attrs, .. }) => attrs,
72            Use(ItemUse { ref mut attrs, .. }) => attrs,
73            other => {
74                return Err(Error::new_spanned(
75                    other,
76                    "this kind of item doesn't have attrs",
77                ))
78            }
79        };
80        Ok(attrs)
81    }
82
83    fn function_or_method(&self) -> Result<&dyn FunctionLike> {
84        match self {
85            Item::Fn(f @ syn::ItemFn { .. }) => Ok(f),
86            other => Err(syn::Error::new_spanned(
87                other,
88                "this item is not a function or method",
89            )),
90        }
91    }
92
93    fn constant(&self) -> Result<&dyn ConstLike> {
94        match self {
95            Item::Const(c @ syn::ItemConst { .. }) => Ok(c),
96            other => Err(syn::Error::new_spanned(other, "this item is not a const")),
97        }
98    }
99
100    fn is_type(&self) -> bool {
101        matches!(self, Item::Type(_))
102    }
103    fn is_macro(&self) -> bool {
104        matches!(self, Item::Macro(_))
105    }
106}
107
108impl ItemLike for ImplItem {
109    fn attrs(&self) -> Result<&[Attribute]> {
110        use syn::ImplItem::*;
111        use syn::*;
112        let attrs = match self {
113            Const(ImplItemConst { ref attrs, .. }) => attrs,
114            Fn(ImplItemFn { ref attrs, .. }) => attrs,
115            Type(ImplItemType { ref attrs, .. }) => attrs,
116            Macro(ImplItemMacro { ref attrs, .. }) => attrs,
117            other => {
118                return Err(Error::new_spanned(
119                    other,
120                    "this kind of item doesn't have attrs",
121                ))
122            }
123        };
124        Ok(attrs)
125    }
126
127    fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
128        use syn::ImplItem::*;
129        use syn::*;
130        let attrs = match self {
131            Const(ImplItemConst { ref mut attrs, .. }) => attrs,
132            Fn(ImplItemFn { ref mut attrs, .. }) => attrs,
133            Type(ImplItemType { ref mut attrs, .. }) => attrs,
134            Macro(ImplItemMacro { ref mut attrs, .. }) => attrs,
135            other => {
136                return Err(Error::new_spanned(
137                    other,
138                    "this kind of item doesn't have attrs",
139                ))
140            }
141        };
142        Ok(attrs)
143    }
144
145    fn function_or_method(&self) -> Result<&dyn FunctionLike> {
146        match self {
147            ImplItem::Fn(f @ syn::ImplItemFn { .. }) => Ok(f),
148            other => Err(syn::Error::new_spanned(
149                other,
150                "this item is not a function or method",
151            )),
152        }
153    }
154
155    fn constant(&self) -> Result<&dyn ConstLike> {
156        match self {
157            ImplItem::Const(c @ syn::ImplItemConst { .. }) => Ok(c),
158            other => Err(syn::Error::new_spanned(other, "this item is not a const")),
159        }
160    }
161
162    fn is_type(&self) -> bool {
163        matches!(self, ImplItem::Type(_))
164    }
165    fn is_macro(&self) -> bool {
166        matches!(self, ImplItem::Macro(_))
167    }
168}
169
170impl ItemLike for TraitItem {
171    fn attrs(&self) -> Result<&[Attribute]> {
172        use syn::TraitItem::*;
173        use syn::*;
174        let attrs = match self {
175            Const(TraitItemConst { ref attrs, .. }) => attrs,
176            Fn(TraitItemFn { ref attrs, .. }) => attrs,
177            Type(TraitItemType { ref attrs, .. }) => attrs,
178            Macro(TraitItemMacro { ref attrs, .. }) => attrs,
179            other => {
180                return Err(Error::new_spanned(
181                    other,
182                    "this kind of item doesn't have attrs",
183                ))
184            }
185        };
186        Ok(attrs)
187    }
188
189    fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
190        use syn::TraitItem::*;
191        use syn::*;
192        let attrs = match self {
193            Const(TraitItemConst { ref mut attrs, .. }) => attrs,
194            Fn(TraitItemFn { ref mut attrs, .. }) => attrs,
195            Type(TraitItemType { ref mut attrs, .. }) => attrs,
196            Macro(TraitItemMacro { ref mut attrs, .. }) => attrs,
197            other => {
198                return Err(Error::new_spanned(
199                    other,
200                    "this kind of impl item doesn't have attrs",
201                ))
202            }
203        };
204        Ok(attrs)
205    }
206
207    fn function_or_method(&self) -> Result<&dyn FunctionLike> {
208        match self {
209            TraitItem::Fn(f @ syn::TraitItemFn { .. }) => Ok(f),
210            other => Err(syn::Error::new_spanned(
211                other,
212                "this item is not a function or method",
213            )),
214        }
215    }
216
217    fn constant(&self) -> Result<&dyn ConstLike> {
218        match self {
219            TraitItem::Const(c @ syn::TraitItemConst { .. }) => Ok(c),
220            other => Err(syn::Error::new_spanned(other, "this item is not a const")),
221        }
222    }
223
224    fn is_type(&self) -> bool {
225        matches!(self, TraitItem::Type(_))
226    }
227    fn is_macro(&self) -> bool {
228        matches!(self, TraitItem::Macro(_))
229    }
230}
231
232/// Extension for `syn::*Item::attrs` using `crate::ext::ItemLike`
233pub trait ItemAttrExt: ItemLike {
234    /// Takes a closure and calls it with separated attrs and item, as both mutable references.
235    ///
236    /// 1. Try to get attrs; Otherwise `Err`
237    /// 2. Split `attrs` from `self` with [std::mem::replace]
238    /// 3. Call the closure `f`
239    /// 4. Merge `attrs` into `self`
240    ///
241    /// Note: During the closure call, `attrs` in `self` is always an empty.
242    /// Always access `attrs` with given closure parameter.
243    ///
244    /// # Panics
245    /// Panics if replaced `attrs` in `self` is not empty at merge step.
246    fn try_split_attr_mut<F, R>(&mut self, f: F) -> Result<R>
247    where
248        F: FnOnce(&mut Vec<Attribute>, &mut Self) -> Result<R>,
249    {
250        let mut attrs = std::mem::take(self.attrs_mut()?);
251        let result = f(&mut attrs, self);
252        let _temp = std::mem::replace(self.attrs_mut().unwrap(), attrs);
253        assert!(
254            _temp.is_empty(),
255            "attrs changed during replacement. this behavior must be a bug."
256        );
257        result
258    }
259}
260
261impl ItemAttrExt for Item {}
262impl ItemAttrExt for ImplItem {}
263impl ItemAttrExt for TraitItem {}
264
265/// Extension for [syn::ItemMod]
266pub trait ItemModExt {
267    /// Returns reference of content items without braces unless a declaration
268    fn items(&self) -> Option<&[Item]>;
269    /// Returns reference of content items without braces unless a declaration
270    fn items_mut(&mut self) -> Option<&mut Vec<Item>>;
271}
272
273impl ItemModExt for ItemMod {
274    fn items(&self) -> Option<&[Item]> {
275        if let Some((_, content)) = self.content.as_ref() {
276            Some(content)
277        } else {
278            None
279        }
280    }
281    fn items_mut(&mut self) -> Option<&mut Vec<Item>> {
282        if let Some((_, content)) = self.content.as_mut() {
283            Some(content)
284        } else {
285            None
286        }
287    }
288}
289
290impl GetIdent for Item {
291    fn get_ident(&self) -> Option<&Ident> {
292        use syn::Item::*;
293        use syn::UseTree::*;
294        use syn::*;
295        #[allow(clippy::collapsible_match)]
296        let attrs = match self {
297            Const(ItemConst { ref ident, .. }) => ident,
298            Enum(ItemEnum { ref ident, .. }) => ident,
299            ExternCrate(ItemExternCrate { ref ident, .. }) => ident,
300            Fn(ItemFn { sig, .. }) => &sig.ident,
301            Impl(ItemImpl { .. }) => unimplemented!(),
302            Macro(ItemMacro { ref ident, .. }) => return ident.as_ref(),
303            Mod(ItemMod { ref ident, .. }) => ident,
304            Static(ItemStatic { ref ident, .. }) => ident,
305            Struct(ItemStruct { ref ident, .. }) => ident,
306            Trait(ItemTrait { ref ident, .. }) => ident,
307            TraitAlias(ItemTraitAlias { ref ident, .. }) => ident,
308            Type(ItemType { ref ident, .. }) => ident,
309            Union(ItemUnion { ref ident, .. }) => ident,
310            Use(ItemUse { ref tree, .. }) => match tree {
311                Name(UseName { ident }) => ident,
312                _ => return None,
313            },
314            _ => return None,
315        };
316        Some(attrs)
317    }
318}
319
320impl GetIdent for ImplItem {
321    fn get_ident(&self) -> Option<&Ident> {
322        use syn::ImplItem::*;
323        use syn::*;
324        let ident = match self {
325            Const(ImplItemConst { ref ident, .. }) => ident,
326            Fn(ImplItemFn { sig, .. }) => &sig.ident,
327            Type(ImplItemType { ref ident, .. }) => ident,
328            Macro(ImplItemMacro {
329                mac: syn::Macro { path, .. },
330                ..
331            }) => return path.get_ident(),
332            _ => return None,
333        };
334        Some(ident)
335    }
336}
337
338impl GetIdent for TraitItem {
339    fn get_ident(&self) -> Option<&Ident> {
340        use syn::TraitItem::*;
341        use syn::*;
342        let ident = match self {
343            Const(TraitItemConst { ref ident, .. }) => ident,
344            Fn(TraitItemFn { sig, .. }) => &sig.ident,
345            Type(TraitItemType { ref ident, .. }) => ident,
346            Macro(TraitItemMacro {
347                mac: syn::Macro { path, .. },
348                ..
349            }) => return path.get_ident(),
350            _ => return None,
351        };
352        Some(ident)
353    }
354}
355
356/// Extension for [syn::ItemFn], [syn::ImplItemMethod], and [syn::TraitItemFn]
357pub trait FunctionLike: Spanned {
358    /// Returns reference of attrs
359    fn attrs(&self) -> &[Attribute];
360    /// Returns mutable reference of attrs
361    fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
362    /// Return reference of vis
363    fn vis(&self) -> &syn::Visibility;
364
365    fn sig(&self) -> &syn::Signature;
366    fn block(&self) -> Option<&syn::Block>;
367}
368
369impl FunctionLike for ItemFn {
370    fn attrs(&self) -> &[Attribute] {
371        &self.attrs
372    }
373    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
374        &mut self.attrs
375    }
376    fn vis(&self) -> &syn::Visibility {
377        &self.vis
378    }
379    fn sig(&self) -> &syn::Signature {
380        &self.sig
381    }
382    fn block(&self) -> Option<&syn::Block> {
383        Some(&self.block)
384    }
385}
386
387impl FunctionLike for ImplItemFn {
388    fn attrs(&self) -> &[Attribute] {
389        &self.attrs
390    }
391    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
392        &mut self.attrs
393    }
394    fn vis(&self) -> &syn::Visibility {
395        &self.vis
396    }
397    fn sig(&self) -> &syn::Signature {
398        &self.sig
399    }
400    fn block(&self) -> Option<&syn::Block> {
401        Some(&self.block)
402    }
403}
404
405impl FunctionLike for TraitItemFn {
406    fn attrs(&self) -> &[Attribute] {
407        &self.attrs
408    }
409    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
410        &mut self.attrs
411    }
412    fn vis(&self) -> &syn::Visibility {
413        &syn::Visibility::Inherited
414    }
415    fn sig(&self) -> &syn::Signature {
416        &self.sig
417    }
418    fn block(&self) -> Option<&syn::Block> {
419        self.default.as_ref()
420    }
421}
422
423/// Extension for [syn::ItemConst], [syn::ImplItemConst], and [syn::TraitItemConst]
424pub trait ConstLike: Spanned {
425    /// Returns reference of attrs
426    fn attrs(&self) -> &[Attribute];
427    /// Returns mutable reference of attrs
428    fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
429    /// Return reference of vis
430    fn vis(&self) -> &syn::Visibility;
431    /// Return reference of const_token
432    fn const_token(&self) -> &syn::token::Const;
433    /// Return reference of ident
434    fn ident(&self) -> &syn::Ident;
435    /// Return reference of colon_token
436    fn colon_token(&self) -> &syn::token::Colon;
437    /// Return reference of ty
438    fn ty(&self) -> &syn::Type;
439}
440
441impl ConstLike for syn::ItemConst {
442    fn attrs(&self) -> &[Attribute] {
443        &self.attrs
444    }
445    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
446        &mut self.attrs
447    }
448    fn vis(&self) -> &syn::Visibility {
449        &self.vis
450    }
451    fn const_token(&self) -> &syn::token::Const {
452        &self.const_token
453    }
454    fn ident(&self) -> &syn::Ident {
455        &self.ident
456    }
457    fn colon_token(&self) -> &syn::token::Colon {
458        &self.colon_token
459    }
460    fn ty(&self) -> &syn::Type {
461        &self.ty
462    }
463}
464
465impl ConstLike for syn::ImplItemConst {
466    fn attrs(&self) -> &[Attribute] {
467        &self.attrs
468    }
469    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
470        &mut self.attrs
471    }
472    fn vis(&self) -> &syn::Visibility {
473        &self.vis
474    }
475    fn const_token(&self) -> &syn::token::Const {
476        &self.const_token
477    }
478    fn ident(&self) -> &syn::Ident {
479        &self.ident
480    }
481    fn colon_token(&self) -> &syn::token::Colon {
482        &self.colon_token
483    }
484    fn ty(&self) -> &syn::Type {
485        &self.ty
486    }
487}
488
489impl ConstLike for syn::TraitItemConst {
490    fn attrs(&self) -> &[Attribute] {
491        &self.attrs
492    }
493    fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
494        &mut self.attrs
495    }
496    fn vis(&self) -> &syn::Visibility {
497        &syn::Visibility::Inherited
498    }
499    fn const_token(&self) -> &syn::token::Const {
500        &self.const_token
501    }
502    fn ident(&self) -> &syn::Ident {
503        &self.ident
504    }
505    fn colon_token(&self) -> &syn::token::Colon {
506        &self.colon_token
507    }
508    fn ty(&self) -> &syn::Type {
509        &self.ty
510    }
511}
512
513#[cfg(test)]
514mod tests {
515    use super::*;
516    use crate::assert_quote_eq;
517    use quote::quote;
518    use syn::parse_quote;
519
520    #[test]
521    fn test_attrs() {
522        let mut item: Item = parse_quote!(
523            #[test]
524            type A = u32;
525        );
526        let expected: Attribute = parse_quote!(#[test]);
527        {
528            let attr = &item.attrs().unwrap()[0];
529            assert_quote_eq!(attr, expected);
530        }
531        {
532            let attr = &item.attrs_mut().unwrap()[0];
533            assert_quote_eq!(attr, expected);
534        }
535    }
536
537    #[test]
538    fn test_items() {
539        let module: ItemMod = parse_quote!(
540            mod m {
541                static x: usize = 0;
542                fn f() {}
543            }
544        );
545        let content = module.items().unwrap();
546        assert!(matches!(content[0], Item::Static(_)));
547        assert!(matches!(content[1], Item::Fn(_)));
548    }
549
550    #[test]
551    fn test_items_decl() {
552        let module: ItemMod = parse_quote!(
553            mod m;
554        );
555        assert!(module.items().is_none());
556    }
557
558    #[test]
559    fn test_function_like() {
560        let function: ItemFn = parse_quote!(
561            fn f(a: u8) -> Result<()> {}
562        );
563        let method: ImplItemFn = parse_quote!(
564            fn f(a: u8) -> Result<()> {}
565        );
566        assert_eq!(quote!(#function).to_string(), quote!(#method).to_string());
567    }
568}