Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/syn/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 133 kB image not shown  

Quelle  expr.rs   Sprache: unbekannt

 
use crate::attr::Attribute;
#[cfg(all(feature = "parsing", feature = "full"))]
use crate::error::Result;
#[cfg(feature = "parsing")]
use crate::ext::IdentExt as _;
#[cfg(feature = "full")]
use crate::generics::BoundLifetimes;
use crate::ident::Ident;
#[cfg(any(feature = "parsing", feature = "full"))]
use crate::lifetime::Lifetime;
use crate::lit::Lit;
use crate::mac::Macro;
use crate::op::{BinOp, UnOp};
#[cfg(feature = "parsing")]
use crate::parse::ParseStream;
#[cfg(feature = "full")]
use crate::pat::Pat;
use crate::path::{AngleBracketedGenericArguments, Path, QSelf};
use crate::punctuated::Punctuated;
#[cfg(feature = "full")]
use crate::stmt::Block;
use crate::token;
#[cfg(feature = "full")]
use crate::ty::ReturnType;
use crate::ty::Type;
use proc_macro2::{Span, TokenStream};
#[cfg(feature = "printing")]
use quote::IdentFragment;
#[cfg(feature = "printing")]
use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};
#[cfg(all(feature = "parsing", feature = "full"))]
use std::mem;

ast_enum_of_structs! {
    /// A Rust expression.
    ///
    /// *This type is available only if Syn is built with the `"derive"` or `"full"`
    /// feature, but most of the variants are not available unless "full" is enabled.*
    ///
    /// # Syntax tree enums
    ///
    /// This type is a syntax tree enum. In Syn this and other syntax tree enums
    /// are designed to be traversed using the following rebinding idiom.
    ///
    /// ```
    /// # use syn::Expr;
    /// #
    /// # fn example(expr: Expr) {
    /// # const IGNORE: &str = stringify! {
    /// let expr: Expr = /* ... */;
    /// # };
    /// match expr {
    ///     Expr::MethodCall(expr) => {
    ///         /* ... */
    ///     }
    ///     Expr::Cast(expr) => {
    ///         /* ... */
    ///     }
    ///     Expr::If(expr) => {
    ///         /* ... */
    ///     }
    ///
    ///     /* ... */
    ///     # _ => {}
    /// # }
    /// # }
    /// ```
    ///
    /// We begin with a variable `expr` of type `Expr` that has no fields
    /// (because it is an enum), and by matching on it and rebinding a variable
    /// with the same name `expr` we effectively imbue our variable with all of
    /// the data fields provided by the variant that it turned out to be. So for
    /// example above if we ended up in the `MethodCall` case then we get to use
    /// `expr.receiver`, `expr.args` etc; if we ended up in the `If` case we get
    /// to use `expr.cond`, `expr.then_branch`, `expr.else_branch`.
    ///
    /// This approach avoids repeating the variant names twice on every line.
    ///
    /// ```
    /// # use syn::{Expr, ExprMethodCall};
    /// #
    /// # fn example(expr: Expr) {
    /// // Repetitive; recommend not doing this.
    /// match expr {
    ///     Expr::MethodCall(ExprMethodCall { method, args, .. }) => {
    /// # }
    /// # _ => {}
    /// # }
    /// # }
    /// ```
    ///
    /// In general, the name to which a syntax tree enum variant is bound should
    /// be a suitable name for the complete syntax tree enum type.
    ///
    /// ```
    /// # use syn::{Expr, ExprField};
    /// #
    /// # fn example(discriminant: ExprField) {
    /// // Binding is called `base` which is the name I would use if I were
    /// // assigning `*discriminant.base` without an `if let`.
    /// if let Expr::Tuple(base) = *discriminant.base {
    /// # }
    /// # }
    /// ```
    ///
    /// A sign that you may not be choosing the right variable names is if you
    /// see names getting repeated in your code, like accessing
    /// `receiver.receiver` or `pat.pat` or `cond.cond`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    #[non_exhaustive]
    pub enum Expr {
        /// A slice literal expression: `[a, b, c, d]`.
        Array(ExprArray),

        /// An assignment expression: `a = compute()`.
        Assign(ExprAssign),

        /// An async block: `async { ... }`.
        Async(ExprAsync),

        /// An await expression: `fut.await`.
        Await(ExprAwait),

        /// A binary operation: `a + b`, `a += b`.
        Binary(ExprBinary),

        /// A blocked scope: `{ ... }`.
        Block(ExprBlock),

        /// A `break`, with an optional label to break and an optional
        /// expression.
        Break(ExprBreak),

        /// A function call expression: `invoke(a, b)`.
        Call(ExprCall),

        /// A cast expression: `foo as f64`.
        Cast(ExprCast),

        /// A closure expression: `|a, b| a + b`.
        Closure(ExprClosure),

        /// A const block: `const { ... }`.
        Const(ExprConst),

        /// A `continue`, with an optional label.
        Continue(ExprContinue),

        /// Access of a named struct field (`obj.k`) or unnamed tuple struct
        /// field (`obj.0`).
        Field(ExprField),

        /// A for loop: `for pat in expr { ... }`.
        ForLoop(ExprForLoop),

        /// An expression contained within invisible delimiters.
        ///
        /// This variant is important for faithfully representing the precedence
        /// of expressions and is related to `None`-delimited spans in a
        /// `TokenStream`.
        Group(ExprGroup),

        /// An `if` expression with an optional `else` block: `if expr { ... }
        /// else { ... }`.
        ///
        /// The `else` branch expression may only be an `If` or `Block`
        /// expression, not any of the other types of expression.
        If(ExprIf),

        /// A square bracketed indexing expression: `vector[2]`.
        Index(ExprIndex),

        /// The inferred value of a const generic argument, denoted `_`.
        Infer(ExprInfer),

        /// A `let` guard: `let Some(x) = opt`.
        Let(ExprLet),

        /// A literal in place of an expression: `1`, `"foo"`.
        Lit(ExprLit),

        /// Conditionless loop: `loop { ... }`.
        Loop(ExprLoop),

        /// A macro invocation expression: `format!("{}", q)`.
        Macro(ExprMacro),

        /// A `match` expression: `match n { Some(n) => {}, None => {} }`.
        Match(ExprMatch),

        /// A method call expression: `x.foo::<T>(a, b)`.
        MethodCall(ExprMethodCall),

        /// A parenthesized expression: `(a + b)`.
        Paren(ExprParen),

        /// A path like `std::mem::replace` possibly containing generic
        /// parameters and a qualified self-type.
        ///
        /// A plain identifier like `x` is a path of length 1.
        Path(ExprPath),

        /// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
        Range(ExprRange),

        /// Address-of operation: `&raw const place` or `&raw mut place`.
        RawAddr(ExprRawAddr),

        /// A referencing operation: `&a` or `&mut a`.
        Reference(ExprReference),

        /// An array literal constructed from one repeated element: `[0u8; N]`.
        Repeat(ExprRepeat),

        /// A `return`, with an optional value to be returned.
        Return(ExprReturn),

        /// A struct literal expression: `Point { x: 1, y: 1 }`.
        ///
        /// The `rest` provides the value of the remaining fields as in `S { a:
        /// 1, b: 1, ..rest }`.
        Struct(ExprStruct),

        /// A try-expression: `expr?`.
        Try(ExprTry),

        /// A try block: `try { ... }`.
        TryBlock(ExprTryBlock),

        /// A tuple expression: `(a, b, c, d)`.
        Tuple(ExprTuple),

        /// A unary operation: `!x`, `*x`.
        Unary(ExprUnary),

        /// An unsafe block: `unsafe { ... }`.
        Unsafe(ExprUnsafe),

        /// Tokens in expression position not interpreted by Syn.
        Verbatim(TokenStream),

        /// A while loop: `while expr { ... }`.
        While(ExprWhile),

        /// A yield expression: `yield expr`.
        Yield(ExprYield),

        // For testing exhaustiveness in downstream code, use the following idiom:
        //
        //     match expr {
        //         #![cfg_attr(test, deny(non_exhaustive_omitted_patterns))]
        //
        //         Expr::Array(expr) => {...}
        //         Expr::Assign(expr) => {...}
        //         ...
        //         Expr::Yield(expr) => {...}
        //
        //         _ => { /* some sane fallback */ }
        //     }
        //
        // This way we fail your tests but don't break your library when adding
        // a variant. You will be notified by a test failure when a variant is
        // added, so that you can add code to handle it, but your library will
        // continue to compile and work for downstream users in the interim.
    }
}

