Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/rust/syn/lookahead.rs
38271 views
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
336