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)` *(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