xref: /linux/rust/syn/lookahead.rs (revision 54e3eae855629702c566bd2e130d9f40e7f35bde)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use crate::buffer::Cursor;
4 use crate::error::{self, Error};
5 use crate::sealed::lookahead::Sealed;
6 use crate::span::IntoSpans;
7 use crate::token::{CustomToken, Token};
8 use proc_macro2::{Delimiter, Span};
9 use std::cell::RefCell;
10 
11 /// Support for checking the next token in a stream to decide how to parse.
12 ///
13 /// An important advantage over [`ParseStream::peek`] is that here we
14 /// automatically construct an appropriate error message based on the token
15 /// alternatives that get peeked. If you are producing your own error message,
16 /// go ahead and use `ParseStream::peek` instead.
17 ///
18 /// Use [`ParseStream::lookahead1`] to construct this object.
19 ///
20 /// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek
21 /// [`ParseStream::lookahead1`]: crate::parse::ParseBuffer::lookahead1
22 ///
23 /// Consuming tokens from the source stream after constructing a lookahead
24 /// object does not also advance the lookahead object.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use syn::{ConstParam, Ident, Lifetime, LifetimeParam, Result, Token, TypeParam};
30 /// use syn::parse::{Parse, ParseStream};
31 ///
32 /// // A generic parameter, a single one of the comma-separated elements inside
33 /// // angle brackets in:
34 /// //
35 /// //     fn f<T: Clone, 'a, 'b: 'a, const N: usize>() { ... }
36 /// //
37 /// // On invalid input, lookahead gives us a reasonable error message.
38 /// //
39 /// //     error: expected one of: identifier, lifetime, `const`
40 /// //       |
41 /// //     5 |     fn f<!Sized>() {}
42 /// //       |          ^
43 /// enum GenericParam {
44 ///     Type(TypeParam),
45 ///     Lifetime(LifetimeParam),
46 ///     Const(ConstParam),
47 /// }
48 ///
49 /// impl Parse for GenericParam {
50 ///     fn parse(input: ParseStream) -> Result<Self> {
51 ///         let lookahead = input.lookahead1();
52 ///         if lookahead.peek(Ident) {
53 ///             input.parse().map(GenericParam::Type)
54 ///         } else if lookahead.peek(Lifetime) {
55 ///             input.parse().map(GenericParam::Lifetime)
56 ///         } else if lookahead.peek(Token![const]) {
57 ///             input.parse().map(GenericParam::Const)
58 ///         } else {
59 ///             Err(lookahead.error())
60 ///         }
61 ///     }
62 /// }
63 /// ```
64 pub struct Lookahead1<'a> {
65     scope: Span,
66     cursor: Cursor<'a>,
67     comparisons: RefCell<Vec<&'static str>>,
68 }
69 
70 pub(crate) fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
71     Lookahead1 {
72         scope,
73         cursor,
74         comparisons: RefCell::new(Vec::new()),
75     }
76 }
77 
78 fn peek_impl(
79     lookahead: &Lookahead1,
80     peek: fn(Cursor) -> bool,
81     display: fn() -> &'static str,
82 ) -> bool {
83     if peek(lookahead.cursor) {
84         return true;
85     }
86     lookahead.comparisons.borrow_mut().push(display());
87     false
88 }
89 
90 impl<'a> Lookahead1<'a> {
91     /// Looks at the next token in the parse stream to determine whether it
92     /// matches the requested type of token.
93     ///
94     /// # Syntax
95     ///
96     /// Note that this method does not use turbofish syntax. Pass the peek type
97     /// inside of parentheses.
98     ///
99     /// - `input.peek(Token![struct])`
100     /// - `input.peek(Token![==])`
101     /// - `input.peek(Ident)`&emsp;*(does not accept keywords)*
102     /// - `input.peek(Ident::peek_any)`
103     /// - `input.peek(Lifetime)`
104     /// - `input.peek(token::Brace)`
105     pub fn peek<T: Peek>(&self, token: T) -> bool {
106         let _ = token;
107         peek_impl(self, T::Token::peek, T::Token::display)
108     }
109 
110     /// Triggers an error at the current position of the parse stream.
111     ///
112     /// The error message will identify all of the expected token types that
113     /// have been peeked against this lookahead instance.
114     pub fn error(self) -> Error {
115         let mut comparisons = self.comparisons.into_inner();
116         comparisons.retain_mut(|display| {
117             if *display == "`)`" {
118                 *display = match self.cursor.scope_delimiter() {
119                     Delimiter::Parenthesis => "`)`",
120                     Delimiter::Brace => "`}`",
121                     Delimiter::Bracket => "`]`",
122                     Delimiter::None => return false,
123                 }
124             }
125             true
126         });
127         match comparisons.len() {
128             0 => {
129                 if self.cursor.eof() {
130                     Error::new(self.scope, "unexpected end of input")
131                 } else {
132                     Error::new(self.cursor.span(), "unexpected token")
133                 }
134             }
135             1 => {
136                 let message = format!("expected {}", comparisons[0]);
137                 error::new_at(self.scope, self.cursor, message)
138             }
139             2 => {
140                 let message = format!("expected {} or {}", comparisons[0], comparisons[1]);
141                 error::new_at(self.scope, self.cursor, message)
142             }
143             _ => {
144                 let join = comparisons.join(", ");
145                 let message = format!("expected one of: {}", join);
146                 error::new_at(self.scope, self.cursor, message)
147             }
148         }
149     }
150 }
151 
152 /// Types that can be parsed by looking at just one token.
153 ///
154 /// Use [`ParseStream::peek`] to peek one of these types in a parse stream
155 /// without consuming it from the stream.
156 ///
157 /// This trait is sealed and cannot be implemented for types outside of Syn.
158 ///
159 /// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek
160 pub trait Peek: Sealed {
161     // Not public API.
162     #[doc(hidden)]
163     type Token: Token;
164 }
165 
166 /// Pseudo-token used for peeking the end of a parse stream.
167 ///
168 /// This type is only useful as an argument to one of the following functions:
169 ///
170 /// - [`ParseStream::peek`][crate::parse::ParseBuffer::peek]
171 /// - [`ParseStream::peek2`][crate::parse::ParseBuffer::peek2]
172 /// - [`ParseStream::peek3`][crate::parse::ParseBuffer::peek3]
173 /// - [`Lookahead1::peek`]
174 ///
175 /// The peek will return `true` if there are no remaining tokens after that
176 /// point in the parse stream.
177 ///
178 /// # Example
179 ///
180 /// Suppose we are parsing attributes containing core::fmt inspired formatting
181 /// arguments:
182 ///
183 /// - `#[fmt("simple example")]`
184 /// - `#[fmt("interpolation e{}ample", self.x)]`
185 /// - `#[fmt("interpolation e{x}ample")]`
186 ///
187 /// and we want to recognize the cases where no interpolation occurs so that
188 /// more efficient code can be generated.
189 ///
190 /// The following implementation uses `input.peek(Token![,]) &&
191 /// input.peek2(End)` to recognize the case of a trailing comma without
192 /// consuming the comma from the parse stream, because if it isn't a trailing
193 /// comma, that same comma needs to be parsed as part of `args`.
194 ///
195 /// ```
196 /// use proc_macro2::TokenStream;
197 /// use quote::quote;
198 /// use syn::parse::{End, Parse, ParseStream, Result};
199 /// use syn::{parse_quote, Attribute, LitStr, Token};
200 ///
201 /// struct FormatArgs {
202 ///     template: LitStr,  // "...{}..."
203 ///     args: TokenStream, // , self.x
204 /// }
205 ///
206 /// impl Parse for FormatArgs {
207 ///     fn parse(input: ParseStream) -> Result<Self> {
208 ///         let template: LitStr = input.parse()?;
209 ///
210 ///         let args = if input.is_empty()
211 ///             || input.peek(Token![,]) && input.peek2(End)
212 ///         {
213 ///             input.parse::<Option<Token![,]>>()?;
214 ///             TokenStream::new()
215 ///         } else {
216 ///             input.parse()?
217 ///         };
218 ///
219 ///         Ok(FormatArgs {
220 ///             template,
221 ///             args,
222 ///         })
223 ///     }
224 /// }
225 ///
226 /// fn main() -> Result<()> {
227 ///     let attrs: Vec<Attribute> = parse_quote! {
228 ///         #[fmt("simple example")]
229 ///         #[fmt("interpolation e{}ample", self.x)]
230 ///         #[fmt("interpolation e{x}ample")]
231 ///     };
232 ///
233 ///     for attr in &attrs {
234 ///         let FormatArgs { template, args } = attr.parse_args()?;
235 ///         let requires_fmt_machinery =
236 ///             !args.is_empty() || template.value().contains(['{', '}']);
237 ///         let out = if requires_fmt_machinery {
238 ///             quote! {
239 ///                 ::core::write!(__formatter, #template #args)
240 ///             }
241 ///         } else {
242 ///             quote! {
243 ///                 __formatter.write_str(#template)
244 ///             }
245 ///         };
246 ///         println!("{}", out);
247 ///     }
248 ///     Ok(())
249 /// }
250 /// ```
251 ///
252 /// Implementing this parsing logic without `peek2(End)` is more clumsy because
253 /// we'd need a parse stream actually advanced past the comma before being able
254 /// to find out whether there is anything after it. It would look something
255 /// like:
256 ///
257 /// ```
258 /// # use proc_macro2::TokenStream;
259 /// # use syn::parse::{ParseStream, Result};
260 /// # use syn::Token;
261 /// #
262 /// # fn parse(input: ParseStream) -> Result<()> {
263 /// use syn::parse::discouraged::Speculative as _;
264 ///
265 /// let ahead = input.fork();
266 /// ahead.parse::<Option<Token![,]>>()?;
267 /// let args = if ahead.is_empty() {
268 ///     input.advance_to(&ahead);
269 ///     TokenStream::new()
270 /// } else {
271 ///     input.parse()?
272 /// };
273 /// # Ok(())
274 /// # }
275 /// ```
276 ///
277 /// or:
278 ///
279 /// ```
280 /// # use proc_macro2::TokenStream;
281 /// # use syn::parse::{ParseStream, Result};
282 /// # use syn::Token;
283 /// #
284 /// # fn parse(input: ParseStream) -> Result<()> {
285 /// use quote::ToTokens as _;
286 ///
287 /// let comma: Option<Token![,]> = input.parse()?;
288 /// let mut args = TokenStream::new();
289 /// if !input.is_empty() {
290 ///     comma.to_tokens(&mut args);
291 ///     input.parse::<TokenStream>()?.to_tokens(&mut args);
292 /// }
293 /// # Ok(())
294 /// # }
295 /// ```
296 pub struct End;
297 
298 impl Copy for End {}
299 
300 impl Clone for End {
301     fn clone(&self) -> Self {
302         *self
303     }
304 }
305 
306 impl Peek for End {
307     type Token = Self;
308 }
309 
310 impl CustomToken for End {
311     fn peek(cursor: Cursor) -> bool {
312         cursor.eof()
313     }
314 
315     fn display() -> &'static str {
316         "`)`" // Lookahead1 error message will fill in the expected close delimiter
317     }
318 }
319 
320 impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Peek for F {
321     type Token = T;
322 }
323 
324 pub enum TokenMarker {}
325 
326 impl<S> IntoSpans<S> for TokenMarker {
327     fn into_spans(self) -> S {
328         match self {}
329     }
330 }
331 
332 impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
333 
334 impl Sealed for End {}
335