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
new(scope: Span, cursor: Cursor) -> Lookahead170 pub(crate) fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
71 Lookahead1 {
72 scope,
73 cursor,
74 comparisons: RefCell::new(Vec::new()),
75 }
76 }
77
peek_impl( lookahead: &Lookahead1, peek: fn(Cursor) -> bool, display: fn() -> &'static str, ) -> bool78 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)` *(does not accept keywords)*
102 /// - `input.peek(Ident::peek_any)`
103 /// - `input.peek(Lifetime)`
104 /// - `input.peek(token::Brace)`
peek<T: Peek>(&self, token: T) -> bool105 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.
error(self) -> Error114 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 {
clone(&self) -> Self301 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 {
peek(cursor: Cursor) -> bool311 fn peek(cursor: Cursor) -> bool {
312 cursor.eof()
313 }
314
display() -> &'static str315 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 {
into_spans(self) -> S327 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