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