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