mirror of https://github.com/torvalds/linux.git
4176 lines
138 KiB
Rust
4176 lines
138 KiB
Rust
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
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) && !input.peek(Token![as]) // 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),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(feature = "parsing", feature = "printing"))]
|
|
impl Member {
|
|
pub(crate) fn is_named(&self) -> bool {
|
|
match self {
|
|
Member::Named(_) => true,
|
|
Member::Unnamed(_) => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
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::{self, 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::{Span, 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])
|
|
&& !generics::parsing::choose_generics_over_qpath_after_keyword(input)
|
|
{
|
|
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(_) = lhs {
|
|
// A range 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::Assign {
|
|
if let Expr::Range(_) = lhs {
|
|
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![=>])
|
|
&& match lhs {
|
|
Expr::Range(_) => false,
|
|
_ => true,
|
|
}
|
|
{
|
|
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 if !attrs.is_empty() {
|
|
if let Expr::Range(range) = e {
|
|
let spans: &[Span] = match &range.limits {
|
|
RangeLimits::HalfOpen(limits) => &limits.spans,
|
|
RangeLimits::Closed(limits) => &limits.spans,
|
|
};
|
|
return Err(crate::error::new2(
|
|
spans[0],
|
|
*spans.last().unwrap(),
|
|
"attributes are not allowed on range expressions starting with `..`",
|
|
));
|
|
}
|
|
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![?])
|
|
&& match e {
|
|
Expr::Range(_) => false,
|
|
_ => true,
|
|
}
|
|
{
|
|
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])
|
|
&& generics::parsing::choose_generics_over_qpath_after_keyword(input)
|
|
|| 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;
|
|
let paren_token = parenthesized!(content in input);
|
|
if content.is_empty() {
|
|
return Ok(Expr::Tuple(ExprTuple {
|
|
attrs: Vec::new(),
|
|
paren_token,
|
|
elems: Punctuated::new(),
|
|
}));
|
|
}
|
|
|
|
let first: Expr = content.parse()?;
|
|
if content.is_empty() {
|
|
return Ok(Expr::Paren(ExprParen {
|
|
attrs: Vec::new(),
|
|
paren_token,
|
|
expr: Box::new(first),
|
|
}));
|
|
}
|
|
|
|
let mut elems = Punctuated::new();
|
|
elems.push_value(first);
|
|
while !content.is_empty() {
|
|
let punct = content.parse()?;
|
|
elems.push_punct(punct);
|
|
if content.is_empty() {
|
|
break;
|
|
}
|
|
let value = content.parse()?;
|
|
elems.push_value(value);
|
|
}
|
|
Ok(Expr::Tuple(ExprTuple {
|
|
attrs: Vec::new(),
|
|
paren_token,
|
|
elems,
|
|
}))
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn array_or_repeat(input: ParseStream) -> Result<Expr> {
|
|
let content;
|
|
let bracket_token = bracketed!(content in input);
|
|
if content.is_empty() {
|
|
return Ok(Expr::Array(ExprArray {
|
|
attrs: Vec::new(),
|
|
bracket_token,
|
|
elems: Punctuated::new(),
|
|
}));
|
|
}
|
|
|
|
let first: Expr = content.parse()?;
|
|
if content.is_empty() || content.peek(Token![,]) {
|
|
let mut elems = Punctuated::new();
|
|
elems.push_value(first);
|
|
while !content.is_empty() {
|
|
let punct = content.parse()?;
|
|
elems.push_punct(punct);
|
|
if content.is_empty() {
|
|
break;
|
|
}
|
|
let value = content.parse()?;
|
|
elems.push_value(value);
|
|
}
|
|
Ok(Expr::Array(ExprArray {
|
|
attrs: Vec::new(),
|
|
bracket_token,
|
|
elems,
|
|
}))
|
|
} else if content.peek(Token![;]) {
|
|
let semi_token: Token![;] = content.parse()?;
|
|
let len: Expr = content.parse()?;
|
|
Ok(Expr::Repeat(ExprRepeat {
|
|
attrs: Vec::new(),
|
|
bracket_token,
|
|
expr: Box::new(first),
|
|
semi_token,
|
|
len: Box::new(len),
|
|
}))
|
|
} else {
|
|
Err(content.error("expected `,` or `;`"))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprArray {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let content;
|
|
let bracket_token = bracketed!(content in input);
|
|
let mut elems = Punctuated::new();
|
|
|
|
while !content.is_empty() {
|
|
let first: Expr = content.parse()?;
|
|
elems.push_value(first);
|
|
if content.is_empty() {
|
|
break;
|
|
}
|
|
let punct = content.parse()?;
|
|
elems.push_punct(punct);
|
|
}
|
|
|
|
Ok(ExprArray {
|
|
attrs: Vec::new(),
|
|
bracket_token,
|
|
elems,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprRepeat {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let content;
|
|
Ok(ExprRepeat {
|
|
bracket_token: bracketed!(content in input),
|
|
attrs: Vec::new(),
|
|
expr: content.parse()?,
|
|
semi_token: content.parse()?,
|
|
len: content.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn continue_parsing_early(mut expr: &Expr) -> bool {
|
|
while let Expr::Group(group) = expr {
|
|
expr = &group.expr;
|
|
}
|
|
match expr {
|
|
Expr::If(_)
|
|
| Expr::While(_)
|
|
| Expr::ForLoop(_)
|
|
| Expr::Loop(_)
|
|
| Expr::Match(_)
|
|
| Expr::TryBlock(_)
|
|
| Expr::Unsafe(_)
|
|
| Expr::Const(_)
|
|
| Expr::Block(_) => false,
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprLit {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprLit {
|
|
attrs: Vec::new(),
|
|
lit: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn expr_group(
|
|
input: ParseStream,
|
|
#[cfg(feature = "full")] allow_struct: AllowStruct,
|
|
) -> Result<Expr> {
|
|
let group = crate::group::parse_group(input)?;
|
|
let mut inner: Expr = group.content.parse()?;
|
|
|
|
match inner {
|
|
Expr::Path(mut expr) if expr.attrs.is_empty() => {
|
|
let grouped_len = expr.path.segments.len();
|
|
Path::parse_rest(input, &mut expr.path, true)?;
|
|
match rest_of_path_or_macro_or_struct(
|
|
expr.qself,
|
|
expr.path,
|
|
input,
|
|
#[cfg(feature = "full")]
|
|
allow_struct,
|
|
)? {
|
|
Expr::Path(expr) if expr.path.segments.len() == grouped_len => {
|
|
inner = Expr::Path(expr);
|
|
}
|
|
extended => return Ok(extended),
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Ok(Expr::Group(ExprGroup {
|
|
attrs: Vec::new(),
|
|
group_token: group.token,
|
|
expr: Box::new(inner),
|
|
}))
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprParen {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let content;
|
|
Ok(ExprParen {
|
|
attrs: Vec::new(),
|
|
paren_token: parenthesized!(content in input),
|
|
expr: content.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprLet {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_struct = AllowStruct(true);
|
|
expr_let(input, allow_struct)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_let(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprLet> {
|
|
Ok(ExprLet {
|
|
attrs: Vec::new(),
|
|
let_token: input.parse()?,
|
|
pat: Box::new(Pat::parse_multi_with_leading_vert(input)?),
|
|
eq_token: input.parse()?,
|
|
expr: Box::new({
|
|
let lhs = unary_expr(input, allow_struct)?;
|
|
parse_expr(input, lhs, allow_struct, Precedence::Compare)?
|
|
}),
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprIf {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
|
|
let mut clauses = Vec::new();
|
|
let mut expr;
|
|
loop {
|
|
let if_token: Token![if] = input.parse()?;
|
|
let cond = input.call(Expr::parse_without_eager_brace)?;
|
|
let then_branch: Block = input.parse()?;
|
|
|
|
expr = ExprIf {
|
|
attrs: Vec::new(),
|
|
if_token,
|
|
cond: Box::new(cond),
|
|
then_branch,
|
|
else_branch: None,
|
|
};
|
|
|
|
if !input.peek(Token![else]) {
|
|
break;
|
|
}
|
|
|
|
let else_token: Token![else] = input.parse()?;
|
|
let lookahead = input.lookahead1();
|
|
if lookahead.peek(Token![if]) {
|
|
expr.else_branch = Some((else_token, Box::new(Expr::PLACEHOLDER)));
|
|
clauses.push(expr);
|
|
} else if lookahead.peek(token::Brace) {
|
|
expr.else_branch = Some((
|
|
else_token,
|
|
Box::new(Expr::Block(ExprBlock {
|
|
attrs: Vec::new(),
|
|
label: None,
|
|
block: input.parse()?,
|
|
})),
|
|
));
|
|
break;
|
|
} else {
|
|
return Err(lookahead.error());
|
|
}
|
|
}
|
|
|
|
while let Some(mut prev) = clauses.pop() {
|
|
*prev.else_branch.as_mut().unwrap().1 = Expr::If(expr);
|
|
expr = prev;
|
|
}
|
|
expr.attrs = attrs;
|
|
Ok(expr)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprInfer {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprInfer {
|
|
attrs: input.call(Attribute::parse_outer)?,
|
|
underscore_token: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprForLoop {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut attrs = input.call(Attribute::parse_outer)?;
|
|
let label: Option<Label> = input.parse()?;
|
|
let for_token: Token![for] = input.parse()?;
|
|
|
|
let pat = Pat::parse_multi_with_leading_vert(input)?;
|
|
|
|
let in_token: Token![in] = input.parse()?;
|
|
let expr: Expr = input.call(Expr::parse_without_eager_brace)?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
attr::parsing::parse_inner(&content, &mut attrs)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprForLoop {
|
|
attrs,
|
|
label,
|
|
for_token,
|
|
pat: Box::new(pat),
|
|
in_token,
|
|
expr: Box::new(expr),
|
|
body: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprLoop {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut attrs = input.call(Attribute::parse_outer)?;
|
|
let label: Option<Label> = input.parse()?;
|
|
let loop_token: Token![loop] = input.parse()?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
attr::parsing::parse_inner(&content, &mut attrs)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprLoop {
|
|
attrs,
|
|
label,
|
|
loop_token,
|
|
body: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprMatch {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut attrs = input.call(Attribute::parse_outer)?;
|
|
let match_token: Token![match] = input.parse()?;
|
|
let expr = Expr::parse_without_eager_brace(input)?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
attr::parsing::parse_inner(&content, &mut attrs)?;
|
|
|
|
let arms = Arm::parse_multiple(&content)?;
|
|
|
|
Ok(ExprMatch {
|
|
attrs,
|
|
match_token,
|
|
expr: Box::new(expr),
|
|
brace_token,
|
|
arms,
|
|
})
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_by_parsing_expr {
|
|
(
|
|
$(
|
|
$expr_type:ty, $variant:ident, $msg:expr,
|
|
)*
|
|
) => {
|
|
$(
|
|
#[cfg(all(feature = "full", feature = "printing"))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for $expr_type {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut expr: Expr = input.parse()?;
|
|
loop {
|
|
match expr {
|
|
Expr::$variant(inner) => return Ok(inner),
|
|
Expr::Group(next) => expr = *next.expr,
|
|
_ => return Err(Error::new_spanned(expr, $msg)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_by_parsing_expr! {
|
|
ExprAssign, Assign, "expected assignment expression",
|
|
ExprAwait, Await, "expected await expression",
|
|
ExprBinary, Binary, "expected binary operation",
|
|
ExprCall, Call, "expected function call expression",
|
|
ExprCast, Cast, "expected cast expression",
|
|
ExprField, Field, "expected struct field access",
|
|
ExprIndex, Index, "expected indexing expression",
|
|
ExprMethodCall, MethodCall, "expected method call expression",
|
|
ExprRange, Range, "expected range expression",
|
|
ExprTry, Try, "expected try expression",
|
|
ExprTuple, Tuple, "expected tuple expression",
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprUnary {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let attrs = Vec::new();
|
|
let allow_struct = AllowStruct(true);
|
|
expr_unary(input, attrs, allow_struct)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_unary(
|
|
input: ParseStream,
|
|
attrs: Vec<Attribute>,
|
|
allow_struct: AllowStruct,
|
|
) -> Result<ExprUnary> {
|
|
Ok(ExprUnary {
|
|
attrs,
|
|
op: input.parse()?,
|
|
expr: Box::new(unary_expr(input, allow_struct)?),
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprClosure {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_struct = AllowStruct(true);
|
|
expr_closure(input, allow_struct)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprRawAddr {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_struct = AllowStruct(true);
|
|
Ok(ExprRawAddr {
|
|
attrs: Vec::new(),
|
|
and_token: input.parse()?,
|
|
raw: input.parse()?,
|
|
mutability: input.parse()?,
|
|
expr: Box::new(unary_expr(input, allow_struct)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprReference {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_struct = AllowStruct(true);
|
|
Ok(ExprReference {
|
|
attrs: Vec::new(),
|
|
and_token: input.parse()?,
|
|
mutability: input.parse()?,
|
|
expr: Box::new(unary_expr(input, allow_struct)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprBreak {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_struct = AllowStruct(true);
|
|
expr_break(input, allow_struct)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprReturn {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprReturn {
|
|
attrs: Vec::new(),
|
|
return_token: input.parse()?,
|
|
expr: {
|
|
if Expr::peek(input) {
|
|
Some(input.parse()?)
|
|
} else {
|
|
None
|
|
}
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_become(input: ParseStream) -> Result<Expr> {
|
|
let begin = input.fork();
|
|
input.parse::<Token![become]>()?;
|
|
input.parse::<Expr>()?;
|
|
Ok(Expr::Verbatim(verbatim::between(&begin, input)))
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprTryBlock {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprTryBlock {
|
|
attrs: Vec::new(),
|
|
try_token: input.parse()?,
|
|
block: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprYield {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprYield {
|
|
attrs: Vec::new(),
|
|
yield_token: input.parse()?,
|
|
expr: {
|
|
if Expr::peek(input) {
|
|
Some(input.parse()?)
|
|
} else {
|
|
None
|
|
}
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_closure(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprClosure> {
|
|
let lifetimes: Option<BoundLifetimes> = input.parse()?;
|
|
let constness: Option<Token![const]> = input.parse()?;
|
|
let movability: Option<Token![static]> = input.parse()?;
|
|
let asyncness: Option<Token![async]> = input.parse()?;
|
|
let capture: Option<Token![move]> = input.parse()?;
|
|
let or1_token: Token![|] = input.parse()?;
|
|
|
|
let mut inputs = Punctuated::new();
|
|
loop {
|
|
if input.peek(Token![|]) {
|
|
break;
|
|
}
|
|
let value = closure_arg(input)?;
|
|
inputs.push_value(value);
|
|
if input.peek(Token![|]) {
|
|
break;
|
|
}
|
|
let punct: Token![,] = input.parse()?;
|
|
inputs.push_punct(punct);
|
|
}
|
|
|
|
let or2_token: Token![|] = input.parse()?;
|
|
|
|
let (output, body) = if input.peek(Token![->]) {
|
|
let arrow_token: Token![->] = input.parse()?;
|
|
let ty: Type = input.parse()?;
|
|
let body: Block = input.parse()?;
|
|
let output = ReturnType::Type(arrow_token, Box::new(ty));
|
|
let block = Expr::Block(ExprBlock {
|
|
attrs: Vec::new(),
|
|
label: None,
|
|
block: body,
|
|
});
|
|
(output, block)
|
|
} else {
|
|
let body = ambiguous_expr(input, allow_struct)?;
|
|
(ReturnType::Default, body)
|
|
};
|
|
|
|
Ok(ExprClosure {
|
|
attrs: Vec::new(),
|
|
lifetimes,
|
|
constness,
|
|
movability,
|
|
asyncness,
|
|
capture,
|
|
or1_token,
|
|
inputs,
|
|
or2_token,
|
|
output,
|
|
body: Box::new(body),
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprAsync {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprAsync {
|
|
attrs: Vec::new(),
|
|
async_token: input.parse()?,
|
|
capture: input.parse()?,
|
|
block: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn closure_arg(input: ParseStream) -> Result<Pat> {
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
let mut pat = Pat::parse_single(input)?;
|
|
|
|
if input.peek(Token![:]) {
|
|
Ok(Pat::Type(PatType {
|
|
attrs,
|
|
pat: Box::new(pat),
|
|
colon_token: input.parse()?,
|
|
ty: input.parse()?,
|
|
}))
|
|
} else {
|
|
match &mut pat {
|
|
Pat::Const(pat) => pat.attrs = attrs,
|
|
Pat::Ident(pat) => pat.attrs = attrs,
|
|
Pat::Lit(pat) => pat.attrs = attrs,
|
|
Pat::Macro(pat) => pat.attrs = attrs,
|
|
Pat::Or(pat) => pat.attrs = attrs,
|
|
Pat::Paren(pat) => pat.attrs = attrs,
|
|
Pat::Path(pat) => pat.attrs = attrs,
|
|
Pat::Range(pat) => pat.attrs = attrs,
|
|
Pat::Reference(pat) => pat.attrs = attrs,
|
|
Pat::Rest(pat) => pat.attrs = attrs,
|
|
Pat::Slice(pat) => pat.attrs = attrs,
|
|
Pat::Struct(pat) => pat.attrs = attrs,
|
|
Pat::Tuple(pat) => pat.attrs = attrs,
|
|
Pat::TupleStruct(pat) => pat.attrs = attrs,
|
|
Pat::Type(_) => unreachable!(),
|
|
Pat::Verbatim(_) => {}
|
|
Pat::Wild(pat) => pat.attrs = attrs,
|
|
}
|
|
Ok(pat)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprWhile {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut attrs = input.call(Attribute::parse_outer)?;
|
|
let label: Option<Label> = input.parse()?;
|
|
let while_token: Token![while] = input.parse()?;
|
|
let cond = Expr::parse_without_eager_brace(input)?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
attr::parsing::parse_inner(&content, &mut attrs)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprWhile {
|
|
attrs,
|
|
label,
|
|
while_token,
|
|
cond: Box::new(cond),
|
|
body: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprConst {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let const_token: Token![const] = input.parse()?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
let inner_attrs = content.call(Attribute::parse_inner)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprConst {
|
|
attrs: inner_attrs,
|
|
const_token,
|
|
block: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Label {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(Label {
|
|
name: input.parse()?,
|
|
colon_token: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Option<Label> {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
if input.peek(Lifetime) {
|
|
input.parse().map(Some)
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprContinue {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Ok(ExprContinue {
|
|
attrs: Vec::new(),
|
|
continue_token: input.parse()?,
|
|
label: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_break(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprBreak> {
|
|
let break_token: Token![break] = input.parse()?;
|
|
|
|
let ahead = input.fork();
|
|
let label: Option<Lifetime> = ahead.parse()?;
|
|
if label.is_some() && ahead.peek(Token![:]) {
|
|
// Not allowed: `break 'label: loop {...}`
|
|
// Parentheses are required. `break ('label: loop {...})`
|
|
let _: Expr = input.parse()?;
|
|
let start_span = label.unwrap().apostrophe;
|
|
let end_span = input.cursor().prev_span();
|
|
return Err(crate::error::new2(
|
|
start_span,
|
|
end_span,
|
|
"parentheses required",
|
|
));
|
|
}
|
|
|
|
input.advance_to(&ahead);
|
|
let expr = if Expr::peek(input) && (allow_struct.0 || !input.peek(token::Brace)) {
|
|
Some(input.parse()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(ExprBreak {
|
|
attrs: Vec::new(),
|
|
break_token,
|
|
label,
|
|
expr,
|
|
})
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for FieldValue {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
let member: Member = input.parse()?;
|
|
let (colon_token, value) = if input.peek(Token![:]) || !member.is_named() {
|
|
let colon_token: Token![:] = input.parse()?;
|
|
let value: Expr = input.parse()?;
|
|
(Some(colon_token), value)
|
|
} else if let Member::Named(ident) = &member {
|
|
let value = Expr::Path(ExprPath {
|
|
attrs: Vec::new(),
|
|
qself: None,
|
|
path: Path::from(ident.clone()),
|
|
});
|
|
(None, value)
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
|
|
Ok(FieldValue {
|
|
attrs,
|
|
member,
|
|
colon_token,
|
|
expr: value,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprStruct {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let expr_style = true;
|
|
let (qself, path) = path::parsing::qpath(input, expr_style)?;
|
|
expr_struct_helper(input, qself, path)
|
|
}
|
|
}
|
|
|
|
fn expr_struct_helper(
|
|
input: ParseStream,
|
|
qself: Option<QSelf>,
|
|
path: Path,
|
|
) -> Result<ExprStruct> {
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
|
|
let mut fields = Punctuated::new();
|
|
while !content.is_empty() {
|
|
if content.peek(Token![..]) {
|
|
return Ok(ExprStruct {
|
|
attrs: Vec::new(),
|
|
qself,
|
|
path,
|
|
brace_token,
|
|
fields,
|
|
dot2_token: Some(content.parse()?),
|
|
rest: if content.is_empty() {
|
|
None
|
|
} else {
|
|
Some(Box::new(content.parse()?))
|
|
},
|
|
});
|
|
}
|
|
|
|
fields.push(content.parse()?);
|
|
if content.is_empty() {
|
|
break;
|
|
}
|
|
let punct: Token![,] = content.parse()?;
|
|
fields.push_punct(punct);
|
|
}
|
|
|
|
Ok(ExprStruct {
|
|
attrs: Vec::new(),
|
|
qself,
|
|
path,
|
|
brace_token,
|
|
fields,
|
|
dot2_token: None,
|
|
rest: None,
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprUnsafe {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let unsafe_token: Token![unsafe] = input.parse()?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
let inner_attrs = content.call(Attribute::parse_inner)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprUnsafe {
|
|
attrs: inner_attrs,
|
|
unsafe_token,
|
|
block: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprBlock {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut attrs = input.call(Attribute::parse_outer)?;
|
|
let label: Option<Label> = input.parse()?;
|
|
|
|
let content;
|
|
let brace_token = braced!(content in input);
|
|
attr::parsing::parse_inner(&content, &mut attrs)?;
|
|
let stmts = content.call(Block::parse_within)?;
|
|
|
|
Ok(ExprBlock {
|
|
attrs,
|
|
label,
|
|
block: Block { brace_token, stmts },
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn expr_range(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprRange> {
|
|
let limits: RangeLimits = input.parse()?;
|
|
let end = parse_range_end(input, &limits, allow_struct)?;
|
|
Ok(ExprRange {
|
|
attrs: Vec::new(),
|
|
start: None,
|
|
limits,
|
|
end,
|
|
})
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn parse_range_end(
|
|
input: ParseStream,
|
|
limits: &RangeLimits,
|
|
allow_struct: AllowStruct,
|
|
) -> Result<Option<Box<Expr>>> {
|
|
if matches!(limits, RangeLimits::HalfOpen(_))
|
|
&& (input.is_empty()
|
|
|| input.peek(Token![,])
|
|
|| input.peek(Token![;])
|
|
|| input.peek(Token![.]) && !input.peek(Token![..])
|
|
|| input.peek(Token![?])
|
|
|| input.peek(Token![=>])
|
|
|| !allow_struct.0 && input.peek(token::Brace)
|
|
|| input.peek(Token![=])
|
|
|| input.peek(Token![+])
|
|
|| input.peek(Token![/])
|
|
|| input.peek(Token![%])
|
|
|| input.peek(Token![^])
|
|
|| input.peek(Token![>])
|
|
|| input.peek(Token![<=])
|
|
|| input.peek(Token![!=])
|
|
|| input.peek(Token![-=])
|
|
|| input.peek(Token![*=])
|
|
|| input.peek(Token![&=])
|
|
|| input.peek(Token![|=])
|
|
|| input.peek(Token![<<=])
|
|
|| input.peek(Token![as]))
|
|
{
|
|
Ok(None)
|
|
} else {
|
|
let end = parse_binop_rhs(input, allow_struct, Precedence::Range)?;
|
|
Ok(Some(end))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for RangeLimits {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let lookahead = input.lookahead1();
|
|
let dot_dot = lookahead.peek(Token![..]);
|
|
let dot_dot_eq = dot_dot && lookahead.peek(Token![..=]);
|
|
let dot_dot_dot = dot_dot && input.peek(Token![...]);
|
|
if dot_dot_eq {
|
|
input.parse().map(RangeLimits::Closed)
|
|
} else if dot_dot && !dot_dot_dot {
|
|
input.parse().map(RangeLimits::HalfOpen)
|
|
} else {
|
|
Err(lookahead.error())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
impl RangeLimits {
|
|
pub(crate) fn parse_obsolete(input: ParseStream) -> Result<Self> {
|
|
let lookahead = input.lookahead1();
|
|
let dot_dot = lookahead.peek(Token![..]);
|
|
let dot_dot_eq = dot_dot && lookahead.peek(Token![..=]);
|
|
let dot_dot_dot = dot_dot && input.peek(Token![...]);
|
|
if dot_dot_eq {
|
|
input.parse().map(RangeLimits::Closed)
|
|
} else if dot_dot_dot {
|
|
let dot3: Token![...] = input.parse()?;
|
|
Ok(RangeLimits::Closed(Token))
|
|
} else if dot_dot {
|
|
input.parse().map(RangeLimits::HalfOpen)
|
|
} else {
|
|
Err(lookahead.error())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ExprPath {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
#[cfg(not(feature = "full"))]
|
|
let attrs = Vec::new();
|
|
#[cfg(feature = "full")]
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
|
|
let expr_style = true;
|
|
let (qself, path) = path::parsing::qpath(input, expr_style)?;
|
|
|
|
Ok(ExprPath { attrs, qself, path })
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Member {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
if input.peek(Ident) {
|
|
input.parse().map(Member::Named)
|
|
} else if input.peek(LitInt) {
|
|
input.parse().map(Member::Unnamed)
|
|
} else {
|
|
Err(input.error("expected identifier or integer"))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
impl Arm {
|
|
pub(crate) fn parse_multiple(input: ParseStream) -> Result<Vec<Self>> {
|
|
let mut arms = Vec::new();
|
|
while !input.is_empty() {
|
|
arms.push(input.call(Arm::parse)?);
|
|
}
|
|
Ok(arms)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Arm {
|
|
fn parse(input: ParseStream) -> Result<Arm> {
|
|
let requires_comma;
|
|
Ok(Arm {
|
|
attrs: input.call(Attribute::parse_outer)?,
|
|
pat: Pat::parse_multi_with_leading_vert(input)?,
|
|
guard: {
|
|
if input.peek(Token![if]) {
|
|
let if_token: Token![if] = input.parse()?;
|
|
let guard: Expr = input.parse()?;
|
|
Some((if_token, Box::new(guard)))
|
|
} else {
|
|
None
|
|
}
|
|
},
|
|
fat_arrow_token: input.parse()?,
|
|
body: {
|
|
let body = Expr::parse_with_earlier_boundary_rule(input)?;
|
|
requires_comma = classify::requires_comma_to_be_match_arm(&body);
|
|
Box::new(body)
|
|
},
|
|
comma: {
|
|
if requires_comma && !input.is_empty() {
|
|
Some(input.parse()?)
|
|
} else {
|
|
input.parse()?
|
|
}
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Index {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let lit: LitInt = input.parse()?;
|
|
if lit.suffix().is_empty() {
|
|
Ok(Index {
|
|
index: lit
|
|
.base10_digits()
|
|
.parse()
|
|
.map_err(|err| Error::new(lit.span(), err))?,
|
|
span: lit.span(),
|
|
})
|
|
} else {
|
|
Err(Error::new(lit.span(), "expected unsuffixed integer"))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn multi_index(e: &mut Expr, dot_token: &mut Token![.], float: LitFloat) -> Result<bool> {
|
|
let float_token = float.token();
|
|
let float_span = float_token.span();
|
|
let mut float_repr = float_token.to_string();
|
|
let trailing_dot = float_repr.ends_with('.');
|
|
if trailing_dot {
|
|
float_repr.truncate(float_repr.len() - 1);
|
|
}
|
|
|
|
let mut offset = 0;
|
|
for part in float_repr.split('.') {
|
|
let mut index: Index =
|
|
crate::parse_str(part).map_err(|err| Error::new(float_span, err))?;
|
|
let part_end = offset + part.len();
|
|
index.span = float_token.subspan(offset..part_end).unwrap_or(float_span);
|
|
|
|
let base = mem::replace(e, Expr::PLACEHOLDER);
|
|
*e = Expr::Field(ExprField {
|
|
attrs: Vec::new(),
|
|
base: Box::new(base),
|
|
dot_token: Token,
|
|
member: Member::Unnamed(index),
|
|
});
|
|
|
|
let dot_span = float_token
|
|
.subspan(part_end..part_end + 1)
|
|
.unwrap_or(float_span);
|
|
*dot_token = Token;
|
|
offset = part_end + 1;
|
|
}
|
|
|
|
Ok(!trailing_dot)
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for PointerMutability {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let lookahead = input.lookahead1();
|
|
if lookahead.peek(Token![const]) {
|
|
Ok(PointerMutability::Const(input.parse()?))
|
|
} else if lookahead.peek(Token![mut]) {
|
|
Ok(PointerMutability::Mut(input.parse()?))
|
|
} else {
|
|
Err(lookahead.error())
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_cast(input: ParseStream) -> Result<()> {
|
|
let kind = if input.peek(Token![.]) && !input.peek(Token![..]) {
|
|
if input.peek2(Token![await]) {
|
|
"`.await`"
|
|
} else if input.peek2(Ident) && (input.peek3(token::Paren) || input.peek3(Token![::])) {
|
|
"a method call"
|
|
} else {
|
|
"a field access"
|
|
}
|
|
} else if input.peek(Token![?]) {
|
|
"`?`"
|
|
} else if input.peek(token::Bracket) {
|
|
"indexing"
|
|
} else if input.peek(token::Paren) {
|
|
"a function call"
|
|
} else {
|
|
return Ok(());
|
|
};
|
|
let msg = format!("casts cannot be followed by {}", kind);
|
|
Err(input.error(msg))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "printing")]
|
|
pub(crate) mod printing {
|
|
use crate::attr::Attribute;
|
|
#[cfg(feature = "full")]
|
|
use crate::attr::FilterAttrs;
|
|
#[cfg(feature = "full")]
|
|
use crate::classify;
|
|
#[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,
|
|
};
|
|
use crate::fixup::FixupContext;
|
|
use crate::op::BinOp;
|
|
use crate::path;
|
|
use crate::path::printing::PathStyle;
|
|
use crate::precedence::Precedence;
|
|
use crate::token;
|
|
#[cfg(feature = "full")]
|
|
use crate::ty::ReturnType;
|
|
use proc_macro2::{Literal, Span, TokenStream};
|
|
use quote::{ToTokens, TokenStreamExt};
|
|
|
|
#[cfg(feature = "full")]
|
|
pub(crate) fn outer_attrs_to_tokens(attrs: &[Attribute], tokens: &mut TokenStream) {
|
|
tokens.append_all(attrs.outer());
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn inner_attrs_to_tokens(attrs: &[Attribute], tokens: &mut TokenStream) {
|
|
tokens.append_all(attrs.inner());
|
|
}
|
|
|
|
#[cfg(not(feature = "full"))]
|
|
pub(crate) fn outer_attrs_to_tokens(_attrs: &[Attribute], _tokens: &mut TokenStream) {}
|
|
|
|
pub(crate) fn print_subexpression(
|
|
expr: &Expr,
|
|
needs_group: bool,
|
|
tokens: &mut TokenStream,
|
|
mut fixup: FixupContext,
|
|
) {
|
|
if needs_group {
|
|
// If we are surrounding the whole cond in parentheses, such as:
|
|
//
|
|
// if (return Struct {}) {}
|
|
//
|
|
// then there is no need for parenthesizing the individual struct
|
|
// expressions within. On the other hand if the whole cond is not
|
|
// parenthesized, then print_expr must parenthesize exterior struct
|
|
// literals.
|
|
//
|
|
// if x == (Struct {}) {}
|
|
//
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| print_expr(expr, tokens, fixup);
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn print_expr(expr: &Expr, tokens: &mut TokenStream, mut fixup: FixupContext) {
|
|
#[cfg(feature = "full")]
|
|
let needs_group = fixup.parenthesize(expr);
|
|
#[cfg(not(feature = "full"))]
|
|
let needs_group = false;
|
|
|
|
if needs_group {
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| match expr {
|
|
#[cfg(feature = "full")]
|
|
Expr::Array(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Assign(e) => print_expr_assign(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Async(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Await(e) => print_expr_await(e, tokens, fixup),
|
|
Expr::Binary(e) => print_expr_binary(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Block(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Break(e) => print_expr_break(e, tokens, fixup),
|
|
Expr::Call(e) => print_expr_call(e, tokens, fixup),
|
|
Expr::Cast(e) => print_expr_cast(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Closure(e) => print_expr_closure(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Const(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Continue(e) => e.to_tokens(tokens),
|
|
Expr::Field(e) => print_expr_field(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::ForLoop(e) => e.to_tokens(tokens),
|
|
Expr::Group(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::If(e) => e.to_tokens(tokens),
|
|
Expr::Index(e) => print_expr_index(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Infer(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Let(e) => print_expr_let(e, tokens, fixup),
|
|
Expr::Lit(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Loop(e) => e.to_tokens(tokens),
|
|
Expr::Macro(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Match(e) => e.to_tokens(tokens),
|
|
Expr::MethodCall(e) => print_expr_method_call(e, tokens, fixup),
|
|
Expr::Paren(e) => e.to_tokens(tokens),
|
|
Expr::Path(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Range(e) => print_expr_range(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::RawAddr(e) => print_expr_raw_addr(e, tokens, fixup),
|
|
Expr::Reference(e) => print_expr_reference(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Repeat(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Return(e) => print_expr_return(e, tokens, fixup),
|
|
Expr::Struct(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Try(e) => print_expr_try(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::TryBlock(e) => e.to_tokens(tokens),
|
|
Expr::Tuple(e) => e.to_tokens(tokens),
|
|
Expr::Unary(e) => print_expr_unary(e, tokens, fixup),
|
|
#[cfg(feature = "full")]
|
|
Expr::Unsafe(e) => e.to_tokens(tokens),
|
|
Expr::Verbatim(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::While(e) => e.to_tokens(tokens),
|
|
#[cfg(feature = "full")]
|
|
Expr::Yield(e) => print_expr_yield(e, tokens, fixup),
|
|
|
|
#[cfg(not(feature = "full"))]
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprArray {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.bracket_token.surround(tokens, |tokens| {
|
|
self.elems.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprAssign {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_assign(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_assign(e: &ExprAssign, tokens: &mut TokenStream, mut fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
|
|
let needs_group = !e.attrs.is_empty();
|
|
if needs_group {
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| {
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
&e.left,
|
|
false,
|
|
false,
|
|
Precedence::Assign,
|
|
);
|
|
print_subexpression(&e.left, left_prec <= Precedence::Range, tokens, left_fixup);
|
|
e.eq_token.to_tokens(tokens);
|
|
print_expr(
|
|
&e.right,
|
|
tokens,
|
|
fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign),
|
|
);
|
|
};
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprAsync {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.async_token.to_tokens(tokens);
|
|
self.capture.to_tokens(tokens);
|
|
self.block.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprAwait {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_await(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_await(e: &ExprAwait, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.base);
|
|
print_subexpression(
|
|
&e.base,
|
|
left_prec < Precedence::Unambiguous,
|
|
tokens,
|
|
left_fixup,
|
|
);
|
|
e.dot_token.to_tokens(tokens);
|
|
e.await_token.to_tokens(tokens);
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprBinary {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_binary(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_binary(e: &ExprBinary, tokens: &mut TokenStream, mut fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
|
|
let needs_group = !e.attrs.is_empty();
|
|
if needs_group {
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| {
|
|
let binop_prec = Precedence::of_binop(&e.op);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
&e.left,
|
|
#[cfg(feature = "full")]
|
|
match &e.op {
|
|
BinOp::Sub(_)
|
|
| BinOp::Mul(_)
|
|
| BinOp::And(_)
|
|
| BinOp::Or(_)
|
|
| BinOp::BitAnd(_)
|
|
| BinOp::BitOr(_)
|
|
| BinOp::Shl(_)
|
|
| BinOp::Lt(_) => true,
|
|
_ => false,
|
|
},
|
|
match &e.op {
|
|
BinOp::Shl(_) | BinOp::Lt(_) => true,
|
|
_ => false,
|
|
},
|
|
#[cfg(feature = "full")]
|
|
binop_prec,
|
|
);
|
|
let left_needs_group = match binop_prec {
|
|
Precedence::Assign => left_prec <= Precedence::Range,
|
|
Precedence::Compare => left_prec <= binop_prec,
|
|
_ => left_prec < binop_prec,
|
|
};
|
|
|
|
let right_fixup = fixup.rightmost_subexpression_fixup(
|
|
#[cfg(feature = "full")]
|
|
false,
|
|
#[cfg(feature = "full")]
|
|
false,
|
|
#[cfg(feature = "full")]
|
|
binop_prec,
|
|
);
|
|
let right_needs_group = binop_prec != Precedence::Assign
|
|
&& right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec;
|
|
|
|
print_subexpression(&e.left, left_needs_group, tokens, left_fixup);
|
|
e.op.to_tokens(tokens);
|
|
print_subexpression(&e.right, right_needs_group, tokens, right_fixup);
|
|
};
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprBlock {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.label.to_tokens(tokens);
|
|
self.block.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.block.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprBreak {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_break(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_break(e: &ExprBreak, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.break_token.to_tokens(tokens);
|
|
e.label.to_tokens(tokens);
|
|
if let Some(value) = &e.expr {
|
|
print_subexpression(
|
|
value,
|
|
// Parenthesize `break 'inner: loop { break 'inner 1 } + 1`
|
|
// ^---------------------------------^
|
|
e.label.is_none() && classify::expr_leading_label(value),
|
|
tokens,
|
|
fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprCall {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_call(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_call(e: &ExprCall, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
&e.func,
|
|
#[cfg(feature = "full")]
|
|
true,
|
|
false,
|
|
#[cfg(feature = "full")]
|
|
Precedence::Unambiguous,
|
|
);
|
|
let needs_group = if let Expr::Field(func) = &*e.func {
|
|
func.member.is_named()
|
|
} else {
|
|
left_prec < Precedence::Unambiguous
|
|
};
|
|
print_subexpression(&e.func, needs_group, tokens, left_fixup);
|
|
|
|
e.paren_token.surround(tokens, |tokens| {
|
|
e.args.to_tokens(tokens);
|
|
});
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprCast {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_cast(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_cast(e: &ExprCast, tokens: &mut TokenStream, mut fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
|
|
let needs_group = !e.attrs.is_empty();
|
|
if needs_group {
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| {
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
&e.expr,
|
|
#[cfg(feature = "full")]
|
|
false,
|
|
false,
|
|
#[cfg(feature = "full")]
|
|
Precedence::Cast,
|
|
);
|
|
print_subexpression(&e.expr, left_prec < Precedence::Cast, tokens, left_fixup);
|
|
e.as_token.to_tokens(tokens);
|
|
e.ty.to_tokens(tokens);
|
|
};
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprClosure {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_closure(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_closure(e: &ExprClosure, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.lifetimes.to_tokens(tokens);
|
|
e.constness.to_tokens(tokens);
|
|
e.movability.to_tokens(tokens);
|
|
e.asyncness.to_tokens(tokens);
|
|
e.capture.to_tokens(tokens);
|
|
e.or1_token.to_tokens(tokens);
|
|
e.inputs.to_tokens(tokens);
|
|
e.or2_token.to_tokens(tokens);
|
|
e.output.to_tokens(tokens);
|
|
if matches!(e.output, ReturnType::Default)
|
|
|| matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none())
|
|
{
|
|
print_expr(
|
|
&e.body,
|
|
tokens,
|
|
fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
|
|
);
|
|
} else {
|
|
token::Brace::default().surround(tokens, |tokens| {
|
|
print_expr(&e.body, tokens, FixupContext::new_stmt());
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprConst {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.const_token.to_tokens(tokens);
|
|
self.block.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.block.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprContinue {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.continue_token.to_tokens(tokens);
|
|
self.label.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprField {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_field(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_field(e: &ExprField, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.base);
|
|
print_subexpression(
|
|
&e.base,
|
|
left_prec < Precedence::Unambiguous,
|
|
tokens,
|
|
left_fixup,
|
|
);
|
|
e.dot_token.to_tokens(tokens);
|
|
e.member.to_tokens(tokens);
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprForLoop {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.label.to_tokens(tokens);
|
|
self.for_token.to_tokens(tokens);
|
|
self.pat.to_tokens(tokens);
|
|
self.in_token.to_tokens(tokens);
|
|
print_expr(&self.expr, tokens, FixupContext::new_condition());
|
|
self.body.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.body.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprGroup {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.group_token.surround(tokens, |tokens| {
|
|
self.expr.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprIf {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
|
|
let mut expr = self;
|
|
loop {
|
|
expr.if_token.to_tokens(tokens);
|
|
print_expr(&expr.cond, tokens, FixupContext::new_condition());
|
|
expr.then_branch.to_tokens(tokens);
|
|
|
|
let (else_token, else_) = match &expr.else_branch {
|
|
Some(else_branch) => else_branch,
|
|
None => break,
|
|
};
|
|
|
|
else_token.to_tokens(tokens);
|
|
match &**else_ {
|
|
Expr::If(next) => {
|
|
expr = next;
|
|
}
|
|
Expr::Block(last) => {
|
|
last.to_tokens(tokens);
|
|
break;
|
|
}
|
|
// If this is not one of the valid expressions to exist in
|
|
// an else clause, wrap it in a block.
|
|
other => {
|
|
token::Brace::default().surround(tokens, |tokens| {
|
|
print_expr(other, tokens, FixupContext::new_stmt());
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprIndex {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_index(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_index(e: &ExprIndex, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
&e.expr,
|
|
#[cfg(feature = "full")]
|
|
true,
|
|
false,
|
|
#[cfg(feature = "full")]
|
|
Precedence::Unambiguous,
|
|
);
|
|
print_subexpression(
|
|
&e.expr,
|
|
left_prec < Precedence::Unambiguous,
|
|
tokens,
|
|
left_fixup,
|
|
);
|
|
e.bracket_token.surround(tokens, |tokens| {
|
|
e.index.to_tokens(tokens);
|
|
});
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprInfer {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.underscore_token.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprLet {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_let(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_let(e: &ExprLet, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.let_token.to_tokens(tokens);
|
|
e.pat.to_tokens(tokens);
|
|
e.eq_token.to_tokens(tokens);
|
|
let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr, Precedence::Let);
|
|
print_subexpression(&e.expr, right_prec < Precedence::Let, tokens, right_fixup);
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprLit {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.lit.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprLoop {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.label.to_tokens(tokens);
|
|
self.loop_token.to_tokens(tokens);
|
|
self.body.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.body.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprMacro {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.mac.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprMatch {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.match_token.to_tokens(tokens);
|
|
print_expr(&self.expr, tokens, FixupContext::new_condition());
|
|
self.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
for (i, arm) in self.arms.iter().enumerate() {
|
|
arm.to_tokens(tokens);
|
|
// Ensure that we have a comma after a non-block arm, except
|
|
// for the last one.
|
|
let is_last = i == self.arms.len() - 1;
|
|
if !is_last
|
|
&& classify::requires_comma_to_be_match_arm(&arm.body)
|
|
&& arm.comma.is_none()
|
|
{
|
|
<Token![,]>::default().to_tokens(tokens);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprMethodCall {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_method_call(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_method_call(e: &ExprMethodCall, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.receiver);
|
|
print_subexpression(
|
|
&e.receiver,
|
|
left_prec < Precedence::Unambiguous,
|
|
tokens,
|
|
left_fixup,
|
|
);
|
|
e.dot_token.to_tokens(tokens);
|
|
e.method.to_tokens(tokens);
|
|
if let Some(turbofish) = &e.turbofish {
|
|
path::printing::print_angle_bracketed_generic_arguments(
|
|
tokens,
|
|
turbofish,
|
|
PathStyle::Expr,
|
|
);
|
|
}
|
|
e.paren_token.surround(tokens, |tokens| {
|
|
e.args.to_tokens(tokens);
|
|
});
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprParen {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.paren_token.surround(tokens, |tokens| {
|
|
self.expr.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprPath {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprRange {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_range(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_range(e: &ExprRange, tokens: &mut TokenStream, mut fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
|
|
let needs_group = !e.attrs.is_empty();
|
|
if needs_group {
|
|
fixup = FixupContext::NONE;
|
|
}
|
|
|
|
let do_print_expr = |tokens: &mut TokenStream| {
|
|
if let Some(start) = &e.start {
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
|
|
start,
|
|
true,
|
|
false,
|
|
Precedence::Range,
|
|
);
|
|
print_subexpression(start, left_prec <= Precedence::Range, tokens, left_fixup);
|
|
}
|
|
e.limits.to_tokens(tokens);
|
|
if let Some(end) = &e.end {
|
|
let right_fixup =
|
|
fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
|
|
let right_prec = right_fixup.rightmost_subexpression_precedence(end);
|
|
print_subexpression(end, right_prec <= Precedence::Range, tokens, right_fixup);
|
|
}
|
|
};
|
|
|
|
if needs_group {
|
|
token::Paren::default().surround(tokens, do_print_expr);
|
|
} else {
|
|
do_print_expr(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprRawAddr {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_raw_addr(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_raw_addr(e: &ExprRawAddr, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.and_token.to_tokens(tokens);
|
|
e.raw.to_tokens(tokens);
|
|
e.mutability.to_tokens(tokens);
|
|
let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr, Precedence::Prefix);
|
|
print_subexpression(
|
|
&e.expr,
|
|
right_prec < Precedence::Prefix,
|
|
tokens,
|
|
right_fixup,
|
|
);
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprReference {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_reference(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_reference(e: &ExprReference, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.and_token.to_tokens(tokens);
|
|
e.mutability.to_tokens(tokens);
|
|
let (right_prec, right_fixup) = fixup.rightmost_subexpression(
|
|
&e.expr,
|
|
#[cfg(feature = "full")]
|
|
Precedence::Prefix,
|
|
);
|
|
print_subexpression(
|
|
&e.expr,
|
|
right_prec < Precedence::Prefix,
|
|
tokens,
|
|
right_fixup,
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprRepeat {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.bracket_token.surround(tokens, |tokens| {
|
|
self.expr.to_tokens(tokens);
|
|
self.semi_token.to_tokens(tokens);
|
|
self.len.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprReturn {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_return(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_return(e: &ExprReturn, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.return_token.to_tokens(tokens);
|
|
if let Some(expr) = &e.expr {
|
|
print_expr(
|
|
expr,
|
|
tokens,
|
|
fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprStruct {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr);
|
|
self.brace_token.surround(tokens, |tokens| {
|
|
self.fields.to_tokens(tokens);
|
|
if let Some(dot2_token) = &self.dot2_token {
|
|
dot2_token.to_tokens(tokens);
|
|
} else if self.rest.is_some() {
|
|
Token).to_tokens(tokens);
|
|
}
|
|
self.rest.to_tokens(tokens);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprTry {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_try(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_try(e: &ExprTry, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.expr);
|
|
print_subexpression(
|
|
&e.expr,
|
|
left_prec < Precedence::Unambiguous,
|
|
tokens,
|
|
left_fixup,
|
|
);
|
|
e.question_token.to_tokens(tokens);
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprTryBlock {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.try_token.to_tokens(tokens);
|
|
self.block.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprTuple {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.paren_token.surround(tokens, |tokens| {
|
|
self.elems.to_tokens(tokens);
|
|
// If we only have one argument, we need a trailing comma to
|
|
// distinguish ExprTuple from ExprParen.
|
|
if self.elems.len() == 1 && !self.elems.trailing_punct() {
|
|
<Token![,]>::default().to_tokens(tokens);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprUnary {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_unary(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
fn print_expr_unary(e: &ExprUnary, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.op.to_tokens(tokens);
|
|
let (right_prec, right_fixup) = fixup.rightmost_subexpression(
|
|
&e.expr,
|
|
#[cfg(feature = "full")]
|
|
Precedence::Prefix,
|
|
);
|
|
print_subexpression(
|
|
&e.expr,
|
|
right_prec < Precedence::Prefix,
|
|
tokens,
|
|
right_fixup,
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprUnsafe {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.unsafe_token.to_tokens(tokens);
|
|
self.block.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.block.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprWhile {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.label.to_tokens(tokens);
|
|
self.while_token.to_tokens(tokens);
|
|
print_expr(&self.cond, tokens, FixupContext::new_condition());
|
|
self.body.brace_token.surround(tokens, |tokens| {
|
|
inner_attrs_to_tokens(&self.attrs, tokens);
|
|
tokens.append_all(&self.body.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ExprYield {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_expr_yield(self, tokens, FixupContext::NONE);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
fn print_expr_yield(e: &ExprYield, tokens: &mut TokenStream, fixup: FixupContext) {
|
|
outer_attrs_to_tokens(&e.attrs, tokens);
|
|
e.yield_token.to_tokens(tokens);
|
|
if let Some(expr) = &e.expr {
|
|
print_expr(
|
|
expr,
|
|
tokens,
|
|
fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Arm {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
tokens.append_all(&self.attrs);
|
|
self.pat.to_tokens(tokens);
|
|
if let Some((if_token, guard)) = &self.guard {
|
|
if_token.to_tokens(tokens);
|
|
guard.to_tokens(tokens);
|
|
}
|
|
self.fat_arrow_token.to_tokens(tokens);
|
|
print_expr(&self.body, tokens, FixupContext::new_match_arm());
|
|
self.comma.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for FieldValue {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.member.to_tokens(tokens);
|
|
if let Some(colon_token) = &self.colon_token {
|
|
colon_token.to_tokens(tokens);
|
|
self.expr.to_tokens(tokens);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Index {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
let mut lit = Literal::i64_unsuffixed(i64::from(self.index));
|
|
lit.set_span(self.span);
|
|
tokens.append(lit);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Label {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.name.to_tokens(tokens);
|
|
self.colon_token.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Member {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
match self {
|
|
Member::Named(ident) => ident.to_tokens(tokens),
|
|
Member::Unnamed(index) => index.to_tokens(tokens),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for RangeLimits {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
match self {
|
|
RangeLimits::HalfOpen(t) => t.to_tokens(tokens),
|
|
RangeLimits::Closed(t) => t.to_tokens(tokens),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for PointerMutability {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
match self {
|
|
PointerMutability::Const(const_token) => const_token.to_tokens(tokens),
|
|
PointerMutability::Mut(mut_token) => mut_token.to_tokens(tokens),
|
|
}
|
|
}
|
|
}
|
|
}
|