FILE
lookahead.rs
File preview with syntax highlighting.
linux-6.19.14/rust/syn/lookahead.rs
1// SPDX-License-Identifier: Apache-2.0 OR MIT 2 3use crate::buffer::Cursor; 4use crate::error::{self, Error}; 5use crate::sealed::lookahead::Sealed; 6use crate::span::IntoSpans; 7use crate::token::{CustomToken, Token}; 8use proc_macro2::{Delimiter, Span}; 9use 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/// ``` 64pub struct Lookahead1<'a> { 65 scope: Span, 66 cursor: Cursor<'a>, 67 comparisons: RefCell<Vec<&'static str>>, 68} 69 70pub(crate) fn new(scope: Span, cursor: Cursor) -> Lookahead1 { 71 Lookahead1 { 72 scope, 73 cursor, 74 comparisons: RefCell::new(Vec::new()), 75 } 76} 77 78fn 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 90impl<'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)` 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 160pub 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/// ``` 296pub struct End; 297 298impl Copy for End {} 299 300impl Clone for End { 301 fn clone(&self) -> Self { 302 *self 303 } 304} 305 306impl Peek for End { 307 type Token = Self; 308} 309 310impl 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 320impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Peek for F { 321 type Token = T; 322} 323 324pub enum TokenMarker {} 325 326impl<S> IntoSpans<S> for TokenMarker { 327 fn into_spans(self) -> S { 328 match self {} 329 } 330} 331 332impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Sealed for F {} 333 334impl Sealed for End {}
~/
Read-only file preview.