ast_struct! {
    /// A slice literal expression: `[a, b, c, d]`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprArray #full {
        pub attrs: Vec<Attribute>,
        pub bracket_token: token::Bracket,
        pub elems: Punctuated<Expr, Token![,]>,
    }
}

ast_struct! {
    /// An assignment expression: `a = compute()`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprAssign #full {
        pub attrs: Vec<Attribute>,
        pub left: Box<Expr>,
        pub eq_token: Token![=],
        pub right: Box<Expr>,
    }
}

ast_struct! {
    /// An async block: `async { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprAsync #full {
        pub attrs: Vec<Attribute>,
        pub async_token: Token![async],
        pub capture: Option<Token![move]>,
        pub block: Block,
    }
}

ast_struct! {
    /// An await expression: `fut.await`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprAwait #full {
        pub attrs: Vec<Attribute>,
        pub base: Box<Expr>,
        pub dot_token: Token![.],
        pub await_token: Token![await],
    }
}

ast_struct! {
    /// A binary operation: `a + b`, `a += b`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprBinary {
        pub attrs: Vec<Attribute>,
        pub left: Box<Expr>,
        pub op: BinOp,
        pub right: Box<Expr>,
    }
}

ast_struct! {
    /// A blocked scope: `{ ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprBlock #full {
        pub attrs: Vec<Attribute>,
        pub label: Option<Label>,
        pub block: Block,
    }
}

ast_struct! {
    /// A `break`, with an optional label to break and an optional
    /// expression.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprBreak #full {
        pub attrs: Vec<Attribute>,
        pub break_token: Token![break],
        pub label: Option<Lifetime>,
        pub expr: Option<Box<Expr>>,
    }
}

ast_struct! {
    /// A function call expression: `invoke(a, b)`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprCall {
        pub attrs: Vec<Attribute>,
        pub func: Box<Expr>,
        pub paren_token: token::Paren,
        pub args: Punctuated<Expr, Token![,]>,
    }
}

ast_struct! {
    /// A cast expression: `foo as f64`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprCast {
        pub attrs: Vec<Attribute>,
        pub expr: Box<Expr>,
        pub as_token: Token![as],
        pub ty: Box<Type>,
    }
}

ast_struct! {
    /// A closure expression: `|a, b| a + b`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprClosure #full {
        pub attrs: Vec<Attribute>,
        pub lifetimes: Option<BoundLifetimes>,
        pub constness: Option<Token![const]>,
        pub movability: Option<Token![static]>,
        pub asyncness: Option<Token![async]>,
        pub capture: Option<Token![move]>,
        pub or1_token: Token![|],
        pub inputs: Punctuated<Pat, Token![,]>,
        pub or2_token: Token![|],
        pub output: ReturnType,
        pub body: Box<Expr>,
    }
}

ast_struct! {
    /// A const block: `const { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprConst #full {
        pub attrs: Vec<Attribute>,
        pub const_token: Token![const],
        pub block: Block,
    }
}

ast_struct! {
    /// A `continue`, with an optional label.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprContinue #full {
        pub attrs: Vec<Attribute>,
        pub continue_token: Token![continue],
        pub label: Option<Lifetime>,
    }
}

ast_struct! {
    /// Access of a named struct field (`obj.k`) or unnamed tuple struct
    /// field (`obj.0`).
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprField {
        pub attrs: Vec<Attribute>,
        pub base: Box<Expr>,
        pub dot_token: Token![.],
        pub member: Member,
    }
}

ast_struct! {
    /// A for loop: `for pat in expr { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprForLoop #full {
        pub attrs: Vec<Attribute>,
        pub label: Option<Label>,
        pub for_token: Token![for],
        pub pat: Box<Pat>,
        pub in_token: Token![in],
        pub expr: Box<Expr>,
        pub body: Block,
    }
}

ast_struct! {
    /// An expression contained within invisible delimiters.
    ///
    /// This variant is important for faithfully representing the precedence
    /// of expressions and is related to `None`-delimited spans in a
    /// `TokenStream`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprGroup {
        pub attrs: Vec<Attribute>,
        pub group_token: token::Group,
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// An `if` expression with an optional `else` block: `if expr { ... }
    /// else { ... }`.
    ///
    /// The `else` branch expression may only be an `If` or `Block`
    /// expression, not any of the other types of expression.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprIf #full {
        pub attrs: Vec<Attribute>,
        pub if_token: Token![if],
        pub cond: Box<Expr>,
        pub then_branch: Block,
        pub else_branch: Option<(Token![else], Box<Expr>)>,
    }
}

ast_struct! {
    /// A square bracketed indexing expression: `vector[2]`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprIndex {
        pub attrs: Vec<Attribute>,
        pub expr: Box<Expr>,
        pub bracket_token: token::Bracket,
        pub index: Box<Expr>,
    }
}

ast_struct! {
    /// The inferred value of a const generic argument, denoted `_`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprInfer #full {
        pub attrs: Vec<Attribute>,
        pub underscore_token: Token![_],
    }
}

ast_struct! {
    /// A `let` guard: `let Some(x) = opt`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprLet #full {
        pub attrs: Vec<Attribute>,
        pub let_token: Token![let],
        pub pat: Box<Pat>,
        pub eq_token: Token![=],
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// A literal in place of an expression: `1`, `"foo"`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprLit {
        pub attrs: Vec<Attribute>,
        pub lit: Lit,
    }
}

ast_struct! {
    /// Conditionless loop: `loop { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprLoop #full {
        pub attrs: Vec<Attribute>,
        pub label: Option<Label>,
        pub loop_token: Token![loop],
        pub body: Block,
    }
}

ast_struct! {
    /// A macro invocation expression: `format!("{}", q)`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprMacro {
        pub attrs: Vec<Attribute>,
        pub mac: Macro,
    }
}

ast_struct! {
    /// A `match` expression: `match n { Some(n) => {}, None => {} }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprMatch #full {
        pub attrs: Vec<Attribute>,
        pub match_token: Token![match],
        pub expr: Box<Expr>,
        pub brace_token: token::Brace,
        pub arms: Vec<Arm>,
    }
}

ast_struct! {
    /// A method call expression: `x.foo::<T>(a, b)`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprMethodCall {
        pub attrs: Vec<Attribute>,
        pub receiver: Box<Expr>,
        pub dot_token: Token![.],
        pub method: Ident,
        pub turbofish: Option<AngleBracketedGenericArguments>,
        pub paren_token: token::Paren,
        pub args: Punctuated<Expr, Token![,]>,
    }
}

