@@ -2,8 +2,8 @@ use crate::buffer::Cursor;
2
2
use crate :: error:: { self , Error } ;
3
3
use crate :: sealed:: lookahead:: Sealed ;
4
4
use crate :: span:: IntoSpans ;
5
- use crate :: token:: Token ;
6
- use proc_macro2:: Span ;
5
+ use crate :: token:: { CustomToken , Token } ;
6
+ use proc_macro2:: { Delimiter , Span } ;
7
7
use std:: cell:: RefCell ;
8
8
9
9
/// Support for checking the next token in a stream to decide how to parse.
@@ -110,7 +110,18 @@ impl<'a> Lookahead1<'a> {
110
110
/// The error message will identify all of the expected token types that
111
111
/// have been peeked against this lookahead instance.
112
112
pub fn error ( self ) -> Error {
113
- let comparisons = self . comparisons . into_inner ( ) ;
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
+ } ) ;
114
125
match comparisons. len ( ) {
115
126
0 => {
116
127
if self . cursor . eof ( ) {
@@ -150,6 +161,160 @@ pub trait Peek: Sealed {
150
161
type Token : Token ;
151
162
}
152
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
+
153
318
impl < F : Copy + FnOnce ( TokenMarker ) -> T , T : Token > Peek for F {
154
319
type Token = T ;
155
320
}
@@ -163,3 +328,5 @@ impl<S> IntoSpans<S> for TokenMarker {
163
328
}
164
329
165
330
impl < F : Copy + FnOnce ( TokenMarker ) -> T , T : Token > Sealed for F { }
331
+
332
+ impl Sealed for End { }
0 commit comments