Skip to main content

syn/
parse_quote.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/// Quasi-quotation macro that accepts input like the [`quote!`] macro but uses
4/// type inference to figure out a return type for those tokens.
5///
6/// [`quote!`]: https://docs.rs/quote/1.0/quote/index.html
7///
8/// The return type can be any syntax tree node that implements the [`Parse`]
9/// trait.
10///
11/// [`Parse`]: crate::parse::Parse
12///
13/// ```
14/// use quote::quote;
15/// use syn::{parse_quote, Stmt};
16///
17/// fn main() {
18///     let name = quote!(v);
19///     let ty = quote!(u8);
20///
21///     let stmt: Stmt = parse_quote! {
22///         let #name: #ty = Default::default();
23///     };
24///
25///     println!("{:#?}", stmt);
26/// }
27/// ```
28///
29/// *This macro is available only if Syn is built with both the `"parsing"` and
30/// `"printing"` features.*
31///
32/// # Example
33///
34/// The following helper function adds a bound `T: HeapSize` to every type
35/// parameter `T` in the input generics.
36///
37/// ```
38/// use syn::{parse_quote, Generics, GenericParam};
39///
40/// // Add a bound `T: HeapSize` to every type parameter T.
41/// fn add_trait_bounds(mut generics: Generics) -> Generics {
42///     for param in &mut generics.params {
43///         if let GenericParam::Type(type_param) = param {
44///             type_param.bounds.push(parse_quote!(HeapSize));
45///         }
46///     }
47///     generics
48/// }
49/// ```
50///
51/// # Special cases
52///
53/// This macro can parse the following additional types as a special case even
54/// though they do not implement the `Parse` trait.
55///
56/// - [`Attribute`] — parses one attribute, allowing either outer like `#[...]`
57///   or inner like `#![...]`
58/// - [`Vec<Attribute>`] — parses multiple attributes, including mixed kinds in
59///   any order
60/// - [`Punctuated<T, P>`] — parses zero or more `T` separated by punctuation
61///   `P` with optional trailing punctuation
62/// - [`Vec<Arm>`] — parses arms separated by optional commas according to the
63///   same grammar as the inside of a `match` expression
64/// - [`Vec<Stmt>`] — parses the same as `Block::parse_within`
65/// - [`Pat`], [`Box<Pat>`] — parses the same as
66///   `Pat::parse_multi_with_leading_vert`
67/// - [`Field`] — parses a named or unnamed struct field
68///
69/// [`Vec<Attribute>`]: Attribute
70/// [`Vec<Arm>`]: Arm
71/// [`Vec<Stmt>`]: Block::parse_within
72/// [`Pat`]: Pat::parse_multi_with_leading_vert
73/// [`Box<Pat>`]: Pat::parse_multi_with_leading_vert
74///
75/// # Panics
76///
77/// Panics if the tokens fail to parse as the expected syntax tree type. The
78/// caller is responsible for ensuring that the input tokens are syntactically
79/// valid.
80#[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "printing"))))]
81#[macro_export]
82macro_rules! parse_quote {
83    ($($tt:tt)*) => {
84        $crate::__private::parse_quote($crate::__private::quote::quote!($($tt)*))
85    };
86}
87
88/// This macro is [`parse_quote!`] + [`quote_spanned!`][quote::quote_spanned].
89///
90/// Please refer to each of their documentation.
91///
92/// # Example
93///
94/// ```
95/// use quote::{quote, quote_spanned};
96/// use syn::spanned::Spanned;
97/// use syn::{parse_quote_spanned, ReturnType, Signature};
98///
99/// // Changes `fn()` to `fn() -> Pin<Box<dyn Future<Output = ()>>>`,
100/// // and `fn() -> T` to `fn() -> Pin<Box<dyn Future<Output = T>>>`,
101/// // without introducing any call_site() spans.
102/// fn make_ret_pinned_future(sig: &mut Signature) {
103///     let ret = match &sig.output {
104///         ReturnType::Default => quote_spanned!(sig.paren_token.span=> ()),
105///         ReturnType::Type(_, ret) => quote!(#ret),
106///     };
107///     sig.output = parse_quote_spanned! {ret.span()=>
108///         -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = #ret>>>
109///     };
110/// }
111/// ```
112#[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "printing"))))]
113#[macro_export]
114macro_rules! parse_quote_spanned {
115    ($span:expr=> $($tt:tt)*) => {
116        $crate::__private::parse_quote($crate::__private::quote::quote_spanned!($span=> $($tt)*))
117    };
118}
119
120////////////////////////////////////////////////////////////////////////////////
121// Can parse any type that implements Parse.
122
123use crate::error::Result;
124use crate::parse::{Parse, ParseStream, Parser};
125use proc_macro2::TokenStream;
126
127// Not public API.
128#[doc(hidden)]
129#[track_caller]
130pub fn parse<T: ParseQuote>(token_stream: TokenStream) -> T {
131    let parser = T::parse;
132    match parser.parse2(token_stream) {
133        Ok(t) => t,
134        Err(err) => panic!("{}", err),
135    }
136}
137
138#[doc(hidden)]
139pub trait ParseQuote: Sized {
140    fn parse(input: ParseStream) -> Result<Self>;
141}
142
143impl<T: Parse> ParseQuote for T {
144    fn parse(input: ParseStream) -> Result<Self> {
145        <T as Parse>::parse(input)
146    }
147}
148
149////////////////////////////////////////////////////////////////////////////////
150// Any other types that we want `parse_quote!` to be able to parse.
151
152use crate::punctuated::Punctuated;
153#[cfg(any(feature = "full", feature = "derive"))]
154use crate::{attr, Attribute, Field, FieldMutability, Ident, Type, Visibility};
155#[cfg(feature = "full")]
156use crate::{Arm, Block, Pat, Stmt};
157
158#[cfg(any(feature = "full", feature = "derive"))]
159impl ParseQuote for Attribute {
160    fn parse(input: ParseStream) -> Result<Self> {
161        if input.peek(Token![#]) && input.peek2(Token![!]) {
162            attr::parsing::single_parse_inner(input)
163        } else {
164            attr::parsing::single_parse_outer(input)
165        }
166    }
167}
168
169#[cfg(any(feature = "full", feature = "derive"))]
170impl ParseQuote for Vec<Attribute> {
171    fn parse(input: ParseStream) -> Result<Self> {
172        let mut attrs = Vec::new();
173        while !input.is_empty() {
174            attrs.push(ParseQuote::parse(input)?);
175        }
176        Ok(attrs)
177    }
178}
179
180#[cfg(any(feature = "full", feature = "derive"))]
181impl ParseQuote for Field {
182    fn parse(input: ParseStream) -> Result<Self> {
183        let attrs = input.call(Attribute::parse_outer)?;
184        let vis: Visibility = input.parse()?;
185
186        let ident: Option<Ident>;
187        let colon_token: Option<Token![:]>;
188        let is_named = input.peek(Ident) && input.peek2(Token![:]) && !input.peek2(Token![::]);
189        if is_named {
190            ident = Some(input.parse()?);
191            colon_token = Some(input.parse()?);
192        } else {
193            ident = None;
194            colon_token = None;
195        }
196
197        let ty: Type = input.parse()?;
198
199        Ok(Field {
200            attrs,
201            vis,
202            mutability: FieldMutability::None,
203            ident,
204            colon_token,
205            ty,
206        })
207    }
208}
209
210#[cfg(feature = "full")]
211impl ParseQuote for Pat {
212    fn parse(input: ParseStream) -> Result<Self> {
213        Pat::parse_multi_with_leading_vert(input)
214    }
215}
216
217#[cfg(feature = "full")]
218impl ParseQuote for Box<Pat> {
219    fn parse(input: ParseStream) -> Result<Self> {
220        <Pat as ParseQuote>::parse(input).map(Box::new)
221    }
222}
223
224impl<T: Parse, P: Parse> ParseQuote for Punctuated<T, P> {
225    fn parse(input: ParseStream) -> Result<Self> {
226        Self::parse_terminated(input)
227    }
228}
229
230#[cfg(feature = "full")]
231impl ParseQuote for Vec<Stmt> {
232    fn parse(input: ParseStream) -> Result<Self> {
233        Block::parse_within(input)
234    }
235}
236
237#[cfg(feature = "full")]
238impl ParseQuote for Vec<Arm> {
239    fn parse(input: ParseStream) -> Result<Self> {
240        Arm::parse_multiple(input)
241    }
242}