ast_struct! {
    /// A parenthesized expression: `(a + b)`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprParen {
        pub attrs: Vec<Attribute>,
        pub paren_token: token::Paren,
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// A path like `std::mem::replace` possibly containing generic
    /// parameters and a qualified self-type.
    ///
    /// A plain identifier like `x` is a path of length 1.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprPath {
        pub attrs: Vec<Attribute>,
        pub qself: Option<QSelf>,
        pub path: Path,
    }
}

ast_struct! {
    /// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprRange #full {
        pub attrs: Vec<Attribute>,
        pub start: Option<Box<Expr>>,
        pub limits: RangeLimits,
        pub end: Option<Box<Expr>>,
    }
}

ast_struct! {
    /// Address-of operation: `&raw const place` or `&raw mut place`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprRawAddr #full {
        pub attrs: Vec<Attribute>,
        pub and_token: Token![&],
        pub raw: Token![raw],
        pub mutability: PointerMutability,
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// A referencing operation: `&a` or `&mut a`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprReference {
        pub attrs: Vec<Attribute>,
        pub and_token: Token![&],
        pub mutability: Option<Token![mut]>,
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// An array literal constructed from one repeated element: `[0u8; N]`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprRepeat #full {
        pub attrs: Vec<Attribute>,
        pub bracket_token: token::Bracket,
        pub expr: Box<Expr>,
        pub semi_token: Token![;],
        pub len: Box<Expr>,
    }
}

ast_struct! {
    /// A `return`, with an optional value to be returned.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprReturn #full {
        pub attrs: Vec<Attribute>,
        pub return_token: Token![return],
        pub expr: Option<Box<Expr>>,
    }
}

ast_struct! {
    /// A struct literal expression: `Point { x: 1, y: 1 }`.
    ///
    /// The `rest` provides the value of the remaining fields as in `S { a:
    /// 1, b: 1, ..rest }`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprStruct {
        pub attrs: Vec<Attribute>,
        pub qself: Option<QSelf>,
        pub path: Path,
        pub brace_token: token::Brace,
        pub fields: Punctuated<FieldValue, Token![,]>,
        pub dot2_token: Option<Token![..]>,
        pub rest: Option<Box<Expr>>,
    }
}

ast_struct! {
    /// A try-expression: `expr?`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprTry #full {
        pub attrs: Vec<Attribute>,
        pub expr: Box<Expr>,
        pub question_token: Token![?],
    }
}

ast_struct! {
    /// A try block: `try { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprTryBlock #full {
        pub attrs: Vec<Attribute>,
        pub try_token: Token![try],
        pub block: Block,
    }
}

ast_struct! {
    /// A tuple expression: `(a, b, c, d)`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprTuple {
        pub attrs: Vec<Attribute>,
        pub paren_token: token::Paren,
        pub elems: Punctuated<Expr, Token![,]>,
    }
}

ast_struct! {
    /// A unary operation: `!x`, `*x`.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct ExprUnary {
        pub attrs: Vec<Attribute>,
        pub op: UnOp,
        pub expr: Box<Expr>,
    }
}

ast_struct! {
    /// An unsafe block: `unsafe { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprUnsafe #full {
        pub attrs: Vec<Attribute>,
        pub unsafe_token: Token![unsafe],
        pub block: Block,
    }
}

ast_struct! {
    /// A while loop: `while expr { ... }`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprWhile #full {
        pub attrs: Vec<Attribute>,
        pub label: Option<Label>,
        pub while_token: Token![while],
        pub cond: Box<Expr>,
        pub body: Block,
    }
}

ast_struct! {
    /// A yield expression: `yield expr`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct ExprYield #full {
        pub attrs: Vec<Attribute>,
        pub yield_token: Token![yield],
        pub expr: Option<Box<Expr>>,
    }
}

impl Expr {
    /// An unspecified invalid expression.
    ///
    /// ```
    /// use quote::ToTokens;
    /// use std::mem;
    /// use syn::{parse_quote, Expr};
    ///
    /// fn unparenthesize(e: &mut Expr) {
    ///     while let Expr::Paren(paren) = e {
    ///         *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER);
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut e: Expr = parse_quote! { ((1 + 1)) };
    ///     unparenthesize(&mut e);
    ///     assert_eq!("1 + 1", e.to_token_stream().to_string());
    /// }
    /// ```
    pub const PLACEHOLDER: Self = Expr::Path(ExprPath {
        attrs: Vec::new(),
        qself: None,
        path: Path {
            leading_colon: None,
            segments: Punctuated::new(),
        },
    });

    /// An alternative to the primary `Expr::parse` parser (from the [`Parse`]
    /// trait) for ambiguous syntactic positions in which a trailing brace
    /// should not be taken as part of the expression.
    ///
    /// [`Parse`]: crate::parse::Parse
    ///
    /// Rust grammar has an ambiguity where braces sometimes turn a path
    /// expression into a struct initialization and sometimes do not. In the
    /// following code, the expression `S {}` is one expression. Presumably
    /// there is an empty struct `struct S {}` defined somewhere which it is
    /// instantiating.
    ///
    /// ```
    /// # struct S;
    /// # impl std::ops::Deref for S {
    /// #     type Target = bool;
    /// #     fn deref(&self) -> &Self::Target {
    /// #         &true
    /// #     }
    /// # }
    /// let _ = *S {};
    ///
    /// // parsed by rustc as: `*(S {})`
    /// ```
    ///
    /// We would want to parse the above using `Expr::parse` after the `=`
    /// token.
    ///
    /// But in the following, `S {}` is *not* a struct init expression.
    ///
    /// ```
    /// # const S: &bool = &true;
    /// if *S {} {}
    ///
    /// // parsed by rustc as:
    /// //
    /// //    if (*S) {
    /// //        /* empty block */
    /// //    }
    /// //    {
    /// //        /* another empty block */
    /// //    }
    /// ```
    ///
    /// For that reason we would want to parse if-conditions using
    /// `Expr::parse_without_eager_brace` after the `if` token. Same for similar
    /// syntactic positions such as the condition expr after a `while` token or
    /// the expr at the top of a `match`.
    ///
    /// The Rust grammar's choices around which way this ambiguity is resolved
    /// at various syntactic positions is fairly arbitrary. Really either parse
    /// behavior could work in most positions, and language designers just
    /// decide each case based on which is more likely to be what the programmer
    /// had in mind most of the time.
    ///
    /// ```
    /// # struct S;
    /// # fn doc() -> S {
    /// if return S {} {}
    /// # unreachable!()
    /// # }
    ///
    /// // parsed by rustc as:
    /// //
    /// //    if (return (S {})) {
    /// //    }
    /// //
    /// // but could equally well have been this other arbitrary choice:
    /// //
    /// //    if (return S) {
    /// //    }
    /// //    {}
    /// ```
    ///
    /// Note the grammar ambiguity on trailing braces is distinct from
    /// precedence and is not captured by assigning a precedence level to the
    /// braced struct init expr in relation to other operators. This can be
    /// illustrated by `return 0..S {}` vs `match 0..S {}`. The former parses as
    /// `return (0..(S {}))` implying tighter precedence for struct init than
    /// `..`, while the latter parses as `match (0..S) {}` implying tighter
    /// precedence for `..` than struct init, a contradiction.
    #[cfg(all(feature = "full", feature = "parsing"))]
    #[cfg_attr(docsrs, doc(cfg(all(feature = "full", feature = "parsing"))))]
    pub fn parse_without_eager_brace(input: ParseStream) -> Result<Expr> {
        parsing::ambiguous_expr(input, parsing::AllowStruct(false))
    }

