syn-ext-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100124440ustar { "git": { "sha1": "5dfc37a9a31694d9e683d2d2269e805487a34f70" }, "path_in_vcs": "" }syn-ext-0.4.0/.github/workflows/ci.yaml000064400000000000000000000023570072674642500161470ustar 00000000000000name: Rust on: push: branches: [ main ] pull_request: branches: env: CARGO_TERM_COLOR: always jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable components: rustfmt override: true - name: rustfmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check test: runs-on: ubuntu-latest strategy: matrix: default: ["", "--no-default-features"] features: ["''", "derive", "parsing", "printing", "clone-impls", "proc-macro", "visit", "visit-mut", "fold", "extra-traits", "full"] steps: - uses: actions/checkout@v2 - name: run check uses: actions-rs/cargo@v1 with: command: check args: ${{ matrix.default }} --features ${{ matrix.features }} --verbose - name: run test uses: actions-rs/cargo@v1 with: command: test args: ${{ matrix.default }} --features ${{ matrix.features }} --verbose - name: clippy uses: actions-rs/cargo@v1 with: command: clippy args: ${{ matrix.default }} --features ${{ matrix.features }} -- -Dwarnings syn-ext-0.4.0/.gitignore000064400000000000000000000005260072674642500132570ustar 00000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk # IDE stuff .vscode syn-ext-0.4.0/Cargo.toml0000644000000023260000000000100104450ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "syn-ext" version = "0.4.0" authors = ["Jeong YunWon "] description = "Human friendly or editable extension for syn" documentation = "https://docs.rs/syn-ext/" license-file = "LICENSE" [package.metadata.docs.rs] features = ["full"] [dependencies.syn] version = "^1" [dev-dependencies.quote] version = "^1" [features] clone-impls = ["syn/clone-impls"] default = [ "derive", "parsing", "printing", "clone-impls", "proc-macro", "syn/default", ] derive = ["syn/derive"] extra-traits = ["syn/extra-traits"] fold = ["syn/fold"] full = ["syn/full"] parsing = ["syn/parsing"] printing = ["syn/printing"] proc-macro = ["syn/proc-macro"] visit = ["syn/visit"] visit-mut = ["syn/visit-mut"] syn-ext-0.4.0/Cargo.toml.orig000064400000000000000000000015430072674642500141560ustar 00000000000000[package] name = "syn-ext" version = "0.4.0" authors = ["Jeong YunWon "] edition = "2018" description = "Human friendly or editable extension for syn" license-file = "LICENSE" documentation = "https://docs.rs/syn-ext/" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["derive", "parsing", "printing", "clone-impls", "proc-macro", "syn/default"] derive = ["syn/derive"] full = ["syn/full"] parsing = ["syn/parsing"] printing = ["syn/printing"] visit = ["syn/visit"] visit-mut = ["syn/visit-mut"] fold = ["syn/fold"] clone-impls = ["syn/clone-impls"] extra-traits = ["syn/extra-traits"] proc-macro = ["syn/proc-macro"] # test = ["syn-test-suite/all-features"] [dependencies] syn = "^1" [dev-dependencies] quote = "^1" [package.metadata.docs.rs] features = ["full"] syn-ext-0.4.0/LICENSE000064400000000000000000000024520072674642500122740ustar 00000000000000BSD 2-Clause License Copyright (c) 2020, Jeong YunWon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syn-ext-0.4.0/README.md000064400000000000000000000003410072674642500125410ustar 00000000000000# syn-ext: shortcuts & editable interface ```rust use syn_ext::ext::*; ``` - Documentation: [https://docs.rs/syn-ext/](https://docs.rs/syn-ext/) - Crates: [https://crates.io/crates/syn-ext](https://crates.io/crates/syn-ext)syn-ext-0.4.0/src/attribute.rs000064400000000000000000000163320072674642500144310ustar 00000000000000use crate::ident::GetIdent; #[cfg(feature = "parsing")] use crate::meta::{self, MetaExt}; use syn::{parse_quote, Attribute, Ident, Meta, MetaList}; #[cfg(feature = "parsing")] use syn::{punctuated::Punctuated, token::Paren, Result}; impl GetIdent for Attribute { /// Get ident of the [syn::Attribute::path] field. fn get_ident(&self) -> Option<&Ident> { self.path.get_ident() } } /// Extension for [syn::Attribute] #[cfg(feature = "parsing")] pub trait AttributeExt { /// Constructs and returns a new [syn::Attribute] from [syn::Meta] fn from_meta(meta: M) -> Self where M: IntoAttribute; /// Takes a closure and calls it with parsed meta. After call, applys back the manipulated [syn::Meta]. /// /// 1. Try [syn::Attribute::parse_meta]; return if `Err` /// 2. Run `f` /// 3. Apply back the manipulated [syn::Meta] by `f`. /// 4. Return the result of `f`. /// /// Note: Even `f` returns `Err`, meta will be made into self. fn try_meta_mut(&mut self, f: F) -> Result where F: FnOnce(&mut Meta) -> Result; /// Returns a fake promoted list value of [syn::MetaList]. /// /// If [syn::Meta::List], return inner [syn::MetaList]. /// If [syn::Meta::Path], return a fake [syn::MetaList] with default paren and empty nested. /// Otherwise return `Err` fn promoted_list(&self) -> Result; /// Takes a closure and calls it with promoted list of parsed meta. After call, applys back the manipulated [syn::MetaList]. /// /// 1. Try [syn::Attribute::parse_meta]; return if `Err` /// 2. Promote to [syn::Meta::List] if [syn::Meta::Path] /// 3. Run `f` to inner [syn::MetaList] /// 4. Apply back the manipulated [syn::Meta] by `f`. /// 5. Return the result of `f`. fn try_promoted_list_mut(&mut self, paren: Paren, f: F) -> Result where F: FnOnce(&mut MetaList) -> Result; } #[cfg(feature = "parsing")] impl AttributeExt for Attribute { fn from_meta(meta: M) -> Self where M: IntoAttribute, { meta.into_attribute() } fn try_meta_mut(&mut self, f: F) -> Result where F: FnOnce(&mut Meta) -> Result, { let mut meta = self.parse_meta()?; let result = f(&mut meta); *self = Self::from_meta(meta); result } fn promoted_list(&self) -> Result { match self.parse_meta()? { Meta::Path(path) => Ok(MetaList { path, paren_token: Default::default(), nested: Punctuated::new(), }), Meta::List(metalist) => Ok(metalist), other => Err(meta::err_promote_to_list(&other)), } } fn try_promoted_list_mut(&mut self, paren: Paren, f: F) -> Result where F: FnOnce(&mut MetaList) -> Result, { self.try_meta_mut(|meta| { let metalist = meta.promote_to_list(paren)?; f(metalist) }) } } /// Extension for `std::iter::Iterator<[syn::Attribute]>` #[cfg(feature = "parsing")] pub trait AttributeIteratorExt { // fn doc_items(&self) -> impl std::iter::Iterator; /// Constructs and returns doc comment string by joining doc from multiple attrs fn doc(self) -> Option; // fn filter_name<'a, P>( // &'a self, // name: &'static str, // ) -> std::iter::Filter, P> // where // P: FnMut(&&'a syn::Attribute) -> bool; } #[cfg(feature = "parsing")] impl<'a, I> AttributeIteratorExt for I where I: std::iter::IntoIterator, { fn doc(self) -> Option { let items: Vec<_> = self .into_iter() .filter_map(|attr| attr.parse_meta().ok().and_then(|m| m.doc().ok())) .collect(); if items.is_empty() { None } else { Some(items.join("\n")) } } // fn filter_name<'a, P>( // &'a self, // name: &'static str, // ) -> std::iter::Filter, P> // where // P: FnMut(&&'a syn::Attribute) -> bool, // { // let mut p = |attr: &&'a Attribute| { // attr.path // .get_ident() // .map_or(false, |ident| ident.to_string() == name) // }; // self.iter().filter(p) // } } #[cfg(test)] mod test { use super::*; use crate::assert_quote_eq; use quote::quote; #[cfg(feature = "parsing")] fn test_meta_round_trip(attr: Attribute) -> Result<()> { let meta = attr.parse_meta()?; let created = Attribute::from_meta(meta); assert_eq!( quote! { #attr }.to_string(), quote! { #created }.to_string() ); Ok(()) } #[cfg(feature = "parsing")] #[test] fn run_test_meta_round_trip() { use syn::parse_quote; test_meta_round_trip(parse_quote! { #[cfg(test)] }).unwrap(); test_meta_round_trip(parse_quote! { #[feature = "full"] }).unwrap(); test_meta_round_trip(parse_quote! { #[cfg(all(a,b,any(c,d)))] }).unwrap(); test_meta_round_trip(parse_quote! { #[a(b="1",d)] }).unwrap(); test_meta_round_trip(parse_quote! { #[abc::de::ef] }).unwrap(); } #[cfg(feature = "parsing")] #[test] fn test_try_meta_mut() { let mut attr: Attribute = parse_quote! { #[cfg(test)] }; attr.try_meta_mut(|meta| match meta { Meta::List(metalist) => { metalist.path = parse_quote! { newcfg }; Ok(()) } _ => unreachable!(), }) .unwrap(); let expected: Attribute = parse_quote! { #[newcfg(test)] }; assert_quote_eq!(attr, expected); attr.try_meta_mut(|meta| match meta { Meta::List(metalist) => { metalist.nested.pop(); metalist.nested.push(parse_quote!(a)); metalist.nested.push(parse_quote!(b = "c")); metalist.nested.push(parse_quote!("d")); Ok(()) } _ => unreachable!(), }) .unwrap(); let expected: Attribute = parse_quote! { #[newcfg(a, b="c", "d")] }; assert_quote_eq!(attr, expected); } #[test] #[cfg(feature = "parsing")] fn test_promoted_list() { let attr: Attribute = parse_quote! { #[derive] }; let list = attr.promoted_list().unwrap(); assert_quote_eq!(attr.path, list.path); assert!(list.nested.is_empty()); } #[cfg(all(feature = "parsing", feature = "full"))] #[test] fn test_doc() { let func: syn::ItemFn = parse_quote! { #[derive] /// doc line 1 #[test] #[doc = "doc line 2"] #[cfg] fn f() {} }; let doc = func.attrs.doc().unwrap(); assert_eq!(doc, "doc line 1\ndoc line 2"); } } pub trait IntoAttribute { fn into_attribute(self) -> Attribute; } impl IntoAttribute for Meta { fn into_attribute(self) -> Attribute { parse_quote!( #[ #self ] ) } } impl IntoAttribute for MetaList { fn into_attribute(self) -> Attribute { Meta::List(self).into_attribute() } } syn-ext-0.4.0/src/generics.rs000064400000000000000000000005040072674642500142170ustar 00000000000000use crate::ext::GetIdent; use syn::{GenericParam, Ident}; impl GetIdent for GenericParam { fn get_ident(&self) -> Option<&Ident> { match self { Self::Type(t) => Some(&t.ident), Self::Lifetime(l) => Some(&l.lifetime.ident), Self::Const(c) => Some(&c.ident), } } } syn-ext-0.4.0/src/ident.rs000064400000000000000000000005110072674642500135210ustar 00000000000000use syn::Ident; /// Shortcut to get [syn::Ident](struct@syn::Ident) from various types pub trait GetIdent { /// Returns reference of ident if its [syn::Path] is [syn::Ident](struct@syn::Ident); Otherwise None /// /// Any [crate::ext::GetPath] also implements `GetIdent`. fn get_ident(&self) -> Option<&Ident>; } syn-ext-0.4.0/src/item.rs000064400000000000000000000430200072674642500133560ustar 00000000000000use crate::ident::GetIdent; use syn::{ spanned::Spanned, Attribute, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod, Result, TraitItem, TraitItemMethod, }; /// Extension for [syn::Item] pub trait ItemLike: Spanned { /// Returns reference of inner attrs if not verbatim; otherwise `Err` fn attrs(&self) -> Result<&[Attribute]>; /// Returns mutable reference of inner attrs if not verbatim; otherwise `Err` fn attrs_mut(&mut self) -> Result<&mut Vec>; /// Returns function-like trait object of Item::Fn, ImplItem::Method, or TraitItem::Method fn function_or_method(&self) -> Result<&dyn FunctionLike>; /// Returns const-like trait object of Item::Const, ImplItem::Const, or TraitItem::Const fn constant(&self) -> Result<&dyn ConstLike>; /// Returns `true` if self matches `*ItemType` fn is_type(&self) -> bool; /// Returns `true` if self matches `*ItemMacro` fn is_macro(&self) -> bool; } impl ItemLike for Item { fn attrs(&self) -> Result<&[Attribute]> { use syn::Item::*; use syn::*; let attrs = match self { Const(ItemConst { ref attrs, .. }) => attrs, Enum(ItemEnum { ref attrs, .. }) => attrs, ExternCrate(ItemExternCrate { ref attrs, .. }) => attrs, Fn(ItemFn { ref attrs, .. }) => attrs, ForeignMod(ItemForeignMod { ref attrs, .. }) => attrs, Impl(ItemImpl { ref attrs, .. }) => attrs, Macro(ItemMacro { ref attrs, .. }) => attrs, Macro2(ItemMacro2 { ref attrs, .. }) => attrs, Mod(ItemMod { ref attrs, .. }) => attrs, Static(ItemStatic { ref attrs, .. }) => attrs, Struct(ItemStruct { ref attrs, .. }) => attrs, Trait(ItemTrait { ref attrs, .. }) => attrs, TraitAlias(ItemTraitAlias { ref attrs, .. }) => attrs, Type(ItemType { ref attrs, .. }) => attrs, Union(ItemUnion { ref attrs, .. }) => attrs, Use(ItemUse { ref attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of item doesn't have attrs", )) } }; Ok(attrs) } fn attrs_mut(&mut self) -> Result<&mut Vec> { use syn::Item::*; use syn::*; let attrs = match self { Const(ItemConst { ref mut attrs, .. }) => attrs, Enum(ItemEnum { ref mut attrs, .. }) => attrs, ExternCrate(ItemExternCrate { ref mut attrs, .. }) => attrs, Fn(ItemFn { ref mut attrs, .. }) => attrs, ForeignMod(ItemForeignMod { ref mut attrs, .. }) => attrs, Impl(ItemImpl { ref mut attrs, .. }) => attrs, Macro(ItemMacro { ref mut attrs, .. }) => attrs, Macro2(ItemMacro2 { ref mut attrs, .. }) => attrs, Mod(ItemMod { ref mut attrs, .. }) => attrs, Static(ItemStatic { ref mut attrs, .. }) => attrs, Struct(ItemStruct { ref mut attrs, .. }) => attrs, Trait(ItemTrait { ref mut attrs, .. }) => attrs, TraitAlias(ItemTraitAlias { ref mut attrs, .. }) => attrs, Type(ItemType { ref mut attrs, .. }) => attrs, Union(ItemUnion { ref mut attrs, .. }) => attrs, Use(ItemUse { ref mut attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of item doesn't have attrs", )) } }; Ok(attrs) } fn function_or_method(&self) -> Result<&dyn FunctionLike> { match self { Item::Fn(f @ syn::ItemFn { .. }) => Ok(f), other => Err(syn::Error::new_spanned( other, "this item is not a function or method", )), } } fn constant(&self) -> Result<&dyn ConstLike> { match self { Item::Const(c @ syn::ItemConst { .. }) => Ok(c), other => Err(syn::Error::new_spanned(other, "this item is not a const")), } } fn is_type(&self) -> bool { matches!(self, Item::Type(_)) } fn is_macro(&self) -> bool { matches!(self, Item::Macro(_)) } } impl ItemLike for ImplItem { fn attrs(&self) -> Result<&[Attribute]> { use syn::ImplItem::*; use syn::*; let attrs = match self { Const(ImplItemConst { ref attrs, .. }) => attrs, Method(ImplItemMethod { ref attrs, .. }) => attrs, Type(ImplItemType { ref attrs, .. }) => attrs, Macro(ImplItemMacro { ref attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of item doesn't have attrs", )) } }; Ok(attrs) } fn attrs_mut(&mut self) -> Result<&mut Vec> { use syn::ImplItem::*; use syn::*; let attrs = match self { Const(ImplItemConst { ref mut attrs, .. }) => attrs, Method(ImplItemMethod { ref mut attrs, .. }) => attrs, Type(ImplItemType { ref mut attrs, .. }) => attrs, Macro(ImplItemMacro { ref mut attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of item doesn't have attrs", )) } }; Ok(attrs) } fn function_or_method(&self) -> Result<&dyn FunctionLike> { match self { ImplItem::Method(f @ syn::ImplItemMethod { .. }) => Ok(f), other => Err(syn::Error::new_spanned( other, "this item is not a function or method", )), } } fn constant(&self) -> Result<&dyn ConstLike> { match self { ImplItem::Const(c @ syn::ImplItemConst { .. }) => Ok(c), other => Err(syn::Error::new_spanned(other, "this item is not a const")), } } fn is_type(&self) -> bool { matches!(self, ImplItem::Type(_)) } fn is_macro(&self) -> bool { matches!(self, ImplItem::Macro(_)) } } impl ItemLike for TraitItem { fn attrs(&self) -> Result<&[Attribute]> { use syn::TraitItem::*; use syn::*; let attrs = match self { Const(TraitItemConst { ref attrs, .. }) => attrs, Method(TraitItemMethod { ref attrs, .. }) => attrs, Type(TraitItemType { ref attrs, .. }) => attrs, Macro(TraitItemMacro { ref attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of item doesn't have attrs", )) } }; Ok(attrs) } fn attrs_mut(&mut self) -> Result<&mut Vec> { use syn::TraitItem::*; use syn::*; let attrs = match self { Const(TraitItemConst { ref mut attrs, .. }) => attrs, Method(TraitItemMethod { ref mut attrs, .. }) => attrs, Type(TraitItemType { ref mut attrs, .. }) => attrs, Macro(TraitItemMacro { ref mut attrs, .. }) => attrs, other => { return Err(Error::new_spanned( other, "this kind of impl item doesn't have attrs", )) } }; Ok(attrs) } fn function_or_method(&self) -> Result<&dyn FunctionLike> { match self { TraitItem::Method(f @ syn::TraitItemMethod { .. }) => Ok(f), other => Err(syn::Error::new_spanned( other, "this item is not a function or method", )), } } fn constant(&self) -> Result<&dyn ConstLike> { match self { TraitItem::Const(c @ syn::TraitItemConst { .. }) => Ok(c), other => Err(syn::Error::new_spanned(other, "this item is not a const")), } } fn is_type(&self) -> bool { matches!(self, TraitItem::Type(_)) } fn is_macro(&self) -> bool { matches!(self, TraitItem::Macro(_)) } } /// Extension for `syn::*Item::attrs` using `crate::ext::ItemLike` pub trait ItemAttrExt: ItemLike { /// Takes a closure and calls it with separated attrs and item, as both mutable references. /// /// 1. Try to get attrs; Otherwise `Err` /// 2. Split `attrs` from `self` with [std::mem::replace] /// 3. Call the closure `f` /// 4. Merge `attrs` into `self` /// /// Note: During the closure call, `attrs` in `self` is always an empty. /// Always access `attrs` with given closure parameter. /// /// # Panics /// Panics if replaced `attrs` in `self` is not empty at merge step. fn try_split_attr_mut(&mut self, f: F) -> Result where F: FnOnce(&mut Vec, &mut Self) -> Result, { let mut attrs = std::mem::take(self.attrs_mut()?); let result = f(&mut attrs, self); let _temp = std::mem::replace(self.attrs_mut().unwrap(), attrs); assert!( _temp.is_empty(), "attrs changed during replacement. this behavior must be a bug." ); result } } impl ItemAttrExt for Item {} impl ItemAttrExt for ImplItem {} impl ItemAttrExt for TraitItem {} /// Extension for [syn::ItemMod] pub trait ItemModExt { /// Returns reference of content items without braces unless a declaration fn items(&self) -> Option<&[Item]>; /// Returns reference of content items without braces unless a declaration fn items_mut(&mut self) -> Option<&mut Vec>; } impl ItemModExt for ItemMod { fn items(&self) -> Option<&[Item]> { if let Some((_, content)) = self.content.as_ref() { Some(content) } else { None } } fn items_mut(&mut self) -> Option<&mut Vec> { if let Some((_, content)) = self.content.as_mut() { Some(content) } else { None } } } impl GetIdent for Item { fn get_ident(&self) -> Option<&Ident> { use syn::Item::*; use syn::UseTree::*; use syn::*; #[allow(clippy::collapsible_match)] let attrs = match self { Const(ItemConst { ref ident, .. }) => ident, Enum(ItemEnum { ref ident, .. }) => ident, ExternCrate(ItemExternCrate { ref ident, .. }) => ident, Fn(ItemFn { sig, .. }) => &sig.ident, Impl(ItemImpl { .. }) => unimplemented!(), Macro(ItemMacro { ref ident, .. }) => return ident.as_ref(), Macro2(ItemMacro2 { ref ident, .. }) => ident, Mod(ItemMod { ref ident, .. }) => ident, Static(ItemStatic { ref ident, .. }) => ident, Struct(ItemStruct { ref ident, .. }) => ident, Trait(ItemTrait { ref ident, .. }) => ident, TraitAlias(ItemTraitAlias { ref ident, .. }) => ident, Type(ItemType { ref ident, .. }) => ident, Union(ItemUnion { ref ident, .. }) => ident, Use(ItemUse { ref tree, .. }) => match tree { Name(UseName { ident }) => ident, _ => return None, }, _ => return None, }; Some(attrs) } } impl GetIdent for ImplItem { fn get_ident(&self) -> Option<&Ident> { use syn::ImplItem::*; use syn::*; let ident = match self { Const(ImplItemConst { ref ident, .. }) => ident, Method(ImplItemMethod { sig, .. }) => &sig.ident, Type(ImplItemType { ref ident, .. }) => ident, Macro(ImplItemMacro { mac: syn::Macro { path, .. }, .. }) => return path.get_ident(), _ => return None, }; Some(ident) } } impl GetIdent for TraitItem { fn get_ident(&self) -> Option<&Ident> { use syn::TraitItem::*; use syn::*; let ident = match self { Const(TraitItemConst { ref ident, .. }) => ident, Method(TraitItemMethod { sig, .. }) => &sig.ident, Type(TraitItemType { ref ident, .. }) => ident, Macro(TraitItemMacro { mac: syn::Macro { path, .. }, .. }) => return path.get_ident(), _ => return None, }; Some(ident) } } /// Extension for [syn::ItemFn], [syn::ImplItemMethod], and [syn::TraitItemMethod] pub trait FunctionLike: Spanned { /// Returns reference of attrs fn attrs(&self) -> &[Attribute]; /// Returns mutable reference of attrs fn attrs_mut(&mut self) -> &mut Vec; /// Return reference of vis fn vis(&self) -> &syn::Visibility; fn sig(&self) -> &syn::Signature; fn block(&self) -> Option<&syn::Block>; } impl FunctionLike for ItemFn { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &self.vis } fn sig(&self) -> &syn::Signature { &self.sig } fn block(&self) -> Option<&syn::Block> { Some(&self.block) } } impl FunctionLike for ImplItemMethod { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &self.vis } fn sig(&self) -> &syn::Signature { &self.sig } fn block(&self) -> Option<&syn::Block> { Some(&self.block) } } impl FunctionLike for TraitItemMethod { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &syn::Visibility::Inherited } fn sig(&self) -> &syn::Signature { &self.sig } fn block(&self) -> Option<&syn::Block> { self.default.as_ref() } } /// Extension for [syn::ItemConst], [syn::ImplItemConst], and [syn::TraitItemConst] pub trait ConstLike: Spanned { /// Returns reference of attrs fn attrs(&self) -> &[Attribute]; /// Returns mutable reference of attrs fn attrs_mut(&mut self) -> &mut Vec; /// Return reference of vis fn vis(&self) -> &syn::Visibility; /// Return reference of const_token fn const_token(&self) -> &syn::token::Const; /// Return reference of ident fn ident(&self) -> &syn::Ident; /// Return reference of colon_token fn colon_token(&self) -> &syn::token::Colon; /// Return reference of ty fn ty(&self) -> &syn::Type; } impl ConstLike for syn::ItemConst { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &self.vis } fn const_token(&self) -> &syn::token::Const { &self.const_token } fn ident(&self) -> &syn::Ident { &self.ident } fn colon_token(&self) -> &syn::token::Colon { &self.colon_token } fn ty(&self) -> &syn::Type { &self.ty } } impl ConstLike for syn::ImplItemConst { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &self.vis } fn const_token(&self) -> &syn::token::Const { &self.const_token } fn ident(&self) -> &syn::Ident { &self.ident } fn colon_token(&self) -> &syn::token::Colon { &self.colon_token } fn ty(&self) -> &syn::Type { &self.ty } } impl ConstLike for syn::TraitItemConst { fn attrs(&self) -> &[Attribute] { &self.attrs } fn attrs_mut(&mut self) -> &mut Vec { &mut self.attrs } fn vis(&self) -> &syn::Visibility { &syn::Visibility::Inherited } fn const_token(&self) -> &syn::token::Const { &self.const_token } fn ident(&self) -> &syn::Ident { &self.ident } fn colon_token(&self) -> &syn::token::Colon { &self.colon_token } fn ty(&self) -> &syn::Type { &self.ty } } #[cfg(test)] mod tests { use super::*; use crate::assert_quote_eq; use quote::quote; use syn::parse_quote; #[test] fn test_attrs() { let mut item: Item = parse_quote!( #[test] type A = u32; ); let expected: Attribute = parse_quote!(#[test]); { let attr = &item.attrs().unwrap()[0]; assert_quote_eq!(attr, expected); } { let attr = &item.attrs_mut().unwrap()[0]; assert_quote_eq!(attr, expected); } } #[test] fn test_items() { let module: ItemMod = parse_quote!( mod m { static x: usize = 0; fn f() {} } ); let content = module.items().unwrap(); assert!(matches!(content[0], Item::Static(_))); assert!(matches!(content[1], Item::Fn(_))); } #[test] fn test_items_decl() { let module: ItemMod = parse_quote!( mod m; ); assert!(module.items().is_none()); } #[test] fn test_function_like() { let function: ItemFn = parse_quote!( fn f(a: u8) -> Result<()> {} ); let method: ImplItemMethod = parse_quote!( fn f(a: u8) -> Result<()> {} ); assert_eq!(quote!(#function).to_string(), quote!(#method).to_string()); } } syn-ext-0.4.0/src/lib.rs000064400000000000000000000032060072674642500131700ustar 00000000000000//! Collections of syn shortcuts and editable interface. //! //! Start with `use syn_ext::ext::*;` //! Look around extension methods in [ext module](ext/index.html) #[cfg(any(feature = "derive", feature = "full"))] mod attribute; #[cfg(any(feature = "derive", feature = "full"))] mod generics; mod ident; #[cfg(feature = "full")] mod item; #[cfg(any(feature = "derive", feature = "full"))] mod meta; mod path; mod punctuated; mod shared; #[cfg(test)] #[macro_use] mod test; /// `use syn_ext::ext::*`; // Namespace module for extension traits. /// /// Always try to use `*`. /// The public names here are intended to be used as `*` and will be changed any time. pub mod ext { // only extension traits can be named here mod basic { pub use crate::ident::GetIdent; pub use crate::punctuated::PunctuatedExt; } #[cfg(any(feature = "derive", feature = "full"))] mod derive { #[cfg(any(feature = "parsing"))] pub use crate::attribute::{AttributeExt, AttributeIteratorExt}; #[cfg(any(feature = "parsing"))] pub use crate::meta::MetaAttributeExt; pub use crate::meta::{ MetaExt, MetaIteratorExt, NestedMetaIteratorExt, NestedMetaRefIteratorExt, }; pub use crate::path::GetPath; } #[cfg(feature = "full")] mod full { pub use crate::item::{ItemAttrExt, ItemLike, ItemModExt}; } pub use basic::*; #[cfg(any(feature = "derive", feature = "full"))] pub use derive::*; #[cfg(feature = "full")] pub use full::*; } pub mod types { #[cfg(any(feature = "derive", feature = "full"))] pub use crate::meta::PunctuatedNestedMeta; } syn-ext-0.4.0/src/meta.rs000064400000000000000000000372630072674642500133620ustar 00000000000000use crate::ident::GetIdent; use crate::path::GetPath; use crate::shared::{thread_local_ref, SharedEmpty}; use std::collections::HashMap as Map; use syn::{ punctuated::Punctuated, token, Attribute, Error, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta, Path, Result, }; /// Shortcut type for [syn::MetaList::nested] pub type PunctuatedNestedMeta = Punctuated; /// Extension for [syn::Meta] pub trait MetaExt { /// Returns `true` if self matches [syn::Meta::Path] fn is_path(&self) -> bool; /// Returns `true` if self matches [syn::Meta::List] fn is_list(&self) -> bool; /// Returns `true` if self matches [syn::Meta::NameValue] fn is_name_value(&self) -> bool; /// Returns `true` if the content matches `doc = ` fn is_doc(&self) -> bool; /// Promotes to empty [syn::Meta::List] with given `paren` if [syn::Meta::Path] /// /// A [syn::Meta::Path] value can be regarded as an empty [syn::Meta::List]. /// `promote` means converting [syn::Meta::Path] to an actual empty [syn::Meta::List]. fn promote_to_list(&mut self, paren: token::Paren) -> Result<&mut MetaList>; /// Returns [syn::MetaList] of [syn::Meta::List]; Otherwise `Err` fn list(&self) -> Result<&MetaList>; /// Returns [syn::MetaList] of [syn::Meta::List]; Otherwise `Err` fn list_mut(&mut self) -> Result<&mut MetaList>; /// Returns [syn::MetaNameValue] of [syn::Meta::NameValue]; Otherwise `Err` fn name_value(&self) -> Result<&MetaNameValue>; /// Returns [syn::MetaNameValue] of [syn::Meta::NameValue]; Otherwise `Err` fn name_value_mut(&mut self) -> Result<&mut MetaNameValue>; /// Returns content of `doc = ` fn doc(&self) -> Result; } pub(crate) fn err_promote_to_list(meta: &Meta) -> Error { Error::new_spanned( meta, "Only Path can be promoted and List is accepted as non-promoted", ) } impl MetaExt for Meta { fn is_path(&self) -> bool { matches!(self, Meta::Path(_)) } fn is_list(&self) -> bool { matches!(self, Meta::List(_)) } fn is_name_value(&self) -> bool { matches!(self, Meta::NameValue(_)) } fn is_doc(&self) -> bool { self.name_value().map_or(false, |v| { v.path.is_ident("doc") && matches!(v.lit, Lit::Str(_)) }) } fn promote_to_list(&mut self, paren: token::Paren) -> Result<&mut MetaList> { let path = match self { Meta::Path(path) => path.clone(), Meta::List(metalist) => return Ok(metalist), other => return Err(err_promote_to_list(other)), }; *self = Meta::List(MetaList { path, paren_token: paren, nested: PunctuatedNestedMeta::new(), }); self.list_mut() } fn list(&self) -> Result<&MetaList> { match self { Meta::List(ref list) => Ok(list), other => Err(Error::new_spanned(other, "Not a List meta")), } } fn list_mut(&mut self) -> Result<&mut MetaList> { match self { Meta::List(ref mut list) => Ok(list), other => Err(Error::new_spanned(other, "Not a List meta")), } } fn name_value(&self) -> Result<&MetaNameValue> { match self { Meta::NameValue(ref name) => Ok(name), other => Err(Error::new_spanned(other, "Not a NameValue meta")), } } fn name_value_mut(&mut self) -> Result<&mut MetaNameValue> { match self { Meta::NameValue(ref mut name) => Ok(name), other => Err(Error::new_spanned(other, "Not a NameValue meta")), } } fn doc(&self) -> Result { let name_value = self.name_value()?; if !name_value.path.is_ident("doc") { return Err(Error::new_spanned(name_value, "Not a doc meta")); } match &name_value.lit { Lit::Str(lit) => Ok(lit.value().trim().to_owned()), other => Err(Error::new_spanned(other, "Doc meta expects string literal")), } } } // where M: 'a + std::borrow::Borrow type IndexMetaRef = (usize, M); type MultiMetaMap<'a, K, M> = Map>>; type UniqueMetaMap<'a, K, M> = Map>; /// Constructs and returns map from [syn::Meta] iterator pub trait MetaIteratorExt<'a, M> where M: 'a + std::borrow::Borrow, { /// Constructs and returns a multi-value map from [syn::Meta] iterator fn to_multi_map(self, path_to_key: KF) -> Result> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; /// Constructs and returns a unique-value map from [syn::Meta] iterator. `Err` if duplicates. fn to_unique_map(self, path_to_key: KF) -> Result> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; } // fn to_multi_map<'a, I, M, K, KF>(iter: I, path_to_key: KF) -> Result> // where // M: 'a + std::borrow::Borrow, // I: std::iter::Iterator>, // K: std::hash::Hash + Eq, // KF: Fn(&Path) -> Result>, // { // let mut map: Map> = Map::new(); // for (i, meta) in iter { // let path = meta.borrow().path(); // let key = if let Some(key) = path_to_key(path)? { // key // } else { // continue; // }; // map.entry(key).or_default().push((i, meta)) // } // Ok(map) // } impl<'a, I, M> MetaIteratorExt<'a, M> for I where M: 'a + std::borrow::Borrow, I: std::iter::Iterator>, { // easier KF with traits? // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push fn to_multi_map(self, path_to_key: KF) -> Result> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut map: Map> = Map::new(); for (i, meta) in self { let path = meta.borrow().path(); let key = if let Some(key) = path_to_key(path)? { key } else { continue; }; map.entry(key).or_default().push((i, meta)) } Ok(map) } fn to_unique_map(self, path_to_key: KF) -> Result> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut map = Map::new(); for (i, meta) in self { let path = meta.borrow().path(); let key = if let Some(key) = path_to_key(path)? { key } else { continue; }; if let Some((_, removed)) = map.insert(key, (i, meta)) { return Err(Error::new_spanned( removed.borrow(), "this attribute path must be unique in the attribute", )); } } Ok(map) } } /// experimental #[allow(clippy::type_complexity)] pub trait NestedMetaRefIteratorExt<'a, M> where M: 'a + std::borrow::Borrow, { // fn to_map_and_lits( // &'a self, // path_to_key: KF, // mata_to_map: MF, // ) -> Result<(MT, Vec<(usize, &'a Lit)>)> // where // K: std::hash::Hash + Eq, // KF: Fn(&Path) -> Result>, // MF: Fn(MI, KF) -> Result, // MI: std::iter::Iterator; fn to_multi_map_and_lits( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, M>, Vec<(usize, &'a Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; fn to_unique_map_and_lits( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, M>, Vec<(usize, &'a Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; } #[allow(clippy::type_complexity)] impl<'a, I> NestedMetaRefIteratorExt<'a, &'a Meta> for I where I: std::iter::IntoIterator, { // easier KF with traits? // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push // fn to_map_and_lits( // &'a self, // path_to_key: KF, // mata_iter_to_map: MF, // ) -> Result<(MT, Vec<(usize, &'a Lit)>)> // where // K: std::hash::Hash + Eq, // KF: Fn(&Path) -> Result>, // MF: Fn(MI, KF) -> Result, // MI: std::iter::Iterator, // { // let mut metas: Vec<(_, &'a _)> = Vec::new(); // let mut lits = Vec::new(); // for (i, nmeta) in self.iter().enumerate() { // match nmeta { // NestedMeta::Meta(meta) => metas.push((i, meta)), // NestedMeta::Lit(lit) => lits.push((i, lit)), // } // } // let map = mata_iter_to_map(metas.into_iter(), path_to_key)?; // Ok((map, lits)) // } fn to_multi_map_and_lits( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, &'a Meta>, Vec<(usize, &'a Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut lits = Vec::new(); for (i, nmeta) in self.into_iter().enumerate() { match nmeta { NestedMeta::Meta(meta) => metas.push((i, meta)), NestedMeta::Lit(lit) => lits.push((i, lit)), } } let map = metas.into_iter().to_multi_map(path_to_key)?; Ok((map, lits)) } fn to_unique_map_and_lits( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, &'a Meta>, Vec<(usize, &'a Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut lits = Vec::new(); for (i, nmeta) in self.into_iter().enumerate() { match nmeta { NestedMeta::Meta(meta) => metas.push((i, meta)), NestedMeta::Lit(lit) => lits.push((i, lit)), } } let map = metas.into_iter().to_unique_map(path_to_key)?; Ok((map, lits)) } } /// experimental #[allow(clippy::type_complexity)] pub trait NestedMetaIteratorExt<'a> { fn into_multi_map_and_lits( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, Meta>, Vec<(usize, Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; fn into_unique_map_and_lits( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, Meta>, Vec<(usize, Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; } #[allow(clippy::type_complexity)] impl<'a, I> NestedMetaIteratorExt<'a> for I where I: std::iter::IntoIterator, { fn into_multi_map_and_lits( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, Meta>, Vec<(usize, Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut lits = Vec::new(); for (i, nmeta) in self.into_iter().enumerate() { match nmeta { NestedMeta::Meta(meta) => metas.push((i, meta)), NestedMeta::Lit(lit) => lits.push((i, lit)), } } let map = metas.into_iter().to_multi_map(path_to_key)?; Ok((map, lits)) } fn into_unique_map_and_lits( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, Meta>, Vec<(usize, Lit)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut lits = Vec::new(); for (i, nmeta) in self.into_iter().enumerate() { match nmeta { NestedMeta::Meta(meta) => metas.push((i, meta)), NestedMeta::Lit(lit) => lits.push((i, lit)), } } let map = metas.into_iter().to_unique_map(path_to_key)?; Ok((map, lits)) } } /// experimental #[allow(clippy::type_complexity)] pub trait MetaAttributeExt<'a> { fn to_multi_map_and_attrs( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, Meta>, Vec<(usize, &'a Attribute)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; fn to_unique_map_and_attrs( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, Meta>, Vec<(usize, &'a Attribute)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>; } #[cfg(any(feature = "parsing"))] #[allow(clippy::type_complexity)] impl<'a, I> MetaAttributeExt<'a> for I where I: std::iter::IntoIterator, { // easier KF with traits? // KF: Err(_) for error, Ok(None) to skip, Ok(Some(_)) to push // fn to_map_and_attrs( // &'a self, // path_to_key: KF, // mata_iter_to_map: MF, // ) -> Result<(MT, Vec<(usize, &'a Attribute)>)> // where // K: std::hash::Hash + Eq, // KF: Fn(&Path) -> Result>, // MF: Fn(MI, KF) -> Result, // MI: std::iter::Iterator>, // { // let mut metas = Vec::new(); // let mut attrs = Vec::new(); // for (i, attr) in self.iter().enumerate() { // match attr.parse_meta() { // Ok(meta) => metas.push((i, meta)), // Err(_) => attrs.push((i, attr)), // } // } // let map = mata_iter_to_map(metas.into_iter(), path_to_key)?; // Ok((map, attrs)) // } fn to_multi_map_and_attrs( self, path_to_key: KF, ) -> Result<(MultiMetaMap<'a, K, Meta>, Vec<(usize, &'a Attribute)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut attrs = Vec::new(); for (i, attr) in self.into_iter().enumerate() { match attr.parse_meta() { Ok(meta) => metas.push((i, meta)), Err(_) => attrs.push((i, attr)), } } let map = metas.into_iter().to_multi_map(path_to_key)?; Ok((map, attrs)) } fn to_unique_map_and_attrs( self, path_to_key: KF, ) -> Result<(UniqueMetaMap<'a, K, Meta>, Vec<(usize, &'a Attribute)>)> where K: std::hash::Hash + Eq, KF: Fn(&Path) -> Result>, { let mut metas = Vec::new(); let mut attrs = Vec::new(); for (i, attr) in self.into_iter().enumerate() { match attr.parse_meta() { Ok(meta) => metas.push((i, meta)), Err(_) => attrs.push((i, attr)), } } let map = metas.into_iter().to_unique_map(path_to_key)?; Ok((map, attrs)) } } impl GetPath for NestedMeta { /// Get path if [syn::NestedMeta::Meta]; Otherwise `None` fn get_path(&self) -> Option<&Path> { match self { NestedMeta::Meta(meta) => Some(meta.path()), NestedMeta::Lit(_) => None, } } } impl GetIdent for Meta { /// Get ident of [syn::Meta::path] fn get_ident(&self) -> Option<&Ident> { self.path().get_ident() } } thread_local! { static EMPTY_META_NESTED: Punctuated = Punctuated::new(); } impl SharedEmpty for Punctuated { fn empty_ref() -> &'static Self { unsafe { thread_local_ref(&EMPTY_META_NESTED) } } } syn-ext-0.4.0/src/path.rs000064400000000000000000000017560072674642500133660ustar 00000000000000use crate::ident::GetIdent; use crate::shared::{thread_local_ref, SharedEmpty}; use syn::{punctuated::Punctuated, Ident, Path}; /// Shortcut to get [syn::Path] from various types pub trait GetPath { /// Returns [syn::Path] from object if possible fn get_path(&self) -> Option<&Path>; } impl GetIdent for T where T: GetPath, { /// Any [crate::ext::GetPath] automatically implements [crate::ext::GetIdent] fn get_ident(&self) -> Option<&Ident> { self.get_path().map(|p| p.get_ident()).flatten() } } thread_local! { static EMPTY_PATH: Path = Path::with_empty(); } impl SharedEmpty for Path { fn empty_ref() -> &'static Self { unsafe { thread_local_ref(&EMPTY_PATH) } } } pub trait PathExt { /// Constructs and returns an empty path with empty segments fn with_empty() -> Path; } impl PathExt for Path { fn with_empty() -> Path { Path { leading_colon: None, segments: Punctuated::new(), } } } syn-ext-0.4.0/src/punctuated.rs000064400000000000000000000023370072674642500146020ustar 00000000000000use syn::punctuated::{Pair, Punctuated}; /// Extension for [syn::punctuated::Punctuated] pub trait PunctuatedExt { /// Removes and returns the element at position index, popping all elements after it and push them back. fn remove(&mut self, index: usize) -> Pair where P: Default; } impl PunctuatedExt for Punctuated { fn remove(&mut self, index: usize) -> Pair where P: Default, { let mut stack = Vec::new(); for _ in index + 1..self.len() { stack.push(self.pop().unwrap().into_value()); } let removed = self.pop().unwrap(); while let Some(item) = stack.pop() { self.push(item) } removed } } #[cfg(test)] mod test { use super::*; use crate::assert_quote_eq; use syn::parse_quote; #[test] fn test_remove() { let mut list: syn::MetaList = parse_quote!(meta(a, b, c, d)); list.nested.remove(2); let expected: syn::MetaList = parse_quote!(meta(a, b, d)); assert_quote_eq!(list, expected); list.nested.remove(0); let expected: syn::MetaList = parse_quote!(meta(b, d)); assert_quote_eq!(list, expected); } } syn-ext-0.4.0/src/shared.rs000064400000000000000000000005350072674642500136720ustar 00000000000000pub trait SharedEmpty { fn empty_ref() -> &'static Self; } /// # Safety /// The value must not be mutable at any point of its lifecycle. /// The local key must not be destroyed. pub(crate) unsafe fn thread_local_ref(local: &'static std::thread::LocalKey) -> &'static T { let ptr = local.with(|nested| nested as *const _); &*ptr } syn-ext-0.4.0/src/test.rs000064400000000000000000000003540072674642500134020ustar 00000000000000#[macro_export] macro_rules! assert_quote_eq { ($q1:expr, $q2:expr) => {{ use quote::quote; let v1 = &$q1; let v2 = &$q2; assert_eq!(quote! { #v1 }.to_string(), quote! { #v2 }.to_string()); }}; }