diff options
Diffstat (limited to 'syn-mid/src')
| -rw-r--r-- | syn-mid/src/arg.rs | 99 | ||||
| -rw-r--r-- | syn-mid/src/lib.rs | 190 | ||||
| -rw-r--r-- | syn-mid/src/macros.rs | 107 | ||||
| -rw-r--r-- | syn-mid/src/pat.rs | 413 | ||||
| -rw-r--r-- | syn-mid/src/path.rs | 50 | 
5 files changed, 859 insertions, 0 deletions
diff --git a/syn-mid/src/arg.rs b/syn-mid/src/arg.rs new file mode 100644 index 0000000..593a1ac --- /dev/null +++ b/syn-mid/src/arg.rs @@ -0,0 +1,99 @@ +use syn::{Attribute, Lifetime, Token}; + +use super::PatType; + +ast_enum_of_structs! { +    /// An argument in a function signature: the `n: usize` in `fn f(n: usize)`. +    pub enum FnArg { +        /// The `self` argument of an associated method, whether taken by value +        /// or by reference. +        Receiver(Receiver), + +        /// A function argument accepted by pattern and type. +        Typed(PatType), +    } +} + +ast_struct! { +    /// The `self` argument of an associated method, whether taken by value +    /// or by reference. +    pub struct Receiver { +        pub attrs: Vec<Attribute>, +        pub reference: Option<(Token![&], Option<Lifetime>)>, +        pub mutability: Option<Token![mut]>, +        pub self_token: Token![self], +    } +} + +mod parsing { +    use syn::{ +        parse::{discouraged::Speculative, Parse, ParseStream, Result}, +        Attribute, Token, +    }; + +    use super::{FnArg, PatType, Receiver}; + +    impl Parse for FnArg { +        fn parse(input: ParseStream<'_>) -> Result<Self> { +            let attrs = input.call(Attribute::parse_outer)?; + +            let ahead = input.fork(); +            if let Ok(mut receiver) = ahead.parse::<Receiver>() { +                if !ahead.peek(Token![:]) { +                    input.advance_to(&ahead); +                    receiver.attrs = attrs; +                    return Ok(FnArg::Receiver(receiver)); +                } +            } + +            let mut typed = input.call(fn_arg_typed)?; +            typed.attrs = attrs; +            Ok(FnArg::Typed(typed)) +        } +    } + +    impl Parse for Receiver { +        fn parse(input: ParseStream<'_>) -> Result<Self> { +            Ok(Self { +                attrs: Vec::new(), +                reference: { +                    if input.peek(Token![&]) { +                        Some((input.parse()?, input.parse()?)) +                    } else { +                        None +                    } +                }, +                mutability: input.parse()?, +                self_token: input.parse()?, +            }) +        } +    } + +    fn fn_arg_typed(input: ParseStream<'_>) -> Result<PatType> { +        Ok(PatType { +            attrs: Vec::new(), +            pat: input.parse()?, +            colon_token: input.parse()?, +            ty: Box::new(input.parse()?), +        }) +    } +} + +mod printing { +    use proc_macro2::TokenStream; +    use quote::{ToTokens, TokenStreamExt}; + +    use super::Receiver; + +    impl ToTokens for Receiver { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            tokens.append_all(&self.attrs); +            if let Some((ampersand, lifetime)) = &self.reference { +                ampersand.to_tokens(tokens); +                lifetime.to_tokens(tokens); +            } +            self.mutability.to_tokens(tokens); +            self.self_token.to_tokens(tokens); +        } +    } +} diff --git a/syn-mid/src/lib.rs b/syn-mid/src/lib.rs new file mode 100644 index 0000000..69bdec9 --- /dev/null +++ b/syn-mid/src/lib.rs @@ -0,0 +1,190 @@ +//! Providing the features between "full" and "derive" of syn. +//! +//! This crate provides the following two unique data structures. +//! +//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed. +//! +//!   ```text +//!   fn process(n: usize) -> Result<()> { ... } +//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^ +//!   ``` +//! +//! * [`syn_mid::Block`] -- A block whose body is not parsed. +//! +//!   ```text +//!   { ... } +//!   ^     ^ +//!   ``` +//! +//! Other data structures are the same as data structures of [syn]. These are defined in this crate +//! because they cannot be used in [syn] without "full" feature. +//! +//! ## Optional features +//! +//! syn-mid in the default features aims to provide the features between "full" +//! and "derive" of [syn]. +//! +//! * **`clone-impls`** — Clone impls for all syntax tree types. +//! +//! [`syn_mid::ItemFn`]: struct.ItemFn.html +//! [`syn_mid::Block`]: struct.Block.html +//! [syn]: https://github.com/dtolnay/syn +//! + +#![doc(html_root_url = "https://docs.rs/syn-mid/0.4.0")] +#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms, unreachable_pub)] +#![warn(single_use_lifetimes)] +#![warn(clippy::all, clippy::pedantic)] +#![allow( +    clippy::eval_order_dependence, +    clippy::large_enum_variant, +    clippy::module_name_repetitions, +    clippy::use_self +)] + +// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn. + +#[macro_use] +mod macros; + +mod arg; +mod pat; +mod path; + +pub use self::arg::*; +pub use self::pat::*; + +use proc_macro2::TokenStream; +use syn::{ +    punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, Visibility, +}; + +ast_struct! { +    /// A braced block containing Rust statements. +    pub struct Block { +        pub brace_token: token::Brace, +        /// Statements in a block +        pub stmts: TokenStream, +    } +} + +ast_struct! { +    /// A free-standing function: `fn process(n: usize) -> Result<()> { ... +    /// }`. +    pub struct ItemFn { +        pub attrs: Vec<Attribute>, +        pub vis: Visibility, +        pub constness: Option<Token![const]>, +        pub asyncness: Option<Token![async]>, +        pub unsafety: Option<Token![unsafe]>, +        pub abi: Option<Abi>, +        pub fn_token: Token![fn], +        pub ident: Ident, +        pub generics: Generics, +        pub paren_token: token::Paren, +        pub inputs: Punctuated<FnArg, Token![,]>, +        pub output: ReturnType, +        pub block: Block, +    } +} + +mod parsing { +    use syn::{ +        braced, parenthesized, +        parse::{Parse, ParseStream, Result}, +        Abi, Attribute, Generics, Ident, ReturnType, Token, Visibility, WhereClause, +    }; + +    use super::{Block, FnArg, ItemFn}; + +    impl Parse for Block { +        fn parse(input: ParseStream<'_>) -> Result<Self> { +            let content; +            Ok(Self { +                brace_token: braced!(content in input), +                stmts: content.parse()?, +            }) +        } +    } + +    impl Parse for ItemFn { +        fn parse(input: ParseStream<'_>) -> Result<Self> { +            let attrs = input.call(Attribute::parse_outer)?; +            let vis: Visibility = input.parse()?; +            let constness: Option<Token![const]> = input.parse()?; +            let asyncness: Option<Token![async]> = input.parse()?; +            let unsafety: Option<Token![unsafe]> = input.parse()?; +            let abi: Option<Abi> = input.parse()?; +            let fn_token: Token![fn] = input.parse()?; +            let ident: Ident = input.parse()?; +            let generics: Generics = input.parse()?; + +            let content; +            let paren_token = parenthesized!(content in input); +            let inputs = content.parse_terminated(FnArg::parse)?; + +            let output: ReturnType = input.parse()?; +            let where_clause: Option<WhereClause> = input.parse()?; + +            let block = input.parse()?; + +            Ok(Self { +                attrs, +                vis, +                constness, +                asyncness, +                unsafety, +                abi, +                fn_token, +                ident, +                generics: Generics { +                    where_clause, +                    ..generics +                }, +                paren_token, +                inputs, +                output, +                block, +            }) +        } +    } +} + +mod printing { +    use proc_macro2::TokenStream; +    use quote::{ToTokens, TokenStreamExt}; + +    use super::{Block, ItemFn}; + +    impl ToTokens for Block { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.brace_token.surround(tokens, |tokens| { +                tokens.append_all(self.stmts.clone()); +            }); +        } +    } + +    impl ToTokens for ItemFn { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            tokens.append_all(&self.attrs); +            self.vis.to_tokens(tokens); +            self.constness.to_tokens(tokens); +            self.asyncness.to_tokens(tokens); +            self.unsafety.to_tokens(tokens); +            self.abi.to_tokens(tokens); +            self.fn_token.to_tokens(tokens); +            self.ident.to_tokens(tokens); +            self.generics.to_tokens(tokens); +            self.paren_token.surround(tokens, |tokens| { +                self.inputs.to_tokens(tokens); +            }); +            self.output.to_tokens(tokens); +            self.generics.where_clause.to_tokens(tokens); +            self.block.brace_token.surround(tokens, |tokens| { +                tokens.append_all(self.block.stmts.clone()); +            }); +        } +    } +} diff --git a/syn-mid/src/macros.rs b/syn-mid/src/macros.rs new file mode 100644 index 0000000..87be7b4 --- /dev/null +++ b/syn-mid/src/macros.rs @@ -0,0 +1,107 @@ +macro_rules! ast_struct { +    ( +        [$($attrs_pub:tt)*] +        struct $name:ident $($rest:tt)* +    ) => { +        #[cfg_attr(feature = "clone-impls", derive(Clone))] +        $($attrs_pub)* struct $name $($rest)* +    }; + +    ($($t:tt)*) => { +        strip_attrs_pub!(ast_struct!($($t)*)); +    }; +} + +macro_rules! ast_enum { +    ( +        [$($attrs_pub:tt)*] +        enum $name:ident $($rest:tt)* +    ) => ( +        #[cfg_attr(feature = "clone-impls", derive(Clone))] +        $($attrs_pub)* enum $name $($rest)* +    ); + +    ($($t:tt)*) => { +        strip_attrs_pub!(ast_enum!($($t)*)); +    }; +} + +macro_rules! ast_enum_of_structs { +    ( +        $(#[$enum_attr:meta])* +        $pub:ident $enum:ident $name:ident $body:tt +    ) => { +        ast_enum!($(#[$enum_attr])* $pub $enum $name $body); +        ast_enum_of_structs_impl!($pub $enum $name $body); +    }; +} + +macro_rules! ast_enum_of_structs_impl { +    ( +        $pub:ident $enum:ident $name:ident { +            $( +                $(#[$variant_attr:meta])* +                $variant:ident $( ($member:ident) )*, +            )* +        } +    ) => { +        check_keyword_matches!(pub $pub); +        check_keyword_matches!(enum $enum); + +        $( +            $( +                impl From<$member> for $name { +                    fn from(e: $member) -> $name { +                        $name::$variant(e) +                    } +                } +            )* +        )* + +        generate_to_tokens! { +            () +            tokens +            $name { $($variant $($member)*,)* } +        } +    }; +} + +macro_rules! generate_to_tokens { +    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident, $($next:tt)*}) => { +        generate_to_tokens!( +            ($($arms)* $name::$variant => {}) +            $tokens $name { $($next)* } +        ); +    }; + +    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident $member:ident, $($next:tt)*}) => { +        generate_to_tokens!( +            ($($arms)* $name::$variant(_e) => quote::ToTokens::to_tokens(_e, $tokens),) +            $tokens $name { $($next)* } +        ); +    }; + +    (($($arms:tt)*) $tokens:ident $name:ident {}) => { +        impl quote::ToTokens for $name { +            fn to_tokens(&self, $tokens: &mut proc_macro2::TokenStream) { +                match self { +                    $($arms)* +                } +            } +        } +    }; +} + +macro_rules! strip_attrs_pub { +    ($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => { +        check_keyword_matches!(pub $pub); + +        $mac!([$(#[$m])* $pub] $($t)*); +    }; +} + +macro_rules! check_keyword_matches { +    (struct struct) => {}; +    (enum enum) => {}; +    (pub pub) => {}; +} diff --git a/syn-mid/src/pat.rs b/syn-mid/src/pat.rs new file mode 100644 index 0000000..8f95381 --- /dev/null +++ b/syn-mid/src/pat.rs @@ -0,0 +1,413 @@ +use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type}; + +ast_enum_of_structs! { +    /// A pattern in a local binding, function signature, match expression, or +    /// various other places. +    /// +    /// # Syntax tree enum +    /// +    /// This type is a [syntax tree enum]. +    /// +    /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums +    pub enum Pat { +        /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. +        Ident(PatIdent), + +        /// A path pattern like `Color::Red`. +        Path(PatPath), + +        /// A reference pattern: `&mut var`. +        Reference(PatReference), + +        /// A struct or struct variant pattern: `Variant { x, y, .. }`. +        Struct(PatStruct), + +        /// A tuple pattern: `(a, b)`. +        Tuple(PatTuple), + +        /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. +        TupleStruct(PatTupleStruct), + +        /// A type ascription pattern: `foo: f64`. +        Type(PatType), + +        /// A pattern that matches any value: `_`. +        Wild(PatWild), + +        #[doc(hidden)] +        __Nonexhaustive, +    } +} + +ast_struct! { +    /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. +    pub struct PatIdent { +        pub attrs: Vec<Attribute>, +        pub by_ref: Option<Token![ref]>, +        pub mutability: Option<Token![mut]>, +        pub ident: Ident, +    } +} + +ast_struct! { +    /// A path pattern like `Color::Red`. +    pub struct PatPath { +        pub attrs: Vec<Attribute>, +        pub path: Path, +    } +} + +ast_struct! { +    /// A reference pattern: `&mut var`. +    pub struct PatReference { +        pub attrs: Vec<Attribute>, +        pub and_token: Token![&], +        pub mutability: Option<Token![mut]>, +        pub pat: Box<Pat>, +    } +} + +ast_struct! { +    /// A struct or struct variant pattern: `Variant { x, y, .. }`. +    pub struct PatStruct { +        pub attrs: Vec<Attribute>, +        pub path: Path, +        pub brace_token: token::Brace, +        pub fields: Punctuated<FieldPat, Token![,]>, +        pub dot2_token: Option<Token![..]>, +    } +} + +ast_struct! { +    /// A tuple pattern: `(a, b)`. +    pub struct PatTuple { +        pub attrs: Vec<Attribute>, +        pub paren_token: token::Paren, +        pub elems: Punctuated<Pat, Token![,]>, +    } +} + +ast_struct! { +    /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. +    pub struct PatTupleStruct { +        pub attrs: Vec<Attribute>, +        pub path: Path, +        pub pat: PatTuple, +    } +} + +ast_struct! { +    /// A type ascription pattern: `foo: f64`. +    pub struct PatType { +        pub attrs: Vec<Attribute>, +        pub pat: Box<Pat>, +        pub colon_token: Token![:], +        pub ty: Box<Type>, +    } +} + +ast_struct! { +    /// A pattern that matches any value: `_`. +    pub struct PatWild { +        pub attrs: Vec<Attribute>, +        pub underscore_token: Token![_], +    } +} + +ast_struct! { +    /// A single field in a struct pattern. +    /// +    /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated +    /// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token. +    pub struct FieldPat { +        pub attrs: Vec<Attribute>, +        pub member: Member, +        pub colon_token: Option<Token![:]>, +        pub pat: Box<Pat>, +    } +} + +mod parsing { +    use syn::{ +        braced, +        ext::IdentExt, +        parenthesized, +        parse::{Parse, ParseStream, Result}, +        punctuated::Punctuated, +        token, Ident, Member, Path, Token, +    }; + +    use crate::path; + +    use super::{ +        FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, +        PatWild, +    }; + +    impl Parse for Pat { +        fn parse(input: ParseStream<'_>) -> Result<Self> { +            let lookahead = input.lookahead1(); +            if lookahead.peek(Ident) +                && ({ +                    input.peek2(Token![::]) +                        || input.peek2(token::Brace) +                        || input.peek2(token::Paren) +                }) +                || input.peek(Token![self]) && input.peek2(Token![::]) +                || lookahead.peek(Token![::]) +                || lookahead.peek(Token![<]) +                || input.peek(Token![Self]) +                || input.peek(Token![super]) +                || input.peek(Token![extern]) +                || input.peek(Token![crate]) +            { +                pat_path_or_struct(input) +            } else if lookahead.peek(Token![_]) { +                input.call(pat_wild).map(Pat::Wild) +            } else if lookahead.peek(Token![ref]) +                || lookahead.peek(Token![mut]) +                || input.peek(Token![self]) +                || input.peek(Ident) +            { +                input.call(pat_ident).map(Pat::Ident) +            } else if lookahead.peek(Token![&]) { +                input.call(pat_reference).map(Pat::Reference) +            } else if lookahead.peek(token::Paren) { +                input.call(pat_tuple).map(Pat::Tuple) +            } else { +                Err(lookahead.error()) +            } +        } +    } + +    fn pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat> { +        let path = path::parse_path(input)?; + +        if input.peek(token::Brace) { +            pat_struct(input, path).map(Pat::Struct) +        } else if input.peek(token::Paren) { +            pat_tuple_struct(input, path).map(Pat::TupleStruct) +        } else { +            Ok(Pat::Path(PatPath { +                attrs: Vec::new(), +                path, +            })) +        } +    } + +    fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> { +        Ok(PatWild { +            attrs: Vec::new(), +            underscore_token: input.parse()?, +        }) +    } + +    fn pat_ident(input: ParseStream<'_>) -> Result<PatIdent> { +        Ok(PatIdent { +            attrs: Vec::new(), +            by_ref: input.parse()?, +            mutability: input.parse()?, +            ident: input.call(Ident::parse_any)?, +        }) +    } + +    fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct> { +        Ok(PatTupleStruct { +            attrs: Vec::new(), +            path, +            pat: input.call(pat_tuple)?, +        }) +    } + +    fn pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct> { +        let content; +        let brace_token = braced!(content in input); + +        let mut fields = Punctuated::new(); +        while !content.is_empty() && !content.peek(Token![..]) { +            let value = content.call(field_pat)?; +            fields.push_value(value); +            if !content.peek(Token![,]) { +                break; +            } +            let punct: Token![,] = content.parse()?; +            fields.push_punct(punct); +        } + +        let dot2_token = if fields.empty_or_trailing() && content.peek(Token![..]) { +            Some(content.parse()?) +        } else { +            None +        }; + +        Ok(PatStruct { +            attrs: Vec::new(), +            path, +            brace_token, +            fields, +            dot2_token, +        }) +    } + +    fn field_pat(input: ParseStream<'_>) -> Result<FieldPat> { +        let boxed: Option<Token![box]> = input.parse()?; +        let by_ref: Option<Token![ref]> = input.parse()?; +        let mutability: Option<Token![mut]> = input.parse()?; +        let member: Member = input.parse()?; + +        if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) +            || is_unnamed(&member) +        { +            return Ok(FieldPat { +                attrs: Vec::new(), +                member, +                colon_token: input.parse()?, +                pat: input.parse()?, +            }); +        } + +        let ident = match member { +            Member::Named(ident) => ident, +            Member::Unnamed(_) => unreachable!(), +        }; + +        let pat = Pat::Ident(PatIdent { +            attrs: Vec::new(), +            by_ref, +            mutability, +            ident: ident.clone(), +        }); + +        Ok(FieldPat { +            member: Member::Named(ident), +            pat: Box::new(pat), +            attrs: Vec::new(), +            colon_token: None, +        }) +    } + +    fn pat_tuple(input: ParseStream<'_>) -> Result<PatTuple> { +        let content; +        let paren_token = parenthesized!(content in input); + +        let mut elems = Punctuated::new(); +        while !content.is_empty() { +            let value: Pat = content.parse()?; +            elems.push_value(value); +            if content.is_empty() { +                break; +            } +            let punct = content.parse()?; +            elems.push_punct(punct); +        } + +        Ok(PatTuple { +            attrs: Vec::new(), +            paren_token, +            elems, +        }) +    } + +    fn pat_reference(input: ParseStream<'_>) -> Result<PatReference> { +        Ok(PatReference { +            attrs: Vec::new(), +            and_token: input.parse()?, +            mutability: input.parse()?, +            pat: input.parse()?, +        }) +    } + +    fn is_unnamed(member: &Member) -> bool { +        match member { +            Member::Named(_) => false, +            Member::Unnamed(_) => true, +        } +    } +} + +mod printing { +    use proc_macro2::TokenStream; +    use quote::{ToTokens, TokenStreamExt}; +    use syn::Token; + +    use super::{ +        FieldPat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType, +        PatWild, +    }; + +    impl ToTokens for PatWild { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.underscore_token.to_tokens(tokens); +        } +    } + +    impl ToTokens for PatIdent { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.by_ref.to_tokens(tokens); +            self.mutability.to_tokens(tokens); +            self.ident.to_tokens(tokens); +        } +    } + +    impl ToTokens for PatStruct { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.path.to_tokens(tokens); +            self.brace_token.surround(tokens, |tokens| { +                self.fields.to_tokens(tokens); +                // NOTE: We need a comma before the dot2 token if it is present. +                if !self.fields.empty_or_trailing() && self.dot2_token.is_some() { +                    <Token![,]>::default().to_tokens(tokens); +                } +                self.dot2_token.to_tokens(tokens); +            }); +        } +    } + +    impl ToTokens for PatTupleStruct { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.path.to_tokens(tokens); +            self.pat.to_tokens(tokens); +        } +    } + +    impl ToTokens for PatType { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            tokens.append_all(&self.attrs); +            self.pat.to_tokens(tokens); +            self.colon_token.to_tokens(tokens); +            self.ty.to_tokens(tokens); +        } +    } + +    impl ToTokens for PatPath { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.path.to_tokens(tokens) +        } +    } + +    impl ToTokens for PatTuple { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.paren_token.surround(tokens, |tokens| { +                self.elems.to_tokens(tokens); +            }); +        } +    } + +    impl ToTokens for PatReference { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            self.and_token.to_tokens(tokens); +            self.mutability.to_tokens(tokens); +            self.pat.to_tokens(tokens); +        } +    } + +    impl ToTokens for FieldPat { +        fn to_tokens(&self, tokens: &mut TokenStream) { +            if let Some(colon_token) = &self.colon_token { +                self.member.to_tokens(tokens); +                colon_token.to_tokens(tokens); +            } +            self.pat.to_tokens(tokens); +        } +    } +} diff --git a/syn-mid/src/path.rs b/syn-mid/src/path.rs new file mode 100644 index 0000000..8093b53 --- /dev/null +++ b/syn-mid/src/path.rs @@ -0,0 +1,50 @@ +use syn::{ +    ext::IdentExt, +    parse::{ParseStream, Result}, +    punctuated::Punctuated, +    Ident, Path, PathArguments, PathSegment, Token, +}; + +fn parse_path_segment(input: ParseStream<'_>) -> Result<PathSegment> { +    if input.peek(Token![super]) +        || input.peek(Token![self]) +        || input.peek(Token![crate]) +        || input.peek(Token![extern]) +    { +        let ident = input.call(Ident::parse_any)?; +        return Ok(PathSegment::from(ident)); +    } + +    let ident = if input.peek(Token![Self]) { +        input.call(Ident::parse_any)? +    } else { +        input.parse()? +    }; + +    if input.peek(Token![::]) && input.peek3(Token![<]) { +        Ok(PathSegment { +            ident, +            arguments: PathArguments::AngleBracketed(input.parse()?), +        }) +    } else { +        Ok(PathSegment::from(ident)) +    } +} + +pub(crate) fn parse_path(input: ParseStream<'_>) -> Result<Path> { +    Ok(Path { +        leading_colon: input.parse()?, +        segments: { +            let mut segments = Punctuated::new(); +            let value = parse_path_segment(input)?; +            segments.push_value(value); +            while input.peek(Token![::]) { +                let punct: Token![::] = input.parse()?; +                segments.push_punct(punct); +                let value = parse_path_segment(input)?; +                segments.push_value(value); +            } +            segments +        }, +    }) +}  | 