    /// An alternative to the primary `Expr::parse` parser (from the [`Parse`]
    /// trait) for syntactic positions in which expression boundaries are placed
    /// more eagerly than done by the typical expression grammar. This includes
    /// expressions at the head of a statement or in the right-hand side of a
    /// `match` arm.
    ///
    /// [`Parse`]: crate::parse::Parse
    ///
    /// Compare the following cases:
    ///
    /// 1.
    ///   ```
    ///   # let result = ();
    ///   # let guard = false;
    ///   # let cond = true;
    ///   # let f = true;
    ///   # let g = f;
    ///   #
    ///   let _ = match result {
    ///       () if guard => if cond { f } else { g }
    ///       () => false,
    ///   };
    ///   ```
    ///
    /// 2.
    ///   ```
    ///   # let cond = true;
    ///   # let f = ();
    ///   # let g = f;
    ///   #
    ///   let _ = || {
    ///       if cond { f } else { g }
    ///       ()
    ///   };
    ///   ```
    ///
    /// 3.
    ///   ```
    ///   # let cond = true;
    ///   # let f = || ();
    ///   # let g = f;
    ///   #
    ///   let _ = [if cond { f } else { g } ()];
    ///   ```
    ///
    /// The same sequence of tokens `if cond { f } else { g } ()` appears in
    /// expression position 3 times. The first two syntactic positions use eager
    /// placement of expression boundaries, and parse as `Expr::If`, with the
    /// adjacent `()` becoming `Pat::Tuple` or `Expr::Tuple`. In contrast, the
    /// third case uses standard expression boundaries and parses as
    /// `Expr::Call`.
    ///
    /// As with [`parse_without_eager_brace`], this ambiguity in the Rust
    /// grammar is independent of precedence.
    ///
    /// [`parse_without_eager_brace`]: Self::parse_without_eager_brace
    #[cfg(all(feature = "full", feature = "parsing"))]
    #[cfg_attr(docsrs, doc(cfg(all(feature = "full", feature = "parsing"))))]
    pub fn parse_with_earlier_boundary_rule(input: ParseStream) -> Result<Expr> {
        parsing::parse_with_earlier_boundary_rule(input)
    }

    /// Returns whether the next token in the parse stream is one that might
    /// possibly form the beginning of an expr.
    ///
    /// This classification is a load-bearing part of the grammar of some Rust
    /// expressions, notably `return` and `break`. For example `return < …` will
    /// never parse `<` as a binary operator regardless of what comes after,
    /// because `<` is a legal starting token for an expression and so it's
    /// required to be continued as a return value, such as `return <Struct as
    /// Trait>::CONST`. Meanwhile `return > …` treats the `>` as a binary
    /// operator because it cannot be a starting token for any Rust expression.
    #[cfg(feature = "parsing")]
    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
    pub fn peek(input: ParseStream) -> bool {
        input.peek(Ident::peek_any) // value name or keyword
            || input.peek(token::Paren) // tuple
            || input.peek(token::Bracket) // array
            || input.peek(token::Brace) // block
            || input.peek(Lit) // literal
            || input.peek(Token![!]) && !input.peek(Token![!=]) // operator not
            || input.peek(Token![-]) && !input.peek(Token![-=]) && !input.peek(Token![->]) // unary minus
            || input.peek(Token![*]) && !input.peek(Token![*=]) // dereference
            || input.peek(Token![|]) && !input.peek(Token![|=]) // closure
            || input.peek(Token![&]) && !input.peek(Token![&=]) // reference
            || input.peek(Token![..]) // range
            || input.peek(Token![<]) && !input.peek(Token![<=]) && !input.peek(Token![<<=]) // associated path
            || input.peek(Token![::]) // absolute path
            || input.peek(Lifetime) // labeled loop
            || input.peek(Token![#]) // expression attributes
    }

    #[cfg(all(feature = "parsing", feature = "full"))]
    pub(crate) fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {
        match self {
            Expr::Array(ExprArray { attrs, .. })
            | Expr::Assign(ExprAssign { attrs, .. })
            | Expr::Async(ExprAsync { attrs, .. })
            | Expr::Await(ExprAwait { attrs, .. })
            | Expr::Binary(ExprBinary { attrs, .. })
            | Expr::Block(ExprBlock { attrs, .. })
            | Expr::Break(ExprBreak { attrs, .. })
            | Expr::Call(ExprCall { attrs, .. })
            | Expr::Cast(ExprCast { attrs, .. })
            | Expr::Closure(ExprClosure { attrs, .. })
            | Expr::Const(ExprConst { attrs, .. })
            | Expr::Continue(ExprContinue { attrs, .. })
            | Expr::Field(ExprField { attrs, .. })
            | Expr::ForLoop(ExprForLoop { attrs, .. })
            | Expr::Group(ExprGroup { attrs, .. })
            | Expr::If(ExprIf { attrs, .. })
            | Expr::Index(ExprIndex { attrs, .. })
            | Expr::Infer(ExprInfer { attrs, .. })
            | Expr::Let(ExprLet { attrs, .. })
            | Expr::Lit(ExprLit { attrs, .. })
            | Expr::Loop(ExprLoop { attrs, .. })
            | Expr::Macro(ExprMacro { attrs, .. })
            | Expr::Match(ExprMatch { attrs, .. })
            | Expr::MethodCall(ExprMethodCall { attrs, .. })
            | Expr::Paren(ExprParen { attrs, .. })
            | Expr::Path(ExprPath { attrs, .. })
            | Expr::Range(ExprRange { attrs, .. })
            | Expr::RawAddr(ExprRawAddr { attrs, .. })
            | Expr::Reference(ExprReference { attrs, .. })
            | Expr::Repeat(ExprRepeat { attrs, .. })
            | Expr::Return(ExprReturn { attrs, .. })
            | Expr::Struct(ExprStruct { attrs, .. })
            | Expr::Try(ExprTry { attrs, .. })
            | Expr::TryBlock(ExprTryBlock { attrs, .. })
            | Expr::Tuple(ExprTuple { attrs, .. })
            | Expr::Unary(ExprUnary { attrs, .. })
            | Expr::Unsafe(ExprUnsafe { attrs, .. })
            | Expr::While(ExprWhile { attrs, .. })
            | Expr::Yield(ExprYield { attrs, .. }) => mem::replace(attrs, new),
            Expr::Verbatim(_) => Vec::new(),
        }
    }
}

ast_enum! {
    /// A struct or tuple struct field accessed in a struct literal or field
    /// expression.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub enum Member {
        /// A named field like `self.x`.
        Named(Ident),
        /// An unnamed field like `self.0`.
        Unnamed(Index),
    }
}

impl From<Ident> for Member {
    fn from(ident: Ident) -> Member {
        Member::Named(ident)
    }
}

impl From<Index> for Member {
    fn from(index: Index) -> Member {
        Member::Unnamed(index)
    }
}

impl From<usize> for Member {
    fn from(index: usize) -> Member {
        Member::Unnamed(Index::from(index))
    }
}

impl Eq for Member {}

impl PartialEq for Member {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Member::Named(this), Member::Named(other)) => this == other,
            (Member::Unnamed(this), Member::Unnamed(other)) => this == other,
            _ => false,
        }
    }
}

