// SPDX-License-Identifier: Apache-2.0 OR MIT // Based on https://github.com/dtolnay/syn/blob/2.0.37/src/item.rs. use proc_macro2::TokenStream; use syn::{ punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, Lifetime, ReturnType, Token, Type, Visibility, }; use super::{Pat, PatType}; ast_struct! { /// A free-standing function: `fn process(n: usize) -> Result<()> { ... /// }`. pub struct ItemFn { pub attrs: Vec, pub vis: Visibility, pub sig: Signature, pub block: Box, } } 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 function signature in a trait or implementation: `unsafe fn /// initialize(&self)`. pub struct Signature { pub constness: Option, pub asyncness: Option, pub unsafety: Option, pub abi: Option, pub fn_token: Token![fn], pub ident: Ident, pub generics: Generics, pub paren_token: token::Paren, pub inputs: Punctuated, pub variadic: Option, pub output: ReturnType, } } 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, pub reference: Option<(Token![&], Option)>, pub mutability: Option, pub self_token: Token![self], pub colon_token: Option, pub ty: Box, } } ast_struct! { /// The variadic argument of a foreign function. pub struct Variadic { pub attrs: Vec, pub pat: Option<(Box, Token![:])>, pub dots: Token![...], pub comma: Option, } } mod parsing { use syn::{ braced, parenthesized, parse::{discouraged::Speculative, Parse, ParseStream, Result}, punctuated::Punctuated, Abi, Attribute, Error, Generics, Ident, Lifetime, Path, ReturnType, Token, Type, TypePath, TypeReference, Visibility, }; use super::{Block, FnArg, ItemFn, Receiver, Signature, Variadic}; use crate::pat::{Pat, PatType, PatWild}; impl Parse for Block { fn parse(input: ParseStream<'_>) -> Result { let content; Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? }) } } impl Parse for Signature { fn parse(input: ParseStream<'_>) -> Result { let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; let unsafety: Option = input.parse()?; let abi: Option = input.parse()?; let fn_token: Token![fn] = input.parse()?; let ident: Ident = input.parse()?; let mut generics: Generics = input.parse()?; let content; let paren_token = parenthesized!(content in input); let (inputs, variadic) = parse_fn_args(&content)?; let output: ReturnType = input.parse()?; generics.where_clause = input.parse()?; Ok(Self { constness, asyncness, unsafety, abi, fn_token, ident, generics, paren_token, inputs, variadic, output, }) } } impl Parse for ItemFn { fn parse(input: ParseStream<'_>) -> Result { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let sig: Signature = input.parse()?; let block = input.parse()?; Ok(Self { attrs, vis, sig, block: Box::new(block) }) } } impl Parse for FnArg { fn parse(input: ParseStream<'_>) -> Result { let allow_variadic = false; let attrs = input.call(Attribute::parse_outer)?; match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? { FnArgOrVariadic::FnArg(arg) => Ok(arg), FnArgOrVariadic::Variadic(_) => unreachable!(), } } } enum FnArgOrVariadic { FnArg(FnArg), Variadic(Variadic), } fn parse_fn_arg_or_variadic( input: ParseStream<'_>, attrs: Vec, allow_variadic: bool, ) -> Result { let ahead = input.fork(); if let Ok(mut receiver) = ahead.parse::() { input.advance_to(&ahead); receiver.attrs = attrs; return Ok(FnArgOrVariadic::FnArg(FnArg::Receiver(receiver))); } // Hack to parse pre-2018 syntax in // test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs // because the rest of the test case is valuable. if input.peek(Ident) && input.peek2(Token![<]) { let span = input.fork().parse::()?.span(); return Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType { attrs, pat: Box::new(Pat::Wild(PatWild { attrs: Vec::new(), underscore_token: Token![_](span), })), colon_token: Token![:](span), ty: input.parse()?, }))); } let pat = Box::new(Pat::parse_single(input)?); let colon_token: Token![:] = input.parse()?; if allow_variadic { if let Some(dots) = input.parse::>()? { return Ok(FnArgOrVariadic::Variadic(Variadic { attrs, pat: Some((pat, colon_token)), dots, comma: None, })); } } Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType { attrs, pat, colon_token, ty: input.parse()?, }))) } impl Parse for Receiver { fn parse(input: ParseStream<'_>) -> Result { let reference = if input.peek(Token![&]) { let ampersand: Token![&] = input.parse()?; let lifetime: Option = input.parse()?; Some((ampersand, lifetime)) } else { None }; let mutability: Option = input.parse()?; let self_token: Token![self] = input.parse()?; let colon_token: Option = if reference.is_some() { None } else { input.parse()? }; let ty: Type = if colon_token.is_some() { input.parse()? } else { let mut ty = Type::Path(TypePath { qself: None, path: Path::from(Ident::new("Self", self_token.span)), }); if let Some((ampersand, lifetime)) = reference.as_ref() { ty = Type::Reference(TypeReference { and_token: Token![&](ampersand.span), lifetime: lifetime.clone(), mutability: mutability.as_ref().map(|m| Token![mut](m.span)), elem: Box::new(ty), }); } ty }; Ok(Self { attrs: Vec::new(), reference, mutability, self_token, colon_token, ty: Box::new(ty), }) } } fn parse_fn_args( input: ParseStream<'_>, ) -> Result<(Punctuated, Option)> { let mut args = Punctuated::new(); let mut variadic = None; let mut has_receiver = false; while !input.is_empty() { let attrs = input.call(Attribute::parse_outer)?; if let Some(dots) = input.parse::>()? { variadic = Some(Variadic { attrs, pat: None, dots, comma: if input.is_empty() { None } else { Some(input.parse()?) }, }); break; } let allow_variadic = true; let arg = match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? { FnArgOrVariadic::FnArg(arg) => arg, FnArgOrVariadic::Variadic(arg) => { variadic = Some(Variadic { comma: if input.is_empty() { None } else { Some(input.parse()?) }, ..arg }); break; } }; match &arg { FnArg::Receiver(receiver) if has_receiver => { return Err(Error::new( receiver.self_token.span, "unexpected second method receiver", )); } FnArg::Receiver(receiver) if !args.is_empty() => { return Err(Error::new(receiver.self_token.span, "unexpected method receiver")); } FnArg::Receiver(_) => has_receiver = true, FnArg::Typed(_) => {} } args.push_value(arg); if input.is_empty() { break; } let comma: Token![,] = input.parse()?; args.push_punct(comma); } Ok((args, variadic)) } } mod printing { use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; use syn::{Token, Type}; use super::{Block, ItemFn, Receiver, Signature, Variadic}; impl ToTokens for ItemFn { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); self.vis.to_tokens(tokens); self.sig.to_tokens(tokens); self.block.to_tokens(tokens); } } 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 Signature { fn to_tokens(&self, tokens: &mut TokenStream) { 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); if let Some(variadic) = &self.variadic { if !self.inputs.empty_or_trailing() { ::default().to_tokens(tokens); } variadic.to_tokens(tokens); } }); self.output.to_tokens(tokens); self.generics.where_clause.to_tokens(tokens); } } 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); if let Some(colon_token) = &self.colon_token { colon_token.to_tokens(tokens); self.ty.to_tokens(tokens); } else { let consistent = match (&self.reference, &self.mutability, &*self.ty) { (Some(_), mutability, Type::Reference(ty)) => { mutability.is_some() == ty.mutability.is_some() && match &*ty.elem { Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"), _ => false, } } (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"), _ => false, }; if !consistent { ::default().to_tokens(tokens); self.ty.to_tokens(tokens); } } } } impl ToTokens for Variadic { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); if let Some((pat, colon)) = &self.pat { pat.to_tokens(tokens); colon.to_tokens(tokens); } self.dots.to_tokens(tokens); self.comma.to_tokens(tokens); } } }