Source code
Revision control
Copy as Markdown
Other Tools
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
#[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) {