impl Hash for Member {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            Member::Named(m) => m.hash(state),
            Member::Unnamed(m) => m.hash(state),
        }
    }
}

#[cfg(feature = "printing")]
impl IdentFragment for Member {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Member::Named(m) => Display::fmt(m, formatter),
            Member::Unnamed(m) => Display::fmt(&m.index, formatter),
        }
    }

    fn span(&self) -> Option<Span> {
        match self {
            Member::Named(m) => Some(m.span()),
            Member::Unnamed(m) => Some(m.span),
        }
    }
}

ast_struct! {
    /// The index of an unnamed tuple struct field.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct Index {
        pub index: u32,
        pub span: Span,
    }
}

impl From<usize> for Index {
    fn from(index: usize) -> Index {
        assert!(index < u32::MAX as usize);
        Index {
            index: index as u32,
            span: Span::call_site(),
        }
    }
}

impl Eq for Index {}

impl PartialEq for Index {
    fn eq(&self, other: &Self) -> bool {
        self.index == other.index
    }
}

impl Hash for Index {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.index.hash(state);
    }
}

#[cfg(feature = "printing")]
impl IdentFragment for Index {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(&self.index, formatter)
    }

    fn span(&self) -> Option<Span> {
        Some(self.span)
    }
}

ast_struct! {
    /// A field-value pair in a struct literal.
    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
    pub struct FieldValue {
        pub attrs: Vec<Attribute>,
        pub member: Member,

        /// The colon in `Struct { x: x }`. If written in shorthand like
        /// `Struct { x }`, there is no colon.
        pub colon_token: Option<Token![:]>,

        pub expr: Expr,
    }
}

#[cfg(feature = "full")]
ast_struct! {
    /// A lifetime labeling a `for`, `while`, or `loop`.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct Label {
        pub name: Lifetime,
        pub colon_token: Token![:],
    }
}

#[cfg(feature = "full")]
ast_struct! {
    /// One arm of a `match` expression: `0..=10 => { return true; }`.
    ///
    /// As in:
    ///
    /// ```
    /// # fn f() -> bool {
    /// #     let n = 0;
    /// match n {
    ///     0..=10 => {
    ///         return true;
    ///     }
    ///     // ...
    ///     # _ => {}
    /// }
    /// #   false
    /// # }
    /// ```
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub struct Arm {
        pub attrs: Vec<Attribute>,
        pub pat: Pat,
        pub guard: Option<(Token![if], Box<Expr>)>,
        pub fat_arrow_token: Token![=>],
        pub body: Box<Expr>,
        pub comma: Option<Token![,]>,
    }
}

#[cfg(feature = "full")]
ast_enum! {
    /// Limit types of a range, inclusive or exclusive.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub enum RangeLimits {
        /// Inclusive at the beginning, exclusive at the end.
        HalfOpen(Token![..]),
        /// Inclusive at the beginning and end.
        Closed(Token![..=]),
    }
}

#[cfg(feature = "full")]
ast_enum! {
    /// Mutability of a raw pointer (`*const T`, `*mut T`), in which non-mutable
    /// isn't the implicit default.
    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
    pub enum PointerMutability {
        Const(Token![const]),
        Mut(Token![mut]),
    }
}

#[cfg(feature = "parsing")]
pub(crate) mod parsing {
    #[cfg(feature = "full")]
    use crate::attr;
    use crate::attr::Attribute;
    #[cfg(feature = "full")]
    use crate::classify;
    use crate::error::{Error, Result};
    #[cfg(feature = "full")]
    use crate::expr::{
        Arm, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprClosure,
        ExprConst, ExprContinue, ExprForLoop, ExprIf, ExprInfer, ExprLet, ExprLoop, ExprMatch,
        ExprRange, ExprRawAddr, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprUnsafe,
        ExprWhile, ExprYield, Label, PointerMutability, RangeLimits,
    };
    use crate::expr::{
        Expr, ExprBinary, ExprCall, ExprCast, ExprField, ExprGroup, ExprIndex, ExprLit, ExprMacro,
        ExprMethodCall, ExprParen, ExprPath, ExprReference, ExprStruct, ExprTuple, ExprUnary,
        FieldValue, Index, Member,
    };
    #[cfg(feature = "full")]
    use crate::generics::BoundLifetimes;
    use crate::ident::Ident;
    #[cfg(feature = "full")]
    use crate::lifetime::Lifetime;
    use crate::lit::{Lit, LitFloat, LitInt};
    use crate::mac::{self, Macro};
    use crate::op::BinOp;
    use crate::parse::discouraged::Speculative as _;
    #[cfg(feature = "full")]
    use crate::parse::ParseBuffer;
    use crate::parse::{Parse, ParseStream};
    #[cfg(feature = "full")]
    use crate::pat::{Pat, PatType};
    use crate::path::{self, AngleBracketedGenericArguments, Path, QSelf};
    use crate::precedence::Precedence;
    use crate::punctuated::Punctuated;
    #[cfg(feature = "full")]
    use crate::stmt::Block;
    use crate::token;
    use crate::ty;
    #[cfg(feature = "full")]
    use crate::ty::{ReturnType, Type};
    use crate::verbatim;
    #[cfg(feature = "full")]
    use proc_macro2::TokenStream;
    use std::mem;

    // When we're parsing expressions which occur before blocks, like in an if
    // statement's condition, we cannot parse a struct literal.
    //
    // Struct literals are ambiguous in certain positions
    // https://github.com/rust-lang/rfcs/pull/92
    #[cfg(feature = "full")]
    pub(super) struct AllowStruct(pub bool);

    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
    impl Parse for Expr {
        fn parse(input: ParseStream) -> Result<Self> {
            ambiguous_expr(
                input,
                #[cfg(feature = "full")]
                AllowStruct(true),
            )
        }
    }

    #[cfg(feature = "full")]
    pub(super) fn parse_with_earlier_boundary_rule(input: ParseStream) -> Result<Expr> {
        let mut attrs = input.call(expr_attrs)?;
        let mut expr = if input.peek(token::Group) {
            let allow_struct = AllowStruct(true);
            let atom = expr_group(input, allow_struct)?;
            if continue_parsing_early(&atom) {
                trailer_helper(input, atom)?
            } else {
                atom
            }
        } else if input.peek(Token![if]) {
            Expr::If(input.parse()?)
        } else if input.peek(Token![while]) {
            Expr::While(input.parse()?)
        } else if input.peek(Token![for])
            && !(input.peek2(Token![<]) && (input.peek3(Lifetime) || input.peek3(Token![>])))
        {
            Expr::ForLoop(input.parse()?)
        } else if input.peek(Token![loop]) {
            Expr::Loop(input.parse()?)
        } else if input.peek(Token![match]) {
            Expr::Match(input.parse()?)
        } else if input.peek(Token![try]) && input.peek2(token::Brace) {
            Expr::TryBlock(input.parse()?)
        } else if input.peek(Token![unsafe]) {
            Expr::Unsafe(input.parse()?)
        } else if input.peek(Token![const]) && input.peek2(token::Brace) {
            Expr::Const(input.parse()?)
        } else if input.peek(token::Brace) {
            Expr::Block(input.parse()?)
        } else if input.peek(Lifetime) {
            atom_labeled(input)?
        } else {
            let allow_struct = AllowStruct(true);
            unary_expr(input, allow_struct)?
        };

        if continue_parsing_early(&expr) {
            attrs.extend(expr.replace_attrs(Vec::new()));
            expr.replace_attrs(attrs);

            let allow_struct = AllowStruct(true);
            return parse_expr(input, expr, allow_struct, Precedence::MIN);
        }

        if input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) {
            expr = trailer_helper(input, expr)?;

            attrs.extend(expr.replace_attrs(Vec::new()));
            expr.replace_attrs(attrs);

            let allow_struct = AllowStruct(true);
            return parse_expr(input, expr, allow_struct, Precedence::MIN);
        }

