aboutsummaryrefslogtreecommitdiff
path: root/syn-mid/src
diff options
context:
space:
mode:
Diffstat (limited to 'syn-mid/src')
-rw-r--r--syn-mid/src/arg.rs99
-rw-r--r--syn-mid/src/lib.rs190
-rw-r--r--syn-mid/src/macros.rs107
-rw-r--r--syn-mid/src/pat.rs413
-rw-r--r--syn-mid/src/path.rs50
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
+ },
+ })
+}