blob: 477841b77ee7f77bfd10ebb1968e9cf4467326bf [file] [log] [blame]
// 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<Attribute>,
pub vis: Visibility,
pub sig: Signature,
pub block: Box<Block>,
}
}
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<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 variadic: Option<Variadic>,
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<Attribute>,
pub reference: Option<(Token![&], Option<Lifetime>)>,
pub mutability: Option<Token![mut]>,
pub self_token: Token![self],
pub colon_token: Option<Token![:]>,
pub ty: Box<Type>,
}
}
ast_struct! {
/// The variadic argument of a foreign function.
pub struct Variadic {
pub attrs: Vec<Attribute>,
pub pat: Option<(Box<Pat>, Token![:])>,
pub dots: Token![...],
pub comma: Option<Token![,]>,
}
}
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<Self> {
let content;
Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
}
}
impl Parse for Signature {
fn parse(input: ParseStream<'_>) -> Result<Self> {
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 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<Self> {
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<Self> {
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<Attribute>,
allow_variadic: bool,
) -> Result<FnArgOrVariadic> {
let ahead = input.fork();
if let Ok(mut receiver) = ahead.parse::<Receiver>() {
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::<Ident>()?.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::<Option<Token![...]>>()? {
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<Self> {
let reference = if input.peek(Token![&]) {
let ampersand: Token![&] = input.parse()?;
let lifetime: Option<Lifetime> = input.parse()?;
Some((ampersand, lifetime))
} else {
None
};
let mutability: Option<Token![mut]> = input.parse()?;
let self_token: Token![self] = input.parse()?;
let colon_token: Option<Token![:]> =
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<FnArg, Token![,]>, Option<Variadic>)> {
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::<Option<Token![...]>>()? {
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() {
<Token![,]>::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 {
<Token![:]>::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);
}
}
}