aboutsummaryrefslogtreecommitdiff
path: root/src/func.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/func.rs')
-rw-r--r--src/func.rs297
1 files changed, 217 insertions, 80 deletions
diff --git a/src/func.rs b/src/func.rs
index 384e80b..477841b 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -1,10 +1,14 @@
+// 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,
- Variadic, Visibility,
+ Type, Visibility,
};
-use super::PatType;
+use super::{Pat, PatType};
ast_struct! {
/// A free-standing function: `fn process(n: usize) -> Result<()> { ...
@@ -64,6 +68,18 @@ ast_struct! {
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![,]>,
}
}
@@ -71,10 +87,13 @@ mod parsing {
use syn::{
braced, parenthesized,
parse::{discouraged::Speculative, Parse, ParseStream, Result},
- parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility,
+ punctuated::Punctuated,
+ Abi, Attribute, Error, Generics, Ident, Lifetime, Path, ReturnType, Token, Type, TypePath,
+ TypeReference, Visibility,
};
- use super::{Block, FnArg, ItemFn, PatType, Receiver, Signature};
+ use super::{Block, FnArg, ItemFn, Receiver, Signature, Variadic};
+ use crate::pat::{Pat, PatType, PatWild};
impl Parse for Block {
fn parse(input: ParseStream<'_>) -> Result<Self> {
@@ -85,18 +104,6 @@ mod parsing {
impl Parse for Signature {
fn parse(input: ParseStream<'_>) -> Result<Self> {
- #[allow(clippy::trivially_copy_pass_by_ref)]
- fn get_variadic(input: &&FnArg) -> Option<Variadic> {
- if let FnArg::Typed(PatType { ty, .. }) = input {
- if let Type::Verbatim(tokens) = &**ty {
- if let Ok(dots) = parse2(tokens.clone()) {
- return Some(Variadic { attrs: Vec::new(), pat: None, comma: None, dots });
- }
- }
- }
- None
- }
-
let constness: Option<Token![const]> = input.parse()?;
let asyncness: Option<Token![async]> = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
@@ -107,8 +114,7 @@ mod parsing {
let content;
let paren_token = parenthesized!(content in input);
- let inputs = content.parse_terminated(FnArg::parse, Token![,])?;
- let variadic = inputs.last().as_ref().and_then(get_variadic);
+ let (inputs, variadic) = parse_fn_args(&content)?;
let output: ReturnType = input.parse()?;
generics.where_clause = input.parse()?;
@@ -133,7 +139,7 @@ mod parsing {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
- let sig = input.parse()?;
+ let sig: Signature = input.parse()?;
let block = input.parse()?;
Ok(Self { attrs, vis, sig, block: Box::new(block) })
}
@@ -141,56 +147,185 @@ mod parsing {
impl Parse for FnArg {
fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let allow_variadic = false;
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));
- }
+ match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? {
+ FnArgOrVariadic::FnArg(arg) => Ok(arg),
+ FnArgOrVariadic::Variadic(_) => unreachable!(),
}
+ }
+ }
+
+ enum FnArgOrVariadic {
+ FnArg(FnArg),
+ Variadic(Variadic),
+ }
- let mut typed = input.call(fn_arg_typed)?;
- typed.attrs = attrs;
- Ok(FnArg::Typed(typed))
+ 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: {
- if input.peek(Token![&]) {
- Some((input.parse()?, input.parse()?))
- } else {
- None
- }
- },
- mutability: input.parse()?,
- self_token: input.parse()?,
+ reference,
+ mutability,
+ self_token,
+ colon_token,
+ ty: Box::new(ty),
})
}
}
- 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()?),
- })
+ 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::{punctuated::Punctuated, Token, Type};
+ use syn::{Token, Type};
- use super::{Block, FnArg, ItemFn, Receiver, Signature};
+ 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) {
@@ -200,25 +335,6 @@ mod printing {
}
}
- fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
- let last = match inputs.last() {
- Some(last) => last,
- None => return false,
- };
-
- let pat = match last {
- FnArg::Typed(pat) => pat,
- FnArg::Receiver(_) => return false,
- };
-
- let tokens = match &*pat.ty {
- Type::Verbatim(tokens) => tokens,
- _ => return false,
- };
-
- tokens.to_string() == "..."
- }
-
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.constness.to_tokens(tokens);
@@ -230,11 +346,11 @@ mod printing {
self.generics.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
self.inputs.to_tokens(tokens);
- if self.variadic.is_some() && !has_variadic(&self.inputs) {
+ if let Some(variadic) = &self.variadic {
if !self.inputs.empty_or_trailing() {
<Token![,]>::default().to_tokens(tokens);
}
- self.variadic.to_tokens(tokens);
+ variadic.to_tokens(tokens);
}
});
self.output.to_tokens(tokens);
@@ -242,17 +358,6 @@ mod printing {
}
}
- 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.brace_token.surround(tokens, |tokens| {
- tokens.append_all(self.block.stmts.clone());
- });
- }
- }
-
impl ToTokens for Receiver {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(&self.attrs);
@@ -262,6 +367,38 @@ mod printing {
}
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);
}
}
}