        attrs.extend(expr.replace_attrs(Vec::new()));
        expr.replace_attrs(attrs);
        Ok(expr)
    }

    #[cfg(feature = "full")]
    impl Copy for AllowStruct {}

    #[cfg(feature = "full")]
    impl Clone for AllowStruct {
        fn clone(&self) -> Self {
            *self
        }
    }

    #[cfg(feature = "full")]
    fn parse_expr(
        input: ParseStream,
        mut lhs: Expr,
        allow_struct: AllowStruct,
        base: Precedence,
    ) -> Result<Expr> {
        loop {
            let ahead = input.fork();
            if let Expr::Range(ExprRange { end: Some(_), .. }) = lhs {
                // A range with an upper bound cannot be the left-hand side of
                // another binary operator.
                break;
            } else if let Ok(op) = ahead.parse::<BinOp>() {
                let precedence = Precedence::of_binop(&op);
                if precedence < base {
                    break;
                }
                if precedence == Precedence::Compare {
                    if let Expr::Binary(lhs) = &lhs {
                        if Precedence::of_binop(&lhs.op) == Precedence::Compare {
                            return Err(input.error("comparison operators cannot be chained"));
                        }
                    }
                }
                input.advance_to(&ahead);
                let right = parse_binop_rhs(input, allow_struct, precedence)?;
                lhs = Expr::Binary(ExprBinary {
                    attrs: Vec::new(),
                    left: Box::new(lhs),
                    op,
                    right,
                });
            } else if Precedence::Assign >= base && input.peek(Token![=]) && !input.peek(Token![=>])
            {
                let eq_token: Token![=] = input.parse()?;
                let right = parse_binop_rhs(input, allow_struct, Precedence::Assign)?;
                lhs = Expr::Assign(ExprAssign {
                    attrs: Vec::new(),
                    left: Box::new(lhs),
                    eq_token,
                    right,
                });
            } else if Precedence::Range >= base && input.peek(Token![..]) {
                let limits: RangeLimits = input.parse()?;
                let end = parse_range_end(input, &limits, allow_struct)?;
                lhs = Expr::Range(ExprRange {
                    attrs: Vec::new(),
                    start: Some(Box::new(lhs)),
                    limits,
                    end,
                });
            } else if Precedence::Cast >= base && input.peek(Token![as]) {
                let as_token: Token![as] = input.parse()?;
                let allow_plus = false;
                let allow_group_generic = false;
                let ty = ty::parsing::ambig_ty(input, allow_plus, allow_group_generic)?;
                check_cast(input)?;
                lhs = Expr::Cast(ExprCast {
                    attrs: Vec::new(),
                    expr: Box::new(lhs),
                    as_token,
                    ty: Box::new(ty),
                });
            } else {
                break;
            }
        }
        Ok(lhs)
    }

    #[cfg(not(feature = "full"))]
    fn parse_expr(input: ParseStream, mut lhs: Expr, base: Precedence) -> Result<Expr> {
        loop {
            let ahead = input.fork();
            if let Ok(op) = ahead.parse::<BinOp>() {
                let precedence = Precedence::of_binop(&op);
                if precedence < base {
                    break;
                }
                if precedence == Precedence::Compare {
                    if let Expr::Binary(lhs) = &lhs {
                        if Precedence::of_binop(&lhs.op) == Precedence::Compare {
                            return Err(input.error("comparison operators cannot be chained"));
                        }
                    }
                }
                input.advance_to(&ahead);
                let right = parse_binop_rhs(input, precedence)?;
                lhs = Expr::Binary(ExprBinary {
                    attrs: Vec::new(),
                    left: Box::new(lhs),
                    op,
                    right,
                });
            } else if Precedence::Cast >= base && input.peek(Token![as]) {
                let as_token: Token![as] = input.parse()?;
                let allow_plus = false;
                let allow_group_generic = false;
                let ty = ty::parsing::ambig_ty(input, allow_plus, allow_group_generic)?;
                check_cast(input)?;
                lhs = Expr::Cast(ExprCast {
                    attrs: Vec::new(),
                    expr: Box::new(lhs),
                    as_token,
                    ty: Box::new(ty),
                });
            } else {
                break;
            }
        }
        Ok(lhs)
    }

    fn parse_binop_rhs(
        input: ParseStream,
        #[cfg(feature = "full")] allow_struct: AllowStruct,
        precedence: Precedence,
    ) -> Result<Box<Expr>> {
        let mut rhs = unary_expr(
            input,
            #[cfg(feature = "full")]
            allow_struct,
        )?;
        loop {
            let next = peek_precedence(input);
            if next > precedence || next == precedence && precedence == Precedence::Assign {
                let cursor = input.cursor();
                rhs = parse_expr(
                    input,
                    rhs,
                    #[cfg(feature = "full")]
                    allow_struct,
                    next,
                )?;
                if cursor == input.cursor() {
                    // Bespoke grammar restrictions separate from precedence can
                    // cause parsing to not advance, such as `..a` being
                    // disallowed in the left-hand side of binary operators,
                    // even ones that have lower precedence than `..`.
                    break;
                }
            } else {
                break;
            }
        }
        Ok(Box::new(rhs))
    }

    fn peek_precedence(input: ParseStream) -> Precedence {
        if let Ok(op) = input.fork().parse() {
            Precedence::of_binop(&op)
        } else if input.peek(Token![=]) && !input.peek(Token![=>]) {
            Precedence::Assign
        } else if input.peek(Token![..]) {
            Precedence::Range
        } else if input.peek(Token![as]) {
            Precedence::Cast
        } else {
            Precedence::MIN
        }
    }

    // Parse an arbitrary expression.
    pub(super) fn ambiguous_expr(
        input: ParseStream,
        #[cfg(feature = "full")] allow_struct: AllowStruct,
    ) -> Result<Expr> {
        let lhs = unary_expr(
            input,
            #[cfg(feature = "full")]
            allow_struct,
        )?;
        parse_expr(
            input,
            lhs,
            #[cfg(feature = "full")]
            allow_struct,
            Precedence::MIN,
        )
    }

    #[cfg(feature = "full")]
    fn expr_attrs(input: ParseStream) -> Result<Vec<Attribute>> {
        let mut attrs = Vec::new();
        while !input.peek(token::Group) && input.peek(Token![#]) {
            attrs.push(input.call(attr::parsing::single_parse_outer)?);
        }
        Ok(attrs)
    }

    // <UnOp> <trailer>
    // & <trailer>
    // &mut <trailer>
    // box <trailer>
    #[cfg(feature = "full")]
    fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
        let begin = input.fork();
        let attrs = input.call(expr_attrs)?;
        if input.peek(token::Group) {
            return trailer_expr(begin, attrs, input, allow_struct);
        }

        if input.peek(Token![&]) {
            let and_token: Token![&] = input.parse()?;
            let raw: Option<Token![raw]> = if input.peek(Token![raw])
                && (input.peek2(Token![mut]) || input.peek2(Token![const]))
            {
                Some(input.parse()?)
            } else {
                None
            };
            let mutability: Option<Token![mut]> = input.parse()?;
            let const_token: Option<Token![const]> = if raw.is_some() && mutability.is_none() {
                Some(input.parse()?)
            } else {
                None
            };
            let expr = Box::new(unary_expr(input, allow_struct)?);
            if let Some(raw) = raw {
                Ok(Expr::RawAddr(ExprRawAddr {
                    attrs,
                    and_token,
                    raw,
                    mutability: match mutability {
                        Some(mut_token) => PointerMutability::Mut(mut_token),
                        None => PointerMutability::Const(const_token.unwrap()),
                    },
                    expr,
                }))
            } else {
                Ok(Expr::Reference(ExprReference {
                    attrs,
                    and_token,
                    mutability,
                    expr,
                }))
            }
        } else if input.peek(Token![*]) || input.peek(Token![!]) || input.peek(Token![-]) {
            expr_unary(input, attrs, allow_struct).map(Expr::Unary)
        } else {
            trailer_expr(begin, attrs, input, allow_struct)
        }
    }

    #[cfg(not(feature = "full"))]
    fn unary_expr(input: ParseStream) -> Result<Expr> {
        if input.peek(Token![&]) {
            Ok(Expr::Reference(ExprReference {
                attrs: Vec::new(),
                and_token: input.parse()?,
                mutability: input.parse()?,
                expr: Box::new(unary_expr(input)?),
            }))
        } else if input.peek(Token![*]) || input.peek(Token![!]) || input.peek(Token![-]) {
            Ok(Expr::Unary(ExprUnary {
                attrs: Vec::new(),
                op: input.parse()?,
                expr: Box::new(unary_expr(input)?),
            }))
        } else {
            trailer_expr(input)
        }
    }

    // <atom> (..<args>) ...
    // <atom> . <ident> (..<args>) ...
    // <atom> . <ident> ...
    // <atom> . <lit> ...
    // <atom> [ <expr> ] ...
    // <atom> ? ...
    #[cfg(feature = "full")]
    fn trailer_expr(
        begin: ParseBuffer,
        mut attrs: Vec<Attribute>,
        input: ParseStream,
        allow_struct: AllowStruct,
    ) -> Result<Expr> {
        let atom = atom_expr(input, allow_struct)?;
        let mut e = trailer_helper(input, atom)?;

        if let Expr::Verbatim(tokens) = &mut e {
            *tokens = verbatim::between(&begin, input);
        } else {
            let inner_attrs = e.replace_attrs(Vec::new());
            attrs.extend(inner_attrs);
            e.replace_attrs(attrs);
        }

        Ok(e)
    }

    #[cfg(feature = "full")]
    fn trailer_helper(input: ParseStream, mut e: Expr) -> Result<Expr> {
        loop {
            if input.peek(token::Paren) {
                let content;
                e = Expr::Call(ExprCall {
                    attrs: Vec::new(),
                    func: Box::new(e),
                    paren_token: parenthesized!(content in input),
                    args: content.parse_terminated(Expr::parse, Token![,])?,
                });
            } else if input.peek(Token![.])
                && !input.peek(Token![..])
                && match e {
                    Expr::Range(_) => false,
                    _ => true,
                }
            {
                let mut dot_token: Token![.] = input.parse()?;

                let float_token: Option<LitFloat> = input.parse()?;
                if let Some(float_token) = float_token {
                    if multi_index(&mut e, &mut dot_token, float_token)? {
                        continue;
                    }
                }

                let await_token: Option<Token![await]> = input.parse()?;
                if let Some(await_token) = await_token {
                    e = Expr::Await(ExprAwait {
                        attrs: Vec::new(),
                        base: Box::new(e),
                        dot_token,
                        await_token,
                    });
                    continue;
                }

                let member: Member = input.parse()?;
                let turbofish = if member.is_named() && input.peek(Token![::]) {
                    Some(AngleBracketedGenericArguments::parse_turbofish(input)?)
                } else {
                    None
                };

                if turbofish.is_some() || input.peek(token::Paren) {
                    if let Member::Named(method) = member {
                        let content;
                        e = Expr::MethodCall(ExprMethodCall {
                            attrs: Vec::new(),
                            receiver: Box::new(e),
                            dot_token,
                            method,
                            turbofish,
                            paren_token: parenthesized!(content in input),
                            args: content.parse_terminated(Expr::parse, Token![,])?,
                        });
                        continue;
                    }
                }

                e = Expr::Field(ExprField {
                    attrs: Vec::new(),
                    base: Box::new(e),
                    dot_token,
                    member,
                });
            } else if input.peek(token::Bracket) {
                let content;
                e = Expr::Index(ExprIndex {
                    attrs: Vec::new(),
                    expr: Box::new(e),
                    bracket_token: bracketed!(content in input),
                    index: content.parse()?,
                });
            } else if input.peek(Token![?]) {
                e = Expr::Try(ExprTry {
                    attrs: Vec::new(),
                    expr: Box::new(e),
                    question_token: input.parse()?,
                });
            } else {
                break;
            }
        }
        Ok(e)
    }

    #[cfg(not(feature = "full"))]
    fn trailer_expr(input: ParseStream) -> Result<Expr> {
        let mut e = atom_expr(input)?;

        loop {
            if input.peek(token::Paren) {
                let content;
                e = Expr::Call(ExprCall {
                    attrs: Vec::new(),
                    func: Box::new(e),
                    paren_token: parenthesized!(content in input),
                    args: content.parse_terminated(Expr::parse, Token![,])?,
                });
            } else if input.peek(Token![.])
                && !input.peek(Token![..])
                && !input.peek2(Token![await])
            {
                let mut dot_token: Token![.] = input.parse()?;

                let float_token: Option<LitFloat> = input.parse()?;
                if let Some(float_token) = float_token {
                    if multi_index(&mut e, &mut dot_token, float_token)? {
                        continue;
                    }
                }

                let member: Member = input.parse()?;
                let turbofish = if member.is_named() && input.peek(Token![::]) {
                    let colon2_token: Token![::] = input.parse()?;
                    let turbofish =
                        AngleBracketedGenericArguments::do_parse(Some(colon2_token), input)?;
                    Some(turbofish)
                } else {
                    None
                };

                if turbofish.is_some() || input.peek(token::Paren) {
                    if let Member::Named(method) = member {
                        let content;
                        e = Expr::MethodCall(ExprMethodCall {
                            attrs: Vec::new(),
                            receiver: Box::new(e),
                            dot_token,
                            method,
                            turbofish,
                            paren_token: parenthesized!(content in input),
                            args: content.parse_terminated(Expr::parse, Token![,])?,
                        });
                        continue;
                    }
                }

                e = Expr::Field(ExprField {
                    attrs: Vec::new(),
                    base: Box::new(e),
                    dot_token,
                    member,
                });
            } else if input.peek(token::Bracket) {
                let content;
                e = Expr::Index(ExprIndex {
                    attrs: Vec::new(),
                    expr: Box::new(e),
                    bracket_token: bracketed!(content in input),
                    index: content.parse()?,
                });
            } else {
                break;
            }
        }

        Ok(e)
    }

    // Parse all atomic expressions which don't have to worry about precedence
    // interactions, as they are fully contained.
    #[cfg(feature = "full")]
    fn atom_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
        if input.peek(token::Group) {
            expr_group(input, allow_struct)
        } else if input.peek(Lit) {
            input.parse().map(Expr::Lit)
        } else if input.peek(Token![async])
            && (input.peek2(token::Brace) || input.peek2(Token![move]) && input.peek3(token::Brace))
        {
            input.parse().map(Expr::Async)
        } else if input.peek(Token![try]) && input.peek2(token::Brace) {
            input.parse().map(Expr::TryBlock)
        } else if input.peek(Token![|])
            || input.peek(Token![move])
            || input.peek(Token![for])
                && input.peek2(Token![<])
                && (input.peek3(Lifetime) || input.peek3(Token![>]))
            || input.peek(Token![const]) && !input.peek2(token::Brace)
            || input.peek(Token![static])
            || input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))
        {
            expr_closure(input, allow_struct).map(Expr::Closure)
        } else if token::parsing::peek_keyword(input.cursor(), "builtin") && input.peek2(Token![#])
        {
            expr_builtin(input)
        } else if input.peek(Ident)
            || input.peek(Token![::])
            || input.peek(Token![<])
            || input.peek(Token![self])
            || input.peek(Token![Self])
            || input.peek(Token![super])
            || input.peek(Token![crate])
            || input.peek(Token![try]) && (input.peek2(Token![!]) || input.peek2(Token![::]))
        {
            path_or_macro_or_struct(input, allow_struct)
        } else if input.peek(token::Paren) {
            paren_or_tuple(input)
        } else if input.peek(Token![break]) {
            expr_break(input, allow_struct).map(Expr::Break)
        } else if input.peek(Token![continue]) {
            input.parse().map(Expr::Continue)
        } else if input.peek(Token![return]) {
            input.parse().map(Expr::Return)
        } else if input.peek(Token![become]) {
            expr_become(input)
        } else if input.peek(token::Bracket) {
            array_or_repeat(input)
        } else if input.peek(Token![let]) {
            expr_let(input, allow_struct).map(Expr::Let)
        } else if input.peek(Token![if]) {
            input.parse().map(Expr::If)
        } else if input.peek(Token![while]) {
            input.parse().map(Expr::While)
        } else if input.peek(Token![for]) {
            input.parse().map(Expr::ForLoop)
        } else if input.peek(Token![loop]) {
            input.parse().map(Expr::Loop)
        } else if input.peek(Token![match]) {
            input.parse().map(Expr::Match)
        } else if input.peek(Token![yield]) {
            input.parse().map(Expr::Yield)
        } else if input.peek(Token![unsafe]) {
            input.parse().map(Expr::Unsafe)
        } else if input.peek(Token![const]) {
            input.parse().map(Expr::Const)
        } else if input.peek(token::Brace) {
            input.parse().map(Expr::Block)
        } else if input.peek(Token![..]) {
            expr_range(input, allow_struct).map(Expr::Range)
        } else if input.peek(Token![_]) {
            input.parse().map(Expr::Infer)
        } else if input.peek(Lifetime) {
            atom_labeled(input)
        } else {
            Err(input.error("expected an expression"))
        }
    }

    #[cfg(feature = "full")]
    fn atom_labeled(input: ParseStream) -> Result<Expr> {
        let the_label: Label = input.parse()?;
        let mut expr = if input.peek(Token![while]) {
            Expr::While(input.parse()?)
        } else if input.peek(Token![for]) {
            Expr::ForLoop(input.parse()?)
        } else if input.peek(Token![loop]) {
            Expr::Loop(input.parse()?)
        } else if input.peek(token::Brace) {
            Expr::Block(input.parse()?)
        } else {
            return Err(input.error("expected loop or block expression"));
        };
        match &mut expr {
            Expr::While(ExprWhile { label, .. })
            | Expr::ForLoop(ExprForLoop { label, .. })
            | Expr::Loop(ExprLoop { label, .. })
            | Expr::Block(ExprBlock { label, .. }) => *label = Some(the_label),
            _ => unreachable!(),
        }
        Ok(expr)
    }

    #[cfg(not(feature = "full"))]
    fn atom_expr(input: ParseStream) -> Result<Expr> {
        if input.peek(token::Group) {
            expr_group(input)
        } else if input.peek(Lit) {
            input.parse().map(Expr::Lit)
        } else if input.peek(token::Paren) {
            paren_or_tuple(input)
        } else if input.peek(Ident)
            || input.peek(Token![::])
            || input.peek(Token![<])
            || input.peek(Token![self])
            || input.peek(Token![Self])
            || input.peek(Token![super])
            || input.peek(Token![crate])
        {
            path_or_macro_or_struct(input)
        } else if input.is_empty() {
            Err(input.error("expected an expression"))
        } else {
            if input.peek(token::Brace) {
                let scan = input.fork();
                let content;
                braced!(content in scan);
                if content.parse::<Expr>().is_ok() && content.is_empty() {
                    let expr_block = verbatim::between(input, &scan);
                    input.advance_to(&scan);
                    return Ok(Expr::Verbatim(expr_block));
                }
            }
            Err(input.error("unsupported expression; enable syn's features=[\"full\"]"))
        }
    }

    #[cfg(feature = "full")]
    fn expr_builtin(input: ParseStream) -> Result<Expr> {
        let begin = input.fork();

        token::parsing::keyword(input, "builtin")?;
        input.parse::<Token![#]>()?;
        input.parse::<Ident>()?;

        let args;
        parenthesized!(args in input);
        args.parse::<TokenStream>()?;

        Ok(Expr::Verbatim(verbatim::between(&begin, input)))
    }

    fn path_or_macro_or_struct(
        input: ParseStream,
        #[cfg(feature = "full")] allow_struct: AllowStruct,
    ) -> Result<Expr> {
        let expr_style = true;
        let (qself, path) = path::parsing::qpath(input, expr_style)?;
        rest_of_path_or_macro_or_struct(
            qself,
            path,
            input,
            #[cfg(feature = "full")]
            allow_struct,
        )
    }

    fn rest_of_path_or_macro_or_struct(
        qself: Option<QSelf>,
        path: Path,
        input: ParseStream,
        #[cfg(feature = "full")] allow_struct: AllowStruct,
    ) -> Result<Expr> {
        if qself.is_none()
            && input.peek(Token![!])
            && !input.peek(Token![!=])
            && path.is_mod_style()
        {
            let bang_token: Token![!] = input.parse()?;
            let (delimiter, tokens) = mac::parse_delimiter(input)?;
            return Ok(Expr::Macro(ExprMacro {
                attrs: Vec::new(),
                mac: Macro {
                    path,
                    bang_token,
                    delimiter,
                    tokens,
                },
            }));
        }

        #[cfg(not(feature = "full"))]
        let allow_struct = (true,);
        if allow_struct.0 && input.peek(token::Brace) {
            return expr_struct_helper(input, qself, path).map(Expr::Struct);
        }

        Ok(Expr::Path(ExprPath {
            attrs: Vec::new(),
            qself,
            path,
        }))
    }

    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
    impl Parse for ExprMacro {
        fn parse(input: ParseStream) -> Result<Self> {
            Ok(ExprMacro {
                attrs: Vec::new(),
                mac: input.parse()?,
            })
        }
    }

    fn paren_or_tuple(input: ParseStream) -> Result<Expr> {
        let content;
--> --------------------

--> maximum size reached

--> --------------------

[ zur Elbe Produktseite wechseln0.51Quellennavigators  Analyse erneut starten  ]