// SPDX-License-Identifier: Apache-2.0 OR MIT12use crate::buffer::Cursor;3use crate::error::{self, Error};4use crate::sealed::lookahead::Sealed;5use crate::span::IntoSpans;6use crate::token::{CustomToken, Token};7use proc_macro2::{Delimiter, Span};8use std::cell::RefCell;910/// Support for checking the next token in a stream to decide how to parse.11///12/// An important advantage over [`ParseStream::peek`] is that here we13/// automatically construct an appropriate error message based on the token14/// alternatives that get peeked. If you are producing your own error message,15/// go ahead and use `ParseStream::peek` instead.16///17/// Use [`ParseStream::lookahead1`] to construct this object.18///19/// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek20/// [`ParseStream::lookahead1`]: crate::parse::ParseBuffer::lookahead121///22/// Consuming tokens from the source stream after constructing a lookahead23/// object does not also advance the lookahead object.24///25/// # Example26///27/// ```28/// use syn::{ConstParam, Ident, Lifetime, LifetimeParam, Result, Token, TypeParam};29/// use syn::parse::{Parse, ParseStream};30///31/// // A generic parameter, a single one of the comma-separated elements inside32/// // angle brackets in:33/// //34/// // fn f<T: Clone, 'a, 'b: 'a, const N: usize>() { ... }35/// //36/// // On invalid input, lookahead gives us a reasonable error message.37/// //38/// // error: expected one of: identifier, lifetime, `const`39/// // |40/// // 5 | fn f<!Sized>() {}41/// // | ^42/// enum GenericParam {43/// Type(TypeParam),44/// Lifetime(LifetimeParam),45/// Const(ConstParam),46/// }47///48/// impl Parse for GenericParam {49/// fn parse(input: ParseStream) -> Result<Self> {50/// let lookahead = input.lookahead1();51/// if lookahead.peek(Ident) {52/// input.parse().map(GenericParam::Type)53/// } else if lookahead.peek(Lifetime) {54/// input.parse().map(GenericParam::Lifetime)55/// } else if lookahead.peek(Token![const]) {56/// input.parse().map(GenericParam::Const)57/// } else {58/// Err(lookahead.error())59/// }60/// }61/// }62/// ```63pub struct Lookahead1<'a> {64scope: Span,65cursor: Cursor<'a>,66comparisons: RefCell<Vec<&'static str>>,67}6869pub(crate) fn new(scope: Span, cursor: Cursor) -> Lookahead1 {70Lookahead1 {71scope,72cursor,73comparisons: RefCell::new(Vec::new()),74}75}7677fn peek_impl(78lookahead: &Lookahead1,79peek: fn(Cursor) -> bool,80display: fn() -> &'static str,81) -> bool {82if peek(lookahead.cursor) {83return true;84}85lookahead.comparisons.borrow_mut().push(display());86false87}8889impl<'a> Lookahead1<'a> {90/// Looks at the next token in the parse stream to determine whether it91/// matches the requested type of token.92///93/// # Syntax94///95/// Note that this method does not use turbofish syntax. Pass the peek type96/// inside of parentheses.97///98/// - `input.peek(Token![struct])`99/// - `input.peek(Token![==])`100/// - `input.peek(Ident)` *(does not accept keywords)*101/// - `input.peek(Ident::peek_any)`102/// - `input.peek(Lifetime)`103/// - `input.peek(token::Brace)`104pub fn peek<T: Peek>(&self, token: T) -> bool {105let _ = token;106peek_impl(self, T::Token::peek, T::Token::display)107}108109/// Triggers an error at the current position of the parse stream.110///111/// The error message will identify all of the expected token types that112/// have been peeked against this lookahead instance.113pub fn error(self) -> Error {114let mut comparisons = self.comparisons.into_inner();115comparisons.retain_mut(|display| {116if *display == "`)`" {117*display = match self.cursor.scope_delimiter() {118Delimiter::Parenthesis => "`)`",119Delimiter::Brace => "`}`",120Delimiter::Bracket => "`]`",121Delimiter::None => return false,122}123}124true125});126match comparisons.len() {1270 => {128if self.cursor.eof() {129Error::new(self.scope, "unexpected end of input")130} else {131Error::new(self.cursor.span(), "unexpected token")132}133}1341 => {135let message = format!("expected {}", comparisons[0]);136error::new_at(self.scope, self.cursor, message)137}1382 => {139let message = format!("expected {} or {}", comparisons[0], comparisons[1]);140error::new_at(self.scope, self.cursor, message)141}142_ => {143let join = comparisons.join(", ");144let message = format!("expected one of: {}", join);145error::new_at(self.scope, self.cursor, message)146}147}148}149}150151/// Types that can be parsed by looking at just one token.152///153/// Use [`ParseStream::peek`] to peek one of these types in a parse stream154/// without consuming it from the stream.155///156/// This trait is sealed and cannot be implemented for types outside of Syn.157///158/// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek159pub trait Peek: Sealed {160// Not public API.161#[doc(hidden)]162type Token: Token;163}164165/// Pseudo-token used for peeking the end of a parse stream.166///167/// This type is only useful as an argument to one of the following functions:168///169/// - [`ParseStream::peek`][crate::parse::ParseBuffer::peek]170/// - [`ParseStream::peek2`][crate::parse::ParseBuffer::peek2]171/// - [`ParseStream::peek3`][crate::parse::ParseBuffer::peek3]172/// - [`Lookahead1::peek`]173///174/// The peek will return `true` if there are no remaining tokens after that175/// point in the parse stream.176///177/// # Example178///179/// Suppose we are parsing attributes containing core::fmt inspired formatting180/// arguments:181///182/// - `#[fmt("simple example")]`183/// - `#[fmt("interpolation e{}ample", self.x)]`184/// - `#[fmt("interpolation e{x}ample")]`185///186/// and we want to recognize the cases where no interpolation occurs so that187/// more efficient code can be generated.188///189/// The following implementation uses `input.peek(Token![,]) &&190/// input.peek2(End)` to recognize the case of a trailing comma without191/// consuming the comma from the parse stream, because if it isn't a trailing192/// comma, that same comma needs to be parsed as part of `args`.193///194/// ```195/// use proc_macro2::TokenStream;196/// use quote::quote;197/// use syn::parse::{End, Parse, ParseStream, Result};198/// use syn::{parse_quote, Attribute, LitStr, Token};199///200/// struct FormatArgs {201/// template: LitStr, // "...{}..."202/// args: TokenStream, // , self.x203/// }204///205/// impl Parse for FormatArgs {206/// fn parse(input: ParseStream) -> Result<Self> {207/// let template: LitStr = input.parse()?;208///209/// let args = if input.is_empty()210/// || input.peek(Token![,]) && input.peek2(End)211/// {212/// input.parse::<Option<Token![,]>>()?;213/// TokenStream::new()214/// } else {215/// input.parse()?216/// };217///218/// Ok(FormatArgs {219/// template,220/// args,221/// })222/// }223/// }224///225/// fn main() -> Result<()> {226/// let attrs: Vec<Attribute> = parse_quote! {227/// #[fmt("simple example")]228/// #[fmt("interpolation e{}ample", self.x)]229/// #[fmt("interpolation e{x}ample")]230/// };231///232/// for attr in &attrs {233/// let FormatArgs { template, args } = attr.parse_args()?;234/// let requires_fmt_machinery =235/// !args.is_empty() || template.value().contains(['{', '}']);236/// let out = if requires_fmt_machinery {237/// quote! {238/// ::core::write!(__formatter, #template #args)239/// }240/// } else {241/// quote! {242/// __formatter.write_str(#template)243/// }244/// };245/// println!("{}", out);246/// }247/// Ok(())248/// }249/// ```250///251/// Implementing this parsing logic without `peek2(End)` is more clumsy because252/// we'd need a parse stream actually advanced past the comma before being able253/// to find out whether there is anything after it. It would look something254/// like:255///256/// ```257/// # use proc_macro2::TokenStream;258/// # use syn::parse::{ParseStream, Result};259/// # use syn::Token;260/// #261/// # fn parse(input: ParseStream) -> Result<()> {262/// use syn::parse::discouraged::Speculative as _;263///264/// let ahead = input.fork();265/// ahead.parse::<Option<Token![,]>>()?;266/// let args = if ahead.is_empty() {267/// input.advance_to(&ahead);268/// TokenStream::new()269/// } else {270/// input.parse()?271/// };272/// # Ok(())273/// # }274/// ```275///276/// or:277///278/// ```279/// # use proc_macro2::TokenStream;280/// # use syn::parse::{ParseStream, Result};281/// # use syn::Token;282/// #283/// # fn parse(input: ParseStream) -> Result<()> {284/// use quote::ToTokens as _;285///286/// let comma: Option<Token![,]> = input.parse()?;287/// let mut args = TokenStream::new();288/// if !input.is_empty() {289/// comma.to_tokens(&mut args);290/// input.parse::<TokenStream>()?.to_tokens(&mut args);291/// }292/// # Ok(())293/// # }294/// ```295pub struct End;296297impl Copy for End {}298299impl Clone for End {300fn clone(&self) -> Self {301*self302}303}304305impl Peek for End {306type Token = Self;307}308309impl CustomToken for End {310fn peek(cursor: Cursor) -> bool {311cursor.eof()312}313314fn display() -> &'static str {315"`)`" // Lookahead1 error message will fill in the expected close delimiter316}317}318319impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Peek for F {320type Token = T;321}322323pub enum TokenMarker {}324325impl<S> IntoSpans<S> for TokenMarker {326fn into_spans(self) -> S {327match self {}328}329}330331impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}332333impl Sealed for End {}334335336