syn_mid/
func.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3// Based on https://github.com/dtolnay/syn/blob/2.0.37/src/item.rs.
4
5use proc_macro2::TokenStream;
6use syn::{
7    punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, Lifetime, ReturnType, Token,
8    Type, Visibility,
9};
10
11use super::{Pat, PatType};
12
13ast_struct! {
14    /// A free-standing function: `fn process(n: usize) -> Result<()> { ...
15    /// }`.
16    pub struct ItemFn {
17        pub attrs: Vec<Attribute>,
18        pub vis: Visibility,
19        pub sig: Signature,
20        pub block: Box<Block>,
21    }
22}
23
24ast_struct! {
25    /// A braced block containing Rust statements.
26    pub struct Block {
27        pub brace_token: token::Brace,
28        /// Statements in a block
29        pub stmts: TokenStream,
30    }
31}
32
33ast_struct! {
34    /// A function signature in a trait or implementation: `unsafe fn
35    /// initialize(&self)`.
36    pub struct Signature {
37        pub constness: Option<Token![const]>,
38        pub asyncness: Option<Token![async]>,
39        pub unsafety: Option<Token![unsafe]>,
40        pub abi: Option<Abi>,
41        pub fn_token: Token![fn],
42        pub ident: Ident,
43        pub generics: Generics,
44        pub paren_token: token::Paren,
45        pub inputs: Punctuated<FnArg, Token![,]>,
46        pub variadic: Option<Variadic>,
47        pub output: ReturnType,
48    }
49}
50
51ast_enum_of_structs! {
52    /// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
53    pub enum FnArg {
54        /// The `self` argument of an associated method, whether taken by value
55        /// or by reference.
56        Receiver(Receiver),
57
58        /// A function argument accepted by pattern and type.
59        Typed(PatType),
60    }
61}
62
63ast_struct! {
64    /// The `self` argument of an associated method, whether taken by value
65    /// or by reference.
66    pub struct Receiver {
67        pub attrs: Vec<Attribute>,
68        pub reference: Option<(Token![&], Option<Lifetime>)>,
69        pub mutability: Option<Token![mut]>,
70        pub self_token: Token![self],
71        pub colon_token: Option<Token![:]>,
72        pub ty: Box<Type>,
73    }
74}
75
76ast_struct! {
77    /// The variadic argument of a foreign function.
78    pub struct Variadic {
79        pub attrs: Vec<Attribute>,
80        pub pat: Option<(Box<Pat>, Token![:])>,
81        pub dots: Token![...],
82        pub comma: Option<Token![,]>,
83    }
84}
85
86mod parsing {
87    use syn::{
88        braced, parenthesized,
89        parse::{discouraged::Speculative, Parse, ParseStream, Result},
90        punctuated::Punctuated,
91        Abi, Attribute, Error, Generics, Ident, Lifetime, Path, ReturnType, Token, Type, TypePath,
92        TypeReference, Visibility,
93    };
94
95    use super::{Block, FnArg, ItemFn, Receiver, Signature, Variadic};
96    use crate::pat::{Pat, PatType, PatWild};
97
98    impl Parse for Block {
99        fn parse(input: ParseStream<'_>) -> Result<Self> {
100            let content;
101            Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
102        }
103    }
104
105    impl Parse for Signature {
106        fn parse(input: ParseStream<'_>) -> Result<Self> {
107            let constness: Option<Token![const]> = input.parse()?;
108            let asyncness: Option<Token![async]> = input.parse()?;
109            let unsafety: Option<Token![unsafe]> = input.parse()?;
110            let abi: Option<Abi> = input.parse()?;
111            let fn_token: Token![fn] = input.parse()?;
112            let ident: Ident = input.parse()?;
113            let mut generics: Generics = input.parse()?;
114
115            let content;
116            let paren_token = parenthesized!(content in input);
117            let (inputs, variadic) = parse_fn_args(&content)?;
118
119            let output: ReturnType = input.parse()?;
120            generics.where_clause = input.parse()?;
121
122            Ok(Self {
123                constness,
124                asyncness,
125                unsafety,
126                abi,
127                fn_token,
128                ident,
129                generics,
130                paren_token,
131                inputs,
132                variadic,
133                output,
134            })
135        }
136    }
137
138    impl Parse for ItemFn {
139        fn parse(input: ParseStream<'_>) -> Result<Self> {
140            let attrs = input.call(Attribute::parse_outer)?;
141            let vis: Visibility = input.parse()?;
142            let sig: Signature = input.parse()?;
143            let block = input.parse()?;
144            Ok(Self { attrs, vis, sig, block: Box::new(block) })
145        }
146    }
147
148    impl Parse for FnArg {
149        fn parse(input: ParseStream<'_>) -> Result<Self> {
150            let allow_variadic = false;
151            let attrs = input.call(Attribute::parse_outer)?;
152            match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? {
153                FnArgOrVariadic::FnArg(arg) => Ok(arg),
154                FnArgOrVariadic::Variadic(_) => unreachable!(),
155            }
156        }
157    }
158
159    enum FnArgOrVariadic {
160        FnArg(FnArg),
161        Variadic(Variadic),
162    }
163
164    fn parse_fn_arg_or_variadic(
165        input: ParseStream<'_>,
166        attrs: Vec<Attribute>,
167        allow_variadic: bool,
168    ) -> Result<FnArgOrVariadic> {
169        let ahead = input.fork();
170        if let Ok(mut receiver) = ahead.parse::<Receiver>() {
171            input.advance_to(&ahead);
172            receiver.attrs = attrs;
173            return Ok(FnArgOrVariadic::FnArg(FnArg::Receiver(receiver)));
174        }
175
176        // Hack to parse pre-2018 syntax in
177        // test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs
178        // because the rest of the test case is valuable.
179        if input.peek(Ident) && input.peek2(Token![<]) {
180            let span = input.fork().parse::<Ident>()?.span();
181            return Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType {
182                attrs,
183                pat: Box::new(Pat::Wild(PatWild {
184                    attrs: Vec::new(),
185                    underscore_token: Token![_](span),
186                })),
187                colon_token: Token![:](span),
188                ty: input.parse()?,
189            })));
190        }
191
192        let pat = Box::new(Pat::parse_single(input)?);
193        let colon_token: Token![:] = input.parse()?;
194
195        if allow_variadic {
196            if let Some(dots) = input.parse::<Option<Token![...]>>()? {
197                return Ok(FnArgOrVariadic::Variadic(Variadic {
198                    attrs,
199                    pat: Some((pat, colon_token)),
200                    dots,
201                    comma: None,
202                }));
203            }
204        }
205
206        Ok(FnArgOrVariadic::FnArg(FnArg::Typed(PatType {
207            attrs,
208            pat,
209            colon_token,
210            ty: input.parse()?,
211        })))
212    }
213
214    impl Parse for Receiver {
215        fn parse(input: ParseStream<'_>) -> Result<Self> {
216            let reference = if input.peek(Token![&]) {
217                let ampersand: Token![&] = input.parse()?;
218                let lifetime: Option<Lifetime> = input.parse()?;
219                Some((ampersand, lifetime))
220            } else {
221                None
222            };
223            let mutability: Option<Token![mut]> = input.parse()?;
224            let self_token: Token![self] = input.parse()?;
225            let colon_token: Option<Token![:]> =
226                if reference.is_some() { None } else { input.parse()? };
227            let ty: Type = if colon_token.is_some() {
228                input.parse()?
229            } else {
230                let mut ty = Type::Path(TypePath {
231                    qself: None,
232                    path: Path::from(Ident::new("Self", self_token.span)),
233                });
234                if let Some((ampersand, lifetime)) = reference.as_ref() {
235                    ty = Type::Reference(TypeReference {
236                        and_token: Token![&](ampersand.span),
237                        lifetime: lifetime.clone(),
238                        mutability: mutability.as_ref().map(|m| Token![mut](m.span)),
239                        elem: Box::new(ty),
240                    });
241                }
242                ty
243            };
244            Ok(Self {
245                attrs: Vec::new(),
246                reference,
247                mutability,
248                self_token,
249                colon_token,
250                ty: Box::new(ty),
251            })
252        }
253    }
254
255    fn parse_fn_args(
256        input: ParseStream<'_>,
257    ) -> Result<(Punctuated<FnArg, Token![,]>, Option<Variadic>)> {
258        let mut args = Punctuated::new();
259        let mut variadic = None;
260        let mut has_receiver = false;
261
262        while !input.is_empty() {
263            let attrs = input.call(Attribute::parse_outer)?;
264
265            if let Some(dots) = input.parse::<Option<Token![...]>>()? {
266                variadic = Some(Variadic {
267                    attrs,
268                    pat: None,
269                    dots,
270                    comma: if input.is_empty() { None } else { Some(input.parse()?) },
271                });
272                break;
273            }
274
275            let allow_variadic = true;
276            let arg = match parse_fn_arg_or_variadic(input, attrs, allow_variadic)? {
277                FnArgOrVariadic::FnArg(arg) => arg,
278                FnArgOrVariadic::Variadic(arg) => {
279                    variadic = Some(Variadic {
280                        comma: if input.is_empty() { None } else { Some(input.parse()?) },
281                        ..arg
282                    });
283                    break;
284                }
285            };
286
287            match &arg {
288                FnArg::Receiver(receiver) if has_receiver => {
289                    return Err(Error::new(
290                        receiver.self_token.span,
291                        "unexpected second method receiver",
292                    ));
293                }
294                FnArg::Receiver(receiver) if !args.is_empty() => {
295                    return Err(Error::new(receiver.self_token.span, "unexpected method receiver"));
296                }
297                FnArg::Receiver(_) => has_receiver = true,
298                FnArg::Typed(_) => {}
299            }
300            args.push_value(arg);
301
302            if input.is_empty() {
303                break;
304            }
305
306            let comma: Token![,] = input.parse()?;
307            args.push_punct(comma);
308        }
309
310        Ok((args, variadic))
311    }
312}
313
314mod printing {
315    use proc_macro2::TokenStream;
316    use quote::{ToTokens, TokenStreamExt};
317    use syn::{Token, Type};
318
319    use super::{Block, ItemFn, Receiver, Signature, Variadic};
320
321    impl ToTokens for ItemFn {
322        fn to_tokens(&self, tokens: &mut TokenStream) {
323            tokens.append_all(&self.attrs);
324            self.vis.to_tokens(tokens);
325            self.sig.to_tokens(tokens);
326            self.block.to_tokens(tokens);
327        }
328    }
329
330    impl ToTokens for Block {
331        fn to_tokens(&self, tokens: &mut TokenStream) {
332            self.brace_token.surround(tokens, |tokens| {
333                tokens.append_all(self.stmts.clone());
334            });
335        }
336    }
337
338    impl ToTokens for Signature {
339        fn to_tokens(&self, tokens: &mut TokenStream) {
340            self.constness.to_tokens(tokens);
341            self.asyncness.to_tokens(tokens);
342            self.unsafety.to_tokens(tokens);
343            self.abi.to_tokens(tokens);
344            self.fn_token.to_tokens(tokens);
345            self.ident.to_tokens(tokens);
346            self.generics.to_tokens(tokens);
347            self.paren_token.surround(tokens, |tokens| {
348                self.inputs.to_tokens(tokens);
349                if let Some(variadic) = &self.variadic {
350                    if !self.inputs.empty_or_trailing() {
351                        <Token![,]>::default().to_tokens(tokens);
352                    }
353                    variadic.to_tokens(tokens);
354                }
355            });
356            self.output.to_tokens(tokens);
357            self.generics.where_clause.to_tokens(tokens);
358        }
359    }
360
361    impl ToTokens for Receiver {
362        fn to_tokens(&self, tokens: &mut TokenStream) {
363            tokens.append_all(&self.attrs);
364            if let Some((ampersand, lifetime)) = &self.reference {
365                ampersand.to_tokens(tokens);
366                lifetime.to_tokens(tokens);
367            }
368            self.mutability.to_tokens(tokens);
369            self.self_token.to_tokens(tokens);
370            if let Some(colon_token) = &self.colon_token {
371                colon_token.to_tokens(tokens);
372                self.ty.to_tokens(tokens);
373            } else {
374                let consistent = match (&self.reference, &self.mutability, &*self.ty) {
375                    (Some(_), mutability, Type::Reference(ty)) => {
376                        mutability.is_some() == ty.mutability.is_some()
377                            && match &*ty.elem {
378                                Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"),
379                                _ => false,
380                            }
381                    }
382                    (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"),
383                    _ => false,
384                };
385                if !consistent {
386                    <Token![:]>::default().to_tokens(tokens);
387                    self.ty.to_tokens(tokens);
388                }
389            }
390        }
391    }
392
393    impl ToTokens for Variadic {
394        fn to_tokens(&self, tokens: &mut TokenStream) {
395            tokens.append_all(&self.attrs);
396            if let Some((pat, colon)) = &self.pat {
397                pat.to_tokens(tokens);
398                colon.to_tokens(tokens);
399            }
400            self.dots.to_tokens(tokens);
401            self.comma.to_tokens(tokens);
402        }
403    }
404}