use crate::attr::Attribute;
#[cfg(all(feature = "parsing", feature = "full"))]
use crate::error::Result;
#[cfg(feature = "full")]
use crate::generics::BoundLifetimes;
use crate::ident::Ident;
#[cfg(feature = "full")]
use crate::lifetime::Lifetime;
use crate::lit::Lit;
use crate::mac::Macro;
use crate::op::{BinOp, UnOp};
#[cfg(all(feature = "parsing", feature = "full"))]
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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
#[non_exhaustive]
pub enum Expr {
Array(ExprArray),
Assign(ExprAssign),
Async(ExprAsync),
Await(ExprAwait),
Binary(ExprBinary),
Block(ExprBlock),
Break(ExprBreak),
Call(ExprCall),
Cast(ExprCast),
Closure(ExprClosure),
Const(ExprConst),
Continue(ExprContinue),
Field(ExprField),
ForLoop(ExprForLoop),
Group(ExprGroup),
If(ExprIf),
Index(ExprIndex),
Infer(ExprInfer),
Let(ExprLet),
Lit(ExprLit),
Loop(ExprLoop),
Macro(ExprMacro),
Match(ExprMatch),
MethodCall(ExprMethodCall),
Paren(ExprParen),
Path(ExprPath),
Range(ExprRange),
Reference(ExprReference),
Repeat(ExprRepeat),
Return(ExprReturn),
Struct(ExprStruct),
Try(ExprTry),
TryBlock(ExprTryBlock),
Tuple(ExprTuple),
Unary(ExprUnary),
Unsafe(ExprUnsafe),
Verbatim(TokenStream),
While(ExprWhile),
Yield(ExprYield),
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprArray #full {
pub attrs: Vec<Attribute>,
pub bracket_token: token::Bracket,
pub elems: Punctuated<Expr, Token![,]>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprBlock #full {
pub attrs: Vec<Attribute>,
pub label: Option<Label>,
pub block: Block,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprConst #full {
pub attrs: Vec<Attribute>,
pub const_token: Token![const],
pub block: Block,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprContinue #full {
pub attrs: Vec<Attribute>,
pub continue_token: Token![continue],
pub label: Option<Lifetime>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprGroup {
pub attrs: Vec<Attribute>,
pub group_token: token::Group,
pub expr: Box<Expr>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprInfer #full {
pub attrs: Vec<Attribute>,
pub underscore_token: Token![_],
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub struct ExprLit {
pub attrs: Vec<Attribute>,
pub lit: Lit,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub struct ExprMacro {
pub attrs: Vec<Attribute>,
pub mac: Macro,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub struct ExprPath {
pub attrs: Vec<Attribute>,
pub qself: Option<QSelf>,
pub path: Path,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprReturn #full {
pub attrs: Vec<Attribute>,
pub return_token: Token![return],
pub expr: Option<Box<Expr>>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprTry #full {
pub attrs: Vec<Attribute>,
pub expr: Box<Expr>,
pub question_token: Token![?],
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprTryBlock #full {
pub attrs: Vec<Attribute>,
pub try_token: Token![try],
pub block: Block,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprTuple #full {
pub attrs: Vec<Attribute>,
pub paren_token: token::Paren,
pub elems: Punctuated<Expr, Token![,]>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub struct ExprUnary {
pub attrs: Vec<Attribute>,
pub op: UnOp,
pub expr: Box<Expr>,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprUnsafe #full {
pub attrs: Vec<Attribute>,
pub unsafe_token: Token![unsafe],
pub block: Block,
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct ExprYield #full {
pub attrs: Vec<Attribute>,
pub yield_token: Token![yield],
pub expr: Option<Box<Expr>>,
}
}
impl Expr {
pub const PLACEHOLDER: Self = Expr::Path(ExprPath {
attrs: Vec::new(),
qself: None,
path: Path {
leading_colon: None,
segments: Punctuated::new(),
},
});
#[cfg(all(feature = "full", feature = "parsing"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "full", feature = "parsing"))))]
pub fn parse_without_eager_brace(input: ParseStream) -> Result<Expr> {
parsing::ambiguous_expr(input, parsing::AllowStruct(false))
}
#[cfg(all(feature = "full", feature = "parsing"))]
#[cfg_attr(doc_cfg, 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)
}
#[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::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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub enum Member {
Named(Ident),
Unnamed(Index),
}
}
impl From<Ident> for Member {
fn from(ident: Ident) -> Member {
Member::Named(ident)
}
}
impl From<Index> for Member {
fn from(index: Index) -> Member {
Member::Unnamed(index)
}
}
impl From<usize> for Member {
fn from(index: usize) -> Member {
Member::Unnamed(Index::from(index))
}
}
impl Eq for Member {}
impl PartialEq for Member {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Member::Named(this), Member::Named(other)) => this == other,
(Member::Unnamed(this), Member::Unnamed(other)) => this == other,
_ => false,
}
}
}
impl Hash for Member {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Member::Named(m) => m.hash(state),
Member::Unnamed(m) => m.hash(state),
}
}
}
#[cfg(feature = "printing")]
impl IdentFragment for Member {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Member::Named(m) => Display::fmt(m, formatter),
Member::Unnamed(m) => Display::fmt(&m.index, formatter),
}
}
fn span(&self) -> Option<Span> {
match self {
Member::Named(m) => Some(m.span()),
Member::Unnamed(m) => Some(m.span),
}
}
}
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
pub struct FieldValue {
pub attrs: Vec<Attribute>,
pub member: Member,
pub colon_token: Option<Token![:]>,
pub expr: Expr,
}
}
#[cfg(feature = "full")]
ast_struct! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub struct Label {
pub name: Lifetime,
pub colon_token: Token![:],
}
}
#[cfg(feature = "full")]
ast_struct! {
#[cfg_attr(doc_cfg, 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! {
#[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
pub enum RangeLimits {
HalfOpen(Token![..]),
Closed(Token![..=]),
}
}
#[cfg(any(feature = "parsing", feature = "printing"))]
#[cfg(feature = "full")]
pub(crate) fn requires_terminator(expr: &Expr) -> bool {
match expr {
Expr::If(_)
| Expr::Match(_)
| Expr::Block(_) | Expr::Unsafe(_) | Expr::While(_)
| Expr::Loop(_)
| Expr::ForLoop(_)
| Expr::TryBlock(_)
| Expr::Const(_) => false,
Expr::Array(_)
| Expr::Assign(_)
| Expr::Async(_)
| Expr::Await(_)
| Expr::Binary(_)
| Expr::Break(_)
| Expr::Call(_)
| Expr::Cast(_)
| Expr::Closure(_)
| Expr::Continue(_)
| Expr::Field(_)
| Expr::Group(_)
| Expr::Index(_)
| Expr::Infer(_)
| Expr::Let(_)
| Expr::Lit(_)
| Expr::Macro(_)
| Expr::MethodCall(_)
| Expr::Paren(_)
| Expr::Path(_)
| Expr::Range(_)
| Expr::Reference(_)
| Expr::Repeat(_)
| Expr::Return(_)
| Expr::Struct(_)
| Expr::Try(_)
| Expr::Tuple(_)
| Expr::Unary(_)
| Expr::Yield(_)
| Expr::Verbatim(_) => true
}
}
#[cfg(feature = "parsing")]
mod precedence {
use super::BinOp;
use std::cmp::Ordering;
pub(super) enum Precedence {
Any,
Assign,
Range,
Or,
And,
Compare,
BitOr,
BitXor,
BitAnd,
Shift,
Arithmetic,
Term,
Cast,
}
impl Precedence {
pub(super) fn of(op: &BinOp) -> Self {
match op {
BinOp::Add(_) | BinOp::Sub(_) => Precedence::Arithmetic,
BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) => Precedence::Term,
BinOp::And(_) => Precedence::And,
BinOp::Or(_) => Precedence::Or,
BinOp::BitXor(_) => Precedence::BitXor,
BinOp::BitAnd(_) => Precedence::BitAnd,
BinOp::BitOr(_) => Precedence::BitOr,
BinOp::Shl(_) | BinOp::Shr(_) => Precedence::Shift,
BinOp::Eq(_)
| BinOp::Lt(_)
| BinOp::Le(_)
| BinOp::Ne(_)
| BinOp::Ge(_)
| BinOp::Gt(_) => Precedence::Compare,
BinOp::AddAssign(_)
| BinOp::SubAssign(_)
| BinOp::MulAssign(_)
| BinOp::DivAssign(_)
| BinOp::RemAssign(_)
| BinOp::BitXorAssign(_)
| BinOp::BitAndAssign(_)
| BinOp::BitOrAssign(_)
| BinOp::ShlAssign(_)
| BinOp::ShrAssign(_) => Precedence::Assign,
}
}
}
impl Copy for Precedence {}
impl Clone for Precedence {
fn clone(&self) -> Self {
*self
}
}
impl PartialEq for Precedence {
fn eq(&self, other: &Self) -> bool {
*self as u8 == *other as u8
}
}
impl PartialOrd for Precedence {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let this = *self as u8;
let other = *other as u8;
Some(this.cmp(&other))
}
}
}
#[cfg(feature = "parsing")]
pub(crate) mod parsing {
#[cfg(feature = "full")]
use crate::attr;
use crate::attr::Attribute;
use crate::error::{Error, Result};
use crate::expr::precedence::Precedence;
#[cfg(feature = "full")]
use crate::expr::{
requires_terminator, Arm, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock,
ExprBreak, ExprClosure, ExprConst, ExprContinue, ExprForLoop, ExprIf, ExprInfer, ExprLet,
ExprLoop, ExprMatch, ExprRange, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprTuple,
ExprUnsafe, ExprWhile, ExprYield, Label, RangeLimits,
};
use crate::expr::{
Expr, ExprBinary, ExprCall, ExprCast, ExprField, ExprGroup, ExprIndex, ExprLit, ExprMacro,
ExprMethodCall, ExprParen, ExprPath, ExprReference, ExprStruct, ExprUnary, FieldValue,
Index, Member,
};
#[cfg(feature = "full")]
use crate::ext::IdentExt as _;
#[cfg(feature = "full")]
use crate::generics::BoundLifetimes;
use crate::ident::Ident;
#[cfg(feature = "full")]
use crate::lifetime::Lifetime;
use crate::lit::{Lit, LitFloat, LitInt};
use crate::mac::{self, Macro};
use crate::op::BinOp;
use crate::parse::discouraged::Speculative as _;
#[cfg(feature = "full")]
use crate::parse::ParseBuffer;
use crate::parse::{Parse, ParseStream};
#[cfg(feature = "full")]
use crate::pat::{Pat, PatType};
use crate::path::{self, AngleBracketedGenericArguments, Path, QSelf};
use crate::punctuated::Punctuated;
#[cfg(feature = "full")]
use crate::stmt::Block;
use crate::token;
use crate::ty;
#[cfg(feature = "full")]
use crate::ty::{ReturnType, Type};
use crate::verbatim;
#[cfg(feature = "full")]
use proc_macro2::TokenStream;
use std::mem;
mod kw {
crate::custom_keyword!(builtin);
crate::custom_keyword!(raw);
}
#[cfg(feature = "full")]
pub(super) struct AllowStruct(pub bool);
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for Expr {
fn parse(input: ParseStream) -> Result<Self> {
ambiguous_expr(
input,
#[cfg(feature = "full")]
AllowStruct(true),
)
}
}
#[cfg(feature = "full")]
pub(super) fn parse_with_earlier_boundary_rule(input: ParseStream) -> Result<Expr> {
let mut attrs = input.call(expr_attrs)?;
let mut expr = if input.peek(token::Group) {
let allow_struct = AllowStruct(true);
let atom = expr_group(input, allow_struct)?;
if continue_parsing_early(&atom) {
trailer_helper(input, atom)?
} else {
atom
}
} else if input.peek(Token![if]) {
Expr::If(input.parse()?)
} else if input.peek(Token![while]) {
Expr::While(input.parse()?)
} else if input.peek(Token![for])
&& !(input.peek2(Token![<]) && (input.peek3(Lifetime) || input.peek3(Token![>])))
{
Expr::ForLoop(input.parse()?)
} else if input.peek(Token![loop]) {
Expr::Loop(input.parse()?)
} else if input.peek(Token![match]) {
Expr::Match(input.parse()?)
} else if input.peek(Token![try]) && input.peek2(token::Brace) {
Expr::TryBlock(input.parse()?)
} else if input.peek(Token![unsafe]) {
Expr::Unsafe(input.parse()?)
} else if input.peek(Token![const]) && input.peek2(token::Brace) {
Expr::Const(input.parse()?)
} else if input.peek(token::Brace) {
Expr::Block(input.parse()?)
} else if input.peek(Lifetime) {
atom_labeled(input)?
} else {
let allow_struct = AllowStruct(true);
unary_expr(input, allow_struct)?
};
if continue_parsing_early(&expr) {
attrs.extend(expr.replace_attrs(Vec::new()));
expr.replace_attrs(attrs);
let allow_struct = AllowStruct(true);
return parse_expr(input, expr, allow_struct, Precedence::Any);
}
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::Any);
}
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 can_begin_expr(input: ParseStream) -> bool {
input.peek(Ident::peek_any) || input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) || input.peek(Lit) || 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![<=]) && !input.peek(Token![<<=]) || input.peek(Token![::]) || input.peek(Lifetime) || input.peek(Token![#]) }
#[cfg(feature = "full")]
fn parse_expr(
input: ParseStream,
mut lhs: Expr,
allow_struct: AllowStruct,
base: Precedence,
) -> Result<Expr> {
loop {
let ahead = input.fork();
if let Expr::Range(ExprRange { end: Some(_), .. }) = lhs {
break;
} else if let Ok(op) = ahead.parse::<BinOp>() {
let precedence = Precedence::of(&op);
if precedence < base {
break;
}
if precedence == Precedence::Compare {
if let Expr::Binary(lhs) = &lhs {
if Precedence::of(&lhs.op) == Precedence::Compare {
break;
}
}
}
input.advance_to(&ahead);
let right = parse_binop_rhs(input, allow_struct, precedence)?;
lhs = Expr::Binary(ExprBinary {
attrs: Vec::new(),
left: Box::new(lhs),
op,
right,
});
} else if Precedence::Assign >= base && input.peek(Token![=]) && !input.peek(Token![=>])
{
let eq_token: Token![=] = input.parse()?;
let right = parse_binop_rhs(input, allow_struct, Precedence::Assign)?;
lhs = Expr::Assign(ExprAssign {
attrs: Vec::new(),
left: Box::new(lhs),
eq_token,
right,
});
} else if Precedence::Range >= base && input.peek(Token![..]) {
let limits: RangeLimits = input.parse()?;
let end = parse_range_end(input, &limits, allow_struct)?;
lhs = Expr::Range(ExprRange {
attrs: Vec::new(),
start: Some(Box::new(lhs)),
limits,
end,
});
} else if Precedence::Cast >= base && input.peek(Token![as]) {
let as_token: Token![as] = input.parse()?;
let allow_plus = false;
let allow_group_generic = false;
let ty = ty::parsing::ambig_ty(input, allow_plus, allow_group_generic)?;
check_cast(input)?;
lhs = Expr::Cast(ExprCast {
attrs: Vec::new(),
expr: Box::new(lhs),
as_token,
ty: Box::new(ty),
});
} else {
break;
}
}
Ok(lhs)
}
#[cfg(not(feature = "full"))]
fn parse_expr(input: ParseStream, mut lhs: Expr, base: Precedence) -> Result<Expr> {
loop {
let ahead = input.fork();
if let Ok(op) = ahead.parse::<BinOp>() {
let precedence = Precedence::of(&op);
if precedence < base {
break;
}
if precedence == Precedence::Compare {
if let Expr::Binary(lhs) = &lhs {
if Precedence::of(&lhs.op) == Precedence::Compare {
break;
}
}
}
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 {
rhs = parse_expr(
input,
rhs,
#[cfg(feature = "full")]
allow_struct,
next,
)?;
} else {
return Ok(Box::new(rhs));
}
}
}
fn peek_precedence(input: ParseStream) -> Precedence {
if let Ok(op) = input.fork().parse() {
Precedence::of(&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::Any
}
}
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::Any,
)
}
#[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)
}
#[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<kw::raw> = if input.peek(kw::raw)
&& (input.peek2(Token![mut]) || input.peek2(Token![const]))
{
Some(input.parse()?)
} else {
None
};
let mutability: Option<Token![mut]> = input.parse()?;
if raw.is_some() && mutability.is_none() {
input.parse::<Token![const]>()?;
}
let expr = Box::new(unary_expr(input, allow_struct)?);
if raw.is_some() {
Ok(Expr::Verbatim(verbatim::between(&begin, input)))
} 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)
}
}
#[cfg(feature = "full")]
fn trailer_expr(
begin: ParseBuffer,
mut attrs: Vec<Attribute>,
input: ParseStream,
allow_struct: AllowStruct,
) -> Result<Expr> {
let atom = atom_expr(input, allow_struct)?;
let mut e = trailer_helper(input, atom)?;
if let Expr::Verbatim(tokens) = &mut e {
*tokens = verbatim::between(&begin, input);
} else {
let inner_attrs = e.replace_attrs(Vec::new());
attrs.extend(inner_attrs);
e.replace_attrs(attrs);
}
Ok(e)
}
#[cfg(feature = "full")]
fn trailer_helper(input: ParseStream, mut e: Expr) -> Result<Expr> {
loop {
if input.peek(token::Paren) {
let content;
e = Expr::Call(ExprCall {
attrs: Vec::new(),
func: Box::new(e),
paren_token: parenthesized!(content in input),
args: content.parse_terminated(Expr::parse, Token![,])?,
});
} else if input.peek(Token![.])
&& !input.peek(Token![..])
&& match e {
Expr::Range(_) => false,
_ => true,
}
{
let mut dot_token: Token![.] = input.parse()?;
let float_token: Option<LitFloat> = input.parse()?;
if let Some(float_token) = float_token {
if multi_index(&mut e, &mut dot_token, float_token)? {
continue;
}
}
let await_token: Option<Token![await]> = input.parse()?;
if let Some(await_token) = await_token {
e = Expr::Await(ExprAwait {
attrs: Vec::new(),
base: Box::new(e),
dot_token,
await_token,
});
continue;
}
let member: Member = input.parse()?;
let turbofish = if member.is_named() && input.peek(Token![::]) {
Some(AngleBracketedGenericArguments::parse_turbofish(input)?)
} else {
None
};
if turbofish.is_some() || input.peek(token::Paren) {
if let Member::Named(method) = member {
let content;
e = Expr::MethodCall(ExprMethodCall {
attrs: Vec::new(),
receiver: Box::new(e),
dot_token,
method,
turbofish,
paren_token: parenthesized!(content in input),
args: content.parse_terminated(Expr::parse, Token![,])?,
});
continue;
}
}
e = Expr::Field(ExprField {
attrs: Vec::new(),
base: Box::new(e),
dot_token,
member,
});
} else if input.peek(token::Bracket) {
let content;
e = Expr::Index(ExprIndex {
attrs: Vec::new(),
expr: Box::new(e),
bracket_token: bracketed!(content in input),
index: content.parse()?,
});
} else if input.peek(Token![?]) {
e = Expr::Try(ExprTry {
attrs: Vec::new(),
expr: Box::new(e),
question_token: input.parse()?,
});
} else {
break;
}
}
Ok(e)
}
#[cfg(not(feature = "full"))]
fn trailer_expr(input: ParseStream) -> Result<Expr> {
let mut e = atom_expr(input)?;
loop {
if input.peek(token::Paren) {
let content;
e = Expr::Call(ExprCall {
attrs: Vec::new(),
func: Box::new(e),
paren_token: parenthesized!(content in input),
args: content.parse_terminated(Expr::parse, Token![,])?,
});
} else if input.peek(Token![.])
&& !input.peek(Token![..])
&& !input.peek2(Token![await])
{
let mut dot_token: Token![.] = input.parse()?;
let float_token: Option<LitFloat> = input.parse()?;
if let Some(float_token) = float_token {
if multi_index(&mut e, &mut dot_token, float_token)? {
continue;
}
}
let member: Member = input.parse()?;
let turbofish = if member.is_named() && input.peek(Token![::]) {
let colon2_token: Token![::] = input.parse()?;
let turbofish =
AngleBracketedGenericArguments::do_parse(Some(colon2_token), input)?;
Some(turbofish)
} else {
None
};
if turbofish.is_some() || input.peek(token::Paren) {
if let Member::Named(method) = member {
let content;
e = Expr::MethodCall(ExprMethodCall {
attrs: Vec::new(),
receiver: Box::new(e),
dot_token,
method,
turbofish,
paren_token: parenthesized!(content in input),
args: content.parse_terminated(Expr::parse, Token![,])?,
});
continue;
}
}
e = Expr::Field(ExprField {
attrs: Vec::new(),
base: Box::new(e),
dot_token,
member,
});
} else if input.peek(token::Bracket) {
let content;
e = Expr::Index(ExprIndex {
attrs: Vec::new(),
expr: Box::new(e),
bracket_token: bracketed!(content in input),
index: content.parse()?,
});
} else {
break;
}
}
Ok(e)
}
#[cfg(feature = "full")]
fn atom_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
if input.peek(token::Group) {
expr_group(input, allow_struct)
} else if input.peek(Lit) {
input.parse().map(Expr::Lit)
} else if input.peek(Token![async])
&& (input.peek2(token::Brace) || input.peek2(Token![move]) && input.peek3(token::Brace))
{
input.parse().map(Expr::Async)
} else if input.peek(Token![try]) && input.peek2(token::Brace) {
input.parse().map(Expr::TryBlock)
} else if input.peek(Token![|])
|| input.peek(Token![move])
|| input.peek(Token![for])
&& input.peek2(Token![<])
&& (input.peek3(Lifetime) || input.peek3(Token![>]))
|| input.peek(Token![const]) && !input.peek2(token::Brace)
|| input.peek(Token![static])
|| input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))
{
expr_closure(input, allow_struct).map(Expr::Closure)
} else if input.peek(kw::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]) {
expr_return(input, allow_struct).map(Expr::Return)
} else if input.peek(token::Bracket) {
array_or_repeat(input)
} else if input.peek(Token![let]) {
input.parse().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) {
input.call(expr_paren).map(Expr::Paren)
} 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();
input.parse::<kw::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 (qself, path) = path::parsing::qpath(input, true)?;
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(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for ExprMacro {
fn parse(input: ParseStream) -> Result<Self> {
Ok(ExprMacro {
attrs: Vec::new(),
mac: input.parse()?,
})
}
}
#[cfg(feature = "full")]
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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for ExprParen {
fn parse(input: ParseStream) -> Result<Self> {
expr_paren(input)
}
}
fn expr_paren(input: ParseStream) -> Result<ExprParen> {
let content;
Ok(ExprParen {
attrs: Vec::new(),
paren_token: parenthesized!(content in input),
expr: content.parse()?,
})
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for ExprLet {
fn parse(input: ParseStream) -> Result<Self> {
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 allow_struct = AllowStruct(false);
let lhs = unary_expr(input, allow_struct)?;
parse_expr(input, lhs, allow_struct, Precedence::Compare)?
}),
})
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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);
continue;
} 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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 mut arms = Vec::new();
while !content.is_empty() {
arms.push(content.call(Arm::parse)?);
}
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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for ExprReturn {
fn parse(input: ParseStream) -> Result<Self> {
let allow_struct = AllowStruct(true);
expr_return(input, allow_struct)
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, 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 can_begin_expr(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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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![:]) {
let _ = ambiguous_expr(input, allow_struct)?;
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 can_begin_expr(input) && (allow_struct.0 || !input.peek(token::Brace)) {
let expr = ambiguous_expr(input, allow_struct)?;
Some(Box::new(expr))
} else {
None
};
Ok(ExprBreak {
attrs: Vec::new(),
break_token,
label,
expr,
})
}
#[cfg(feature = "full")]
fn expr_return(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprReturn> {
Ok(ExprReturn {
attrs: Vec::new(),
return_token: input.parse()?,
expr: {
if can_begin_expr(input) {
let expr = ambiguous_expr(input, allow_struct)?;
Some(Box::new(expr))
} else {
None
}
},
})
}
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for ExprStruct {
fn parse(input: ParseStream) -> Result<Self> {
let (qself, path) = path::parsing::qpath(input, true)?;
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(doc_cfg, 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(doc_cfg, 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![..])
|| !allow_struct.0 && input.peek(token::Brace))
{
Ok(None)
} else {
let end = parse_binop_rhs(input, allow_struct, Precedence::Range)?;
Ok(Some(end))
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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![..=](dot3.spans)))
} else if dot_dot {
input.parse().map(RangeLimits::HalfOpen)
} else {
Err(lookahead.error())
}
}
}
#[cfg_attr(doc_cfg, 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 (qself, path) = path::parsing::qpath(input, true)?;
Ok(ExprPath { attrs, qself, path })
}
}
#[cfg_attr(doc_cfg, 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")]
#[cfg_attr(doc_cfg, 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 = requires_terminator(&body);
Box::new(body)
},
comma: {
if requires_comma && !input.is_empty() {
Some(input.parse()?)
} else {
input.parse()?
}
},
})
}
}
#[cfg_attr(doc_cfg, 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![.](dot_token.span),
member: Member::Unnamed(index),
});
let dot_span = float_token
.subspan(part_end..part_end + 1)
.unwrap_or(float_span);
*dot_token = Token![.](dot_span);
offset = part_end + 1;
}
Ok(!trailing_dot)
}
impl Member {
pub(crate) fn is_named(&self) -> bool {
match self {
Member::Named(_) => true,
Member::Unnamed(_) => false,
}
}
}
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::expr::{
requires_terminator, Arm, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBlock,
ExprBreak, ExprClosure, ExprConst, ExprContinue, ExprForLoop, ExprIf, ExprInfer, ExprLet,
ExprLoop, ExprMatch, ExprRange, ExprRepeat, ExprReturn, ExprTry, ExprTryBlock, ExprTuple,
ExprUnsafe, ExprWhile, ExprYield, Label, RangeLimits,
};
use crate::expr::{
ExprBinary, ExprCall, ExprCast, ExprField, ExprGroup, ExprIndex, ExprLit, ExprMacro,
ExprMethodCall, ExprParen, ExprPath, ExprReference, ExprStruct, ExprUnary, FieldValue,
Index, Member,
};
use crate::path;
#[cfg(feature = "full")]
use crate::token;
use proc_macro2::{Literal, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
#[cfg(feature = "full")]
fn wrap_bare_struct(tokens: &mut TokenStream, e: &Expr) {
if let Expr::Struct(_) = *e {
token::Paren::default().surround(tokens, |tokens| {
e.to_tokens(tokens);
});
} else {
e.to_tokens(tokens);
}
}
#[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) {}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprAssign {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.left.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
self.right.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprAwait {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.base.to_tokens(tokens);
self.dot_token.to_tokens(tokens);
self.await_token.to_tokens(tokens);
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprBinary {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.left.to_tokens(tokens);
self.op.to_tokens(tokens);
self.right.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprBreak {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.break_token.to_tokens(tokens);
self.label.to_tokens(tokens);
self.expr.to_tokens(tokens);
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprCall {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.func.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
self.args.to_tokens(tokens);
});
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprCast {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.expr.to_tokens(tokens);
self.as_token.to_tokens(tokens);
self.ty.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprClosure {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.lifetimes.to_tokens(tokens);
self.constness.to_tokens(tokens);
self.movability.to_tokens(tokens);
self.asyncness.to_tokens(tokens);
self.capture.to_tokens(tokens);
self.or1_token.to_tokens(tokens);
self.inputs.to_tokens(tokens);
self.or2_token.to_tokens(tokens);
self.output.to_tokens(tokens);
self.body.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprField {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.base.to_tokens(tokens);
self.dot_token.to_tokens(tokens);
self.member.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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);
wrap_bare_struct(tokens, &self.expr);
self.body.brace_token.surround(tokens, |tokens| {
inner_attrs_to_tokens(&self.attrs, tokens);
tokens.append_all(&self.body.stmts);
});
}
}
#[cfg_attr(doc_cfg, 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(doc_cfg, 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);
wrap_bare_struct(tokens, &expr.cond);
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;
}
other => {
token::Brace::default().surround(tokens, |tokens| other.to_tokens(tokens));
break;
}
}
}
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprIndex {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.expr.to_tokens(tokens);
self.bracket_token.surround(tokens, |tokens| {
self.index.to_tokens(tokens);
});
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprLet {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.let_token.to_tokens(tokens);
self.pat.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
wrap_bare_struct(tokens, &self.expr);
}
}
#[cfg_attr(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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);
wrap_bare_struct(tokens, &self.expr);
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);
let is_last = i == self.arms.len() - 1;
if !is_last && requires_terminator(&arm.body) && arm.comma.is_none() {
<Token![,]>::default().to_tokens(tokens);
}
}
});
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprMethodCall {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.receiver.to_tokens(tokens);
self.dot_token.to_tokens(tokens);
self.method.to_tokens(tokens);
self.turbofish.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
self.args.to_tokens(tokens);
});
}
}
#[cfg_attr(doc_cfg, 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(doc_cfg, 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_path(tokens, &self.qself, &self.path);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprRange {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.start.to_tokens(tokens);
self.limits.to_tokens(tokens);
self.end.to_tokens(tokens);
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprReference {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.and_token.to_tokens(tokens);
self.mutability.to_tokens(tokens);
self.expr.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprReturn {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.return_token.to_tokens(tokens);
self.expr.to_tokens(tokens);
}
}
#[cfg_attr(doc_cfg, 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_path(tokens, &self.qself, &self.path);
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![..](Span::call_site()).to_tokens(tokens);
}
self.rest.to_tokens(tokens);
});
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprTry {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.expr.to_tokens(tokens);
self.question_token.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(feature = "full")]
#[cfg_attr(doc_cfg, 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 self.elems.len() == 1 && !self.elems.trailing_punct() {
<Token![,]>::default().to_tokens(tokens);
}
});
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprUnary {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.op.to_tokens(tokens);
self.expr.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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(doc_cfg, 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);
wrap_bare_struct(tokens, &self.cond);
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(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for ExprYield {
fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens);
self.yield_token.to_tokens(tokens);
self.expr.to_tokens(tokens);
}
}
#[cfg(feature = "full")]
#[cfg_attr(doc_cfg, 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);
self.body.to_tokens(tokens);
self.comma.to_tokens(tokens);
}
}
#[cfg_attr(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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),
}
}
}
}