blob: 979de7191b1a04fec2b419c25e45f5c8b014893d [file] [log] [blame]
// Copyright 2018 Syn Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::*;
use proc_macro2::{Span, TokenStream};
use punctuated::Punctuated;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
#[cfg(feature = "full")]
use std::mem;
#[cfg(feature = "extra-traits")]
use tt::TokenStreamHelper;
ast_enum_of_structs! {
/// A Rust expression.
///
/// *This type is available if Syn is built with the `"derive"` or `"full"`
/// feature.*
///
/// # 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::IfLet(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 `IfLet` case we
/// get to use `expr.pat`, `expr.then_branch`, `expr.else_branch`.
///
/// The pattern is similar if the input expression is borrowed:
///
/// ```
/// # use syn::Expr;
/// #
/// # fn example(expr: &Expr) {
/// match *expr {
/// Expr::MethodCall(ref expr) => {
/// # }
/// # _ => {}
/// # }
/// # }
/// ```
///
/// This approach avoids repeating the variant names twice on every line.
///
/// ```
/// # use syn::{Expr, ExprMethodCall};
/// #
/// # fn example(expr: Expr) {
/// # match expr {
/// Expr::MethodCall(ExprMethodCall { method, args, .. }) => { // repetitive
/// # }
/// # _ => {}
/// # }
/// # }
/// ```
///
/// 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(ref 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`.
pub enum Expr {
/// A box expression: `box f`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Box(ExprBox #full {
pub attrs: Vec<Attribute>,
pub box_token: Token![box],
pub expr: Box<Expr>,
}),
/// A placement expression: `place <- value`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub InPlace(ExprInPlace #full {
pub attrs: Vec<Attribute>,
pub place: Box<Expr>,
pub arrow_token: Token![<-],
pub value: Box<Expr>,
}),
/// A slice literal expression: `[a, b, c, d]`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Array(ExprArray #full {
pub attrs: Vec<Attribute>,
pub bracket_token: token::Bracket,
pub elems: Punctuated<Expr, Token![,]>,
}),
/// A function call expression: `invoke(a, b)`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Call(ExprCall {
pub attrs: Vec<Attribute>,
pub func: Box<Expr>,
pub paren_token: token::Paren,
pub args: Punctuated<Expr, Token![,]>,
}),
/// A method call expression: `x.foo::<T>(a, b)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub MethodCall(ExprMethodCall #full {
pub attrs: Vec<Attribute>,
pub receiver: Box<Expr>,
pub dot_token: Token![.],
pub method: Ident,
pub turbofish: Option<MethodTurbofish>,
pub paren_token: token::Paren,
pub args: Punctuated<Expr, Token![,]>,
}),
/// A tuple expression: `(a, b, c, d)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Tuple(ExprTuple #full {
pub attrs: Vec<Attribute>,
pub paren_token: token::Paren,
pub elems: Punctuated<Expr, Token![,]>,
}),
/// A binary operation: `a + b`, `a * b`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Binary(ExprBinary {
pub attrs: Vec<Attribute>,
pub left: Box<Expr>,
pub op: BinOp,
pub right: Box<Expr>,
}),
/// A unary operation: `!x`, `*x`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Unary(ExprUnary {
pub attrs: Vec<Attribute>,
pub op: UnOp,
pub expr: Box<Expr>,
}),
/// A literal in place of an expression: `1`, `"foo"`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Lit(ExprLit {
pub attrs: Vec<Attribute>,
pub lit: Lit,
}),
/// A cast expression: `foo as f64`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Cast(ExprCast {
pub attrs: Vec<Attribute>,
pub expr: Box<Expr>,
pub as_token: Token![as],
pub ty: Box<Type>,
}),
/// A type ascription expression: `foo: f64`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Type(ExprType #full {
pub attrs: Vec<Attribute>,
pub expr: Box<Expr>,
pub colon_token: Token![:],
pub ty: Box<Type>,
}),
/// An `if` expression with an optional `else` block: `if expr { ... }
/// else { ... }`.
///
/// The `else` branch expression may only be an `If`, `IfLet`, or
/// `Block` expression, not any of the other types of expression.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub If(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>)>,
}),
/// An `if let` expression with an optional `else` block: `if let pat =
/// expr { ... } else { ... }`.
///
/// The `else` branch expression may only be an `If`, `IfLet`, or
/// `Block` expression, not any of the other types of expression.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub IfLet(ExprIfLet #full {
pub attrs: Vec<Attribute>,
pub if_token: Token![if],
pub let_token: Token![let],
pub pats: Punctuated<Pat, Token![|]>,
pub eq_token: Token![=],
pub expr: Box<Expr>,
pub then_branch: Block,
pub else_branch: Option<(Token![else], Box<Expr>)>,
}),
/// A while loop: `while expr { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub While(ExprWhile #full {
pub attrs: Vec<Attribute>,
pub label: Option<Label>,
pub while_token: Token![while],
pub cond: Box<Expr>,
pub body: Block,
}),
/// A while-let loop: `while let pat = expr { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub WhileLet(ExprWhileLet #full {
pub attrs: Vec<Attribute>,
pub label: Option<Label>,
pub while_token: Token![while],
pub let_token: Token![let],
pub pats: Punctuated<Pat, Token![|]>,
pub eq_token: Token![=],
pub expr: Box<Expr>,
pub body: Block,
}),
/// A for loop: `for pat in expr { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub ForLoop(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,
}),
/// Conditionless loop: `loop { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Loop(ExprLoop #full {
pub attrs: Vec<Attribute>,
pub label: Option<Label>,
pub loop_token: Token![loop],
pub body: Block,
}),
/// A `match` expression: `match n { Some(n) => {}, None => {} }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Match(ExprMatch #full {
pub attrs: Vec<Attribute>,
pub match_token: Token![match],
pub expr: Box<Expr>,
pub brace_token: token::Brace,
pub arms: Vec<Arm>,
}),
/// A closure expression: `|a, b| a + b`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Closure(ExprClosure #full {
pub attrs: Vec<Attribute>,
pub movability: Option<Token![static]>,
pub capture: Option<Token![move]>,
pub or1_token: Token![|],
pub inputs: Punctuated<FnArg, Token![,]>,
pub or2_token: Token![|],
pub output: ReturnType,
pub body: Box<Expr>,
}),
/// An unsafe block: `unsafe { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Unsafe(ExprUnsafe #full {
pub attrs: Vec<Attribute>,
pub unsafe_token: Token![unsafe],
pub block: Block,
}),
/// A blocked scope: `{ ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Block(ExprBlock #full {
pub attrs: Vec<Attribute>,
pub block: Block,
}),
/// An assignment expression: `a = compute()`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Assign(ExprAssign #full {
pub attrs: Vec<Attribute>,
pub left: Box<Expr>,
pub eq_token: Token![=],
pub right: Box<Expr>,
}),
/// A compound assignment expression: `counter += 1`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub AssignOp(ExprAssignOp #full {
pub attrs: Vec<Attribute>,
pub left: Box<Expr>,
pub op: BinOp,
pub right: Box<Expr>,
}),
/// Access of a named struct field (`obj.k`) or unnamed tuple struct
/// field (`obj.0`).
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Field(ExprField {
pub attrs: Vec<Attribute>,
pub base: Box<Expr>,
pub dot_token: Token![.],
pub member: Member,
}),
/// A square bracketed indexing expression: `vector[2]`.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Index(ExprIndex {
pub attrs: Vec<Attribute>,
pub expr: Box<Expr>,
pub bracket_token: token::Bracket,
pub index: Box<Expr>,
}),
/// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Range(ExprRange #full {
pub attrs: Vec<Attribute>,
pub from: Option<Box<Expr>>,
pub limits: RangeLimits,
pub to: Option<Box<Expr>>,
}),
/// 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.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Path(ExprPath {
pub attrs: Vec<Attribute>,
pub qself: Option<QSelf>,
pub path: Path,
}),
/// A referencing operation: `&a` or `&mut a`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Reference(ExprReference #full {
pub attrs: Vec<Attribute>,
pub and_token: Token![&],
pub mutability: Option<Token![mut]>,
pub expr: Box<Expr>,
}),
/// A `break`, with an optional label to break and an optional
/// expression.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Break(ExprBreak #full {
pub attrs: Vec<Attribute>,
pub break_token: Token![break],
pub label: Option<Lifetime>,
pub expr: Option<Box<Expr>>,
}),
/// A `continue`, with an optional label.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Continue(ExprContinue #full {
pub attrs: Vec<Attribute>,
pub continue_token: Token![continue],
pub label: Option<Lifetime>,
}),
/// A `return`, with an optional value to be returned.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Return(ExprReturn #full {
pub attrs: Vec<Attribute>,
pub return_token: Token![return],
pub expr: Option<Box<Expr>>,
}),
/// A macro invocation expression: `format!("{}", q)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Macro(ExprMacro #full {
pub attrs: Vec<Attribute>,
pub mac: Macro,
}),
/// 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 }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Struct(ExprStruct #full {
pub attrs: Vec<Attribute>,
pub path: Path,
pub brace_token: token::Brace,
pub fields: Punctuated<FieldValue, Token![,]>,
pub dot2_token: Option<Token![..]>,
pub rest: Option<Box<Expr>>,
}),
/// An array literal constructed from one repeated element: `[0u8; N]`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Repeat(ExprRepeat #full {
pub attrs: Vec<Attribute>,
pub bracket_token: token::Bracket,
pub expr: Box<Expr>,
pub semi_token: Token![;],
pub len: Box<Expr>,
}),
/// A parenthesized expression: `(a + b)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Paren(ExprParen {
pub attrs: Vec<Attribute>,
pub paren_token: token::Paren,
pub expr: Box<Expr>,
}),
/// 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`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Group(ExprGroup #full {
pub attrs: Vec<Attribute>,
pub group_token: token::Group,
pub expr: Box<Expr>,
}),
/// A try-expression: `expr?`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Try(ExprTry #full {
pub attrs: Vec<Attribute>,
pub expr: Box<Expr>,
pub question_token: Token![?],
}),
/// A catch expression: `do catch { ... }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Catch(ExprCatch #full {
pub attrs: Vec<Attribute>,
pub do_token: Token![do],
pub catch_token: Token![catch],
pub block: Block,
}),
/// A yield expression: `yield expr`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Yield(ExprYield #full {
pub attrs: Vec<Attribute>,
pub yield_token: Token![yield],
pub expr: Option<Box<Expr>>,
}),
/// Tokens in expression position not interpreted by Syn.
///
/// *This type is available if Syn is built with the `"derive"` or
/// `"full"` feature.*
pub Verbatim(ExprVerbatim #manual_extra_traits {
pub tts: TokenStream,
}),
}
}
#[cfg(feature = "extra-traits")]
impl Eq for ExprVerbatim {}
#[cfg(feature = "extra-traits")]
impl PartialEq for ExprVerbatim {
fn eq(&self, other: &Self) -> bool {
TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
}
}
#[cfg(feature = "extra-traits")]
impl Hash for ExprVerbatim {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
TokenStreamHelper(&self.tts).hash(state);
}
}
impl Expr {
// Not public API.
#[doc(hidden)]
#[cfg(feature = "full")]
pub fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {
match *self {
Expr::Box(ExprBox { ref mut attrs, .. })
| Expr::InPlace(ExprInPlace { ref mut attrs, .. })
| Expr::Array(ExprArray { ref mut attrs, .. })
| Expr::Call(ExprCall { ref mut attrs, .. })
| Expr::MethodCall(ExprMethodCall { ref mut attrs, .. })
| Expr::Tuple(ExprTuple { ref mut attrs, .. })
| Expr::Binary(ExprBinary { ref mut attrs, .. })
| Expr::Unary(ExprUnary { ref mut attrs, .. })
| Expr::Lit(ExprLit { ref mut attrs, .. })
| Expr::Cast(ExprCast { ref mut attrs, .. })
| Expr::Type(ExprType { ref mut attrs, .. })
| Expr::If(ExprIf { ref mut attrs, .. })
| Expr::IfLet(ExprIfLet { ref mut attrs, .. })
| Expr::While(ExprWhile { ref mut attrs, .. })
| Expr::WhileLet(ExprWhileLet { ref mut attrs, .. })
| Expr::ForLoop(ExprForLoop { ref mut attrs, .. })
| Expr::Loop(ExprLoop { ref mut attrs, .. })
| Expr::Match(ExprMatch { ref mut attrs, .. })
| Expr::Closure(ExprClosure { ref mut attrs, .. })
| Expr::Unsafe(ExprUnsafe { ref mut attrs, .. })
| Expr::Block(ExprBlock { ref mut attrs, .. })
| Expr::Assign(ExprAssign { ref mut attrs, .. })
| Expr::AssignOp(ExprAssignOp { ref mut attrs, .. })
| Expr::Field(ExprField { ref mut attrs, .. })
| Expr::Index(ExprIndex { ref mut attrs, .. })
| Expr::Range(ExprRange { ref mut attrs, .. })
| Expr::Path(ExprPath { ref mut attrs, .. })
| Expr::Reference(ExprReference { ref mut attrs, .. })
| Expr::Break(ExprBreak { ref mut attrs, .. })
| Expr::Continue(ExprContinue { ref mut attrs, .. })
| Expr::Return(ExprReturn { ref mut attrs, .. })
| Expr::Macro(ExprMacro { ref mut attrs, .. })
| Expr::Struct(ExprStruct { ref mut attrs, .. })
| Expr::Repeat(ExprRepeat { ref mut attrs, .. })
| Expr::Paren(ExprParen { ref mut attrs, .. })
| Expr::Group(ExprGroup { ref mut attrs, .. })
| Expr::Try(ExprTry { ref mut attrs, .. })
| Expr::Catch(ExprCatch { ref mut attrs, .. })
| Expr::Yield(ExprYield { ref mut attrs, .. }) => mem::replace(attrs, new),
Expr::Verbatim(_) => {
// TODO
Vec::new()
}
}
}
}
ast_enum! {
/// A struct or tuple struct field accessed in a struct literal or field
/// expression.
///
/// *This type is available if Syn is built with the `"derive"` or `"full"`
/// feature.*
pub enum Member {
/// A named field like `self.x`.
Named(Ident),
/// An unnamed field like `self.0`.
Unnamed(Index),
}
}
ast_struct! {
/// The index of an unnamed tuple struct field.
///
/// *This type is available if Syn is built with the `"derive"` or `"full"`
/// feature.*
pub struct Index #manual_extra_traits {
pub index: u32,
pub span: Span,
}
}
impl From<usize> for Index {
fn from(index: usize) -> Index {
assert!(index < u32::max_value() as usize);
Index {
index: index as u32,
span: Span::call_site(),
}
}
}
#[cfg(feature = "extra-traits")]
impl Eq for Index {}
#[cfg(feature = "extra-traits")]
impl PartialEq for Index {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Index {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state);
}
}
#[cfg(feature = "full")]
ast_struct! {
/// The `::<>` explicit type parameters passed to a method call:
/// `parse::<u64>()`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct MethodTurbofish {
pub colon2_token: Token![::],
pub lt_token: Token![<],
pub args: Punctuated<GenericMethodArgument, Token![,]>,
pub gt_token: Token![>],
}
}
#[cfg(feature = "full")]
ast_enum! {
/// An individual generic argument to a method, like `T`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub enum GenericMethodArgument {
/// A type argument.
Type(Type),
/// A const expression. Must be inside of a block.
///
/// NOTE: Identity expressions are represented as Type arguments, as
/// they are indistinguishable syntactically.
Const(Expr),
}
}
#[cfg(feature = "full")]
ast_struct! {
/// A field-value pair in a struct literal.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct FieldValue {
/// Attributes tagged on the field.
pub attrs: Vec<Attribute>,
/// Name or index of the field.
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![:]>,
/// Value of the field.
pub expr: Expr,
}
}
#[cfg(feature = "full")]
ast_struct! {
/// A lifetime labeling a `for`, `while`, or `loop`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct Label {
pub name: Lifetime,
pub colon_token: Token![:],
}
}
#[cfg(feature = "full")]
ast_struct! {
/// A braced block containing Rust statements.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct Block {
pub brace_token: token::Brace,
/// Statements in a block
pub stmts: Vec<Stmt>,
}
}
#[cfg(feature = "full")]
ast_enum! {
/// A statement, usually ending in a semicolon.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub enum Stmt {
/// A local (let) binding.
Local(Local),
/// An item definition.
Item(Item),
/// Expr without trailing semicolon.
Expr(Expr),
/// Expression with trailing semicolon.
Semi(Expr, Token![;]),
}
}
#[cfg(feature = "full")]
ast_struct! {
/// A local `let` binding: `let x: u64 = s.parse()?`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct Local {
pub attrs: Vec<Attribute>,
pub let_token: Token![let],
pub pats: Punctuated<Pat, Token![|]>,
pub ty: Option<(Token![:], Box<Type>)>,
pub init: Option<(Token![=], Box<Expr>)>,
pub semi_token: Token![;],
}
}
#[cfg(feature = "full")]
ast_enum_of_structs! {
/// A pattern in a local binding, function signature, match expression, or
/// various other places.
///
/// *This type is available if Syn is built with the `"full"` feature.*
///
/// # Syntax tree enum
///
/// This type is a [syntax tree enum].
///
/// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
// Clippy false positive
// https://github.com/Manishearth/rust-clippy/issues/1241
#[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))]
pub enum Pat {
/// A pattern that matches any value: `_`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Wild(PatWild {
pub underscore_token: Token![_],
}),
/// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Ident(PatIdent {
pub by_ref: Option<Token![ref]>,
pub mutability: Option<Token![mut]>,
pub ident: Ident,
pub subpat: Option<(Token![@], Box<Pat>)>,
}),
/// A struct or struct variant pattern: `Variant { x, y, .. }`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Struct(PatStruct {
pub path: Path,
pub brace_token: token::Brace,
pub fields: Punctuated<FieldPat, Token![,]>,
pub dot2_token: Option<Token![..]>,
}),
/// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub TupleStruct(PatTupleStruct {
pub path: Path,
pub pat: PatTuple,
}),
/// A path pattern like `Color::Red`, optionally qualified with a
/// self-type.
///
/// Unquailfied path patterns can legally refer to variants, structs,
/// constants or associated constants. Quailfied path patterns like
/// `<A>::B::C` and `<A as Trait>::B::C` can only legally refer to
/// associated constants.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Path(PatPath {
pub qself: Option<QSelf>,
pub path: Path,
}),
/// A tuple pattern: `(a, b)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Tuple(PatTuple {
pub paren_token: token::Paren,
pub front: Punctuated<Pat, Token![,]>,
pub dot2_token: Option<Token![..]>,
pub comma_token: Option<Token![,]>,
pub back: Punctuated<Pat, Token![,]>,
}),
/// A box pattern: `box v`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Box(PatBox {
pub box_token: Token![box],
pub pat: Box<Pat>,
}),
/// A reference pattern: `&mut (first, second)`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Ref(PatRef {
pub and_token: Token![&],
pub mutability: Option<Token![mut]>,
pub pat: Box<Pat>,
}),
/// A literal pattern: `0`.
///
/// This holds an `Expr` rather than a `Lit` because negative numbers
/// are represented as an `Expr::Unary`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Lit(PatLit {
pub expr: Box<Expr>,
}),
/// A range pattern: `1..=2`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Range(PatRange {
pub lo: Box<Expr>,
pub limits: RangeLimits,
pub hi: Box<Expr>,
}),
/// A dynamically sized slice pattern: `[a, b, i.., y, z]`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Slice(PatSlice {
pub bracket_token: token::Bracket,
pub front: Punctuated<Pat, Token![,]>,
pub middle: Option<Box<Pat>>,
pub dot2_token: Option<Token![..]>,
pub comma_token: Option<Token![,]>,
pub back: Punctuated<Pat, Token![,]>,
}),
/// A macro in expression position.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Macro(PatMacro {
pub mac: Macro,
}),
/// Tokens in pattern position not interpreted by Syn.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub Verbatim(PatVerbatim #manual_extra_traits {
pub tts: TokenStream,
}),
}
}
#[cfg(all(feature = "full", feature = "extra-traits"))]
impl Eq for PatVerbatim {}
#[cfg(all(feature = "full", feature = "extra-traits"))]
impl PartialEq for PatVerbatim {
fn eq(&self, other: &Self) -> bool {
TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
}
}
#[cfg(all(feature = "full", feature = "extra-traits"))]
impl Hash for PatVerbatim {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
TokenStreamHelper(&self.tts).hash(state);
}
}
#[cfg(feature = "full")]
ast_struct! {
/// One arm of a `match` expression: `0...10 => { return true; }`.
///
/// As in:
///
/// ```rust
/// # fn f() -> bool {
/// # let n = 0;
/// match n {
/// 0...10 => {
/// return true;
/// }
/// // ...
/// # _ => {}
/// }
/// # false
/// # }
/// ```
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct Arm {
pub attrs: Vec<Attribute>,
pub leading_vert: Option<Token![|]>,
pub pats: Punctuated<Pat, Token![|]>,
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.
///
/// *This type is available if Syn is built with the `"full"` feature.*
#[cfg_attr(feature = "clone-impls", derive(Copy))]
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_struct! {
/// A single field in a struct pattern.
///
/// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated
/// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token.
///
/// *This type is available if Syn is built with the `"full"` feature.*
pub struct FieldPat {
pub attrs: Vec<Attribute>,
pub member: Member,
pub colon_token: Option<Token![:]>,
pub pat: Box<Pat>,
}
}
#[cfg(any(feature = "parsing", feature = "printing"))]
#[cfg(feature = "full")]
fn arm_expr_requires_comma(expr: &Expr) -> bool {
// see https://github.com/rust-lang/rust/blob/eb8f2586e
// /src/libsyntax/parse/classify.rs#L17-L37
match *expr {
Expr::Unsafe(..)
| Expr::Block(..)
| Expr::If(..)
| Expr::IfLet(..)
| Expr::Match(..)
| Expr::While(..)
| Expr::WhileLet(..)
| Expr::Loop(..)
| Expr::ForLoop(..)
| Expr::Catch(..) => false,
_ => true,
}
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use path::parsing::qpath;
#[cfg(feature = "full")]
use path::parsing::ty_no_eq_after;
use buffer::Cursor;
#[cfg(feature = "full")]
use parse_error;
#[cfg(feature = "full")]
use proc_macro2::TokenStream;
use synom::PResult;
use synom::Synom;
// 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
macro_rules! ambiguous_expr {
($i:expr, $allow_struct:ident) => {
ambiguous_expr($i, $allow_struct, true)
};
}
// When we are parsing an optional suffix expression, we cannot allow blocks
// if structs are not allowed.
//
// Example:
//
// if break {} {}
//
// is ambiguous between:
//
// if (break {}) {}
// if (break) {} {}
#[cfg(feature = "full")]
macro_rules! opt_ambiguous_expr {
($i:expr, $allow_struct:ident) => {
option!($i, call!(ambiguous_expr, $allow_struct, $allow_struct))
};
}
impl Synom for Expr {
named!(parse -> Self, ambiguous_expr!(true));
fn description() -> Option<&'static str> {
Some("expression")
}
}
#[cfg(feature = "full")]
named!(expr_no_struct -> Expr, ambiguous_expr!(false));
// Parse an arbitrary expression.
#[cfg(feature = "full")]
fn ambiguous_expr(i: Cursor, allow_struct: bool, allow_block: bool) -> PResult<Expr> {
call!(i, assign_expr, allow_struct, allow_block)
}
#[cfg(not(feature = "full"))]
fn ambiguous_expr(i: Cursor, allow_struct: bool, allow_block: bool) -> PResult<Expr> {
// NOTE: We intentionally skip assign_expr, placement_expr, and
// range_expr, as they are not parsed in non-full mode.
call!(i, or_expr, allow_struct, allow_block)
}
// Parse a left-associative binary operator.
macro_rules! binop {
(
$name: ident,
$next: ident,
$submac: ident!( $($args:tt)* )
) => {
named!($name(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!($next, allow_struct, allow_block) >>
many0!(do_parse!(
op: $submac!($($args)*) >>
rhs: call!($next, allow_struct, true) >>
({
e = ExprBinary {
attrs: Vec::new(),
left: Box::new(e.into()),
op: op,
right: Box::new(rhs.into()),
}.into();
})
)) >>
(e)
));
}
}
// <placement> = <placement> ..
// <placement> += <placement> ..
// <placement> -= <placement> ..
// <placement> *= <placement> ..
// <placement> /= <placement> ..
// <placement> %= <placement> ..
// <placement> ^= <placement> ..
// <placement> &= <placement> ..
// <placement> |= <placement> ..
// <placement> <<= <placement> ..
// <placement> >>= <placement> ..
//
// NOTE: This operator is right-associative.
#[cfg(feature = "full")]
named!(assign_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(placement_expr, allow_struct, allow_block) >>
alt!(
do_parse!(
eq: punct!(=) >>
// Recurse into self to parse right-associative operator.
rhs: call!(assign_expr, allow_struct, true) >>
({
e = ExprAssign {
attrs: Vec::new(),
left: Box::new(e),
eq_token: eq,
right: Box::new(rhs),
}.into();
})
)
|
do_parse!(
op: call!(BinOp::parse_assign_op) >>
// Recurse into self to parse right-associative operator.
rhs: call!(assign_expr, allow_struct, true) >>
({
e = ExprAssignOp {
attrs: Vec::new(),
left: Box::new(e),
op: op,
right: Box::new(rhs),
}.into();
})
)
|
epsilon!()
) >>
(e)
));
// <range> <- <range> ..
//
// NOTE: The `in place { expr }` version of this syntax is parsed in
// `atom_expr`, not here.
//
// NOTE: This operator is right-associative.
#[cfg(feature = "full")]
named!(placement_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(range_expr, allow_struct, allow_block) >>
alt!(
do_parse!(
arrow: punct!(<-) >>
// Recurse into self to parse right-associative operator.
rhs: call!(placement_expr, allow_struct, true) >>
({
e = ExprInPlace {
attrs: Vec::new(),
// op: BinOp::Place(larrow),
place: Box::new(e),
arrow_token: arrow,
value: Box::new(rhs),
}.into();
})
)
|
epsilon!()
) >>
(e)
));
// <or> ... <or> ..
// <or> .. <or> ..
// <or> ..
//
// NOTE: This is currently parsed oddly - I'm not sure of what the exact
// rules are for parsing these expressions are, but this is not correct.
// For example, `a .. b .. c` is not a legal expression. It should not
// be parsed as either `(a .. b) .. c` or `a .. (b .. c)` apparently.
//
// NOTE: The form of ranges which don't include a preceding expression are
// parsed by `atom_expr`, rather than by this function.
#[cfg(feature = "full")]
named!(range_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(or_expr, allow_struct, allow_block) >>
many0!(do_parse!(
limits: syn!(RangeLimits) >>
// We don't want to allow blocks here if we don't allow structs. See
// the reasoning for `opt_ambiguous_expr!` above.
hi: option!(call!(or_expr, allow_struct, allow_struct)) >>
({
e = ExprRange {
attrs: Vec::new(),
from: Some(Box::new(e)),
limits: limits,
to: hi.map(|e| Box::new(e)),
}.into();
})
)) >>
(e)
));
// <and> || <and> ...
binop!(or_expr, and_expr, map!(punct!(||), BinOp::Or));
// <compare> && <compare> ...
binop!(and_expr, compare_expr, map!(punct!(&&), BinOp::And));
// <bitor> == <bitor> ...
// <bitor> != <bitor> ...
// <bitor> >= <bitor> ...
// <bitor> <= <bitor> ...
// <bitor> > <bitor> ...
// <bitor> < <bitor> ...
//
// NOTE: This operator appears to be parsed as left-associative, but errors
// if it is used in a non-associative manner.
binop!(
compare_expr,
bitor_expr,
alt!(
punct!(==) => { BinOp::Eq }
|
punct!(!=) => { BinOp::Ne }
|
// must be above Lt
punct!(<=) => { BinOp::Le }
|
// must be above Gt
punct!(>=) => { BinOp::Ge }
|
do_parse!(
// Make sure that we don't eat the < part of a <- operator
not!(punct!(<-)) >>
t: punct!(<) >>
(BinOp::Lt(t))
)
|
punct!(>) => { BinOp::Gt }
)
);
// <bitxor> | <bitxor> ...
binop!(
bitor_expr,
bitxor_expr,
do_parse!(not!(punct!(||)) >> not!(punct!(|=)) >> t: punct!(|) >> (BinOp::BitOr(t)))
);
// <bitand> ^ <bitand> ...
binop!(
bitxor_expr,
bitand_expr,
do_parse!(
// NOTE: Make sure we aren't looking at ^=.
not!(punct!(^=)) >> t: punct!(^) >> (BinOp::BitXor(t))
)
);
// <shift> & <shift> ...
binop!(
bitand_expr,
shift_expr,
do_parse!(
// NOTE: Make sure we aren't looking at && or &=.
not!(punct!(&&)) >> not!(punct!(&=)) >> t: punct!(&) >> (BinOp::BitAnd(t))
)
);
// <arith> << <arith> ...
// <arith> >> <arith> ...
binop!(
shift_expr,
arith_expr,
alt!(
punct!(<<) => { BinOp::Shl }
|
punct!(>>) => { BinOp::Shr }
)
);
// <term> + <term> ...
// <term> - <term> ...
binop!(
arith_expr,
term_expr,
alt!(
punct!(+) => { BinOp::Add }
|
punct!(-) => { BinOp::Sub }
)
);
// <cast> * <cast> ...
// <cast> / <cast> ...
// <cast> % <cast> ...
binop!(
term_expr,
cast_expr,
alt!(
punct!(*) => { BinOp::Mul }
|
punct!(/) => { BinOp::Div }
|
punct!(%) => { BinOp::Rem }
)
);
// <unary> as <ty>
// <unary> : <ty>
#[cfg(feature = "full")]
named!(cast_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(unary_expr, allow_struct, allow_block) >>
many0!(alt!(
do_parse!(
as_: keyword!(as) >>
// We can't accept `A + B` in cast expressions, as it's
// ambiguous with the + expression.
ty: call!(Type::without_plus) >>
({
e = ExprCast {
attrs: Vec::new(),
expr: Box::new(e),
as_token: as_,
ty: Box::new(ty),
}.into();
})
)
|
do_parse!(
colon: punct!(:) >>
// We can't accept `A + B` in cast expressions, as it's
// ambiguous with the + expression.
ty: call!(Type::without_plus) >>
({
e = ExprType {
attrs: Vec::new(),
expr: Box::new(e),
colon_token: colon,
ty: Box::new(ty),
}.into();
})
)
)) >>
(e)
));
// <unary> as <ty>
#[cfg(not(feature = "full"))]
named!(cast_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(unary_expr, allow_struct, allow_block) >>
many0!(do_parse!(
as_: keyword!(as) >>
// We can't accept `A + B` in cast expressions, as it's
// ambiguous with the + expression.
ty: call!(Type::without_plus) >>
({
e = ExprCast {
attrs: Vec::new(),
expr: Box::new(e),
as_token: as_,
ty: Box::new(ty),
}.into();
})
)) >>
(e)
));
// <UnOp> <trailer>
// & <trailer>
// &mut <trailer>
// box <trailer>
#[cfg(feature = "full")]
named!(unary_expr(allow_struct: bool, allow_block: bool) -> Expr, alt!(
do_parse!(
attrs: many0!(Attribute::parse_outer) >>
op: syn!(UnOp) >>
expr: call!(unary_expr, allow_struct, true) >>
(ExprUnary {
attrs: attrs,
op: op,
expr: Box::new(expr),
}.into())
)
|
do_parse!(
attrs: many0!(Attribute::parse_outer) >>
and: punct!(&) >>
mutability: option!(keyword!(mut)) >>
expr: call!(unary_expr, allow_struct, true) >>
(ExprReference {
attrs: attrs,
and_token: and,
mutability: mutability,
expr: Box::new(expr),
}.into())
)
|
do_parse!(
attrs: many0!(Attribute::parse_outer) >>
box_: keyword!(box) >>
expr: call!(unary_expr, allow_struct, true) >>
(ExprBox {
attrs: attrs,
box_token: box_,
expr: Box::new(expr),
}.into())
)
|
call!(trailer_expr, allow_struct, allow_block)
));
// XXX: This duplication is ugly
#[cfg(not(feature = "full"))]
named!(unary_expr(allow_struct: bool, allow_block: bool) -> Expr, alt!(
do_parse!(
op: syn!(UnOp) >>
expr: call!(unary_expr, allow_struct, true) >>
(ExprUnary {
attrs: Vec::new(),
op: op,
expr: Box::new(expr),
}.into())
)
|
call!(trailer_expr, allow_struct, allow_block)
));
#[cfg(feature = "full")]
fn take_outer(attrs: &mut Vec<Attribute>) -> Vec<Attribute> {
let mut outer = Vec::new();
let mut inner = Vec::new();
for attr in mem::replace(attrs, Vec::new()) {
match attr.style {
AttrStyle::Outer => outer.push(attr),
AttrStyle::Inner(_) => inner.push(attr),
}
}
*attrs = inner;
outer
}
// <atom> (..<args>) ...
// <atom> . <ident> (..<args>) ...
// <atom> . <ident> ...
// <atom> . <lit> ...
// <atom> [ <expr> ] ...
// <atom> ? ...
#[cfg(feature = "full")]
named!(trailer_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(atom_expr, allow_struct, allow_block) >>
outer_attrs: value!({
let mut attrs = e.replace_attrs(Vec::new());
let outer_attrs = take_outer(&mut attrs);
e.replace_attrs(attrs);
outer_attrs
}) >>
many0!(alt!(
tap!(args: and_call => {
let (paren, args) = args;
e = ExprCall {
attrs: Vec::new(),
func: Box::new(e),
args: args,
paren_token: paren,
}.into();
})
|
tap!(more: and_method_call => {
let mut call = more;
call.receiver = Box::new(e);
e = call.into();
})
|
tap!(field: and_field => {
let (token, member) = field;
e = ExprField {
attrs: Vec::new(),
base: Box::new(e),
dot_token: token,
member: member,
}.into();
})
|
tap!(i: and_index => {
let (bracket, i) = i;
e = ExprIndex {
attrs: Vec::new(),
expr: Box::new(e),
bracket_token: bracket,
index: Box::new(i),
}.into();
})
|
tap!(question: punct!(?) => {
e = ExprTry {
attrs: Vec::new(),
expr: Box::new(e),
question_token: question,
}.into();
})
)) >>
({
let mut attrs = outer_attrs;
attrs.extend(e.replace_attrs(Vec::new()));
e.replace_attrs(attrs);
e
})
));
// XXX: Duplication == ugly
#[cfg(not(feature = "full"))]
named!(trailer_expr(allow_struct: bool, allow_block: bool) -> Expr, do_parse!(
mut e: call!(atom_expr, allow_struct, allow_block) >>
many0!(alt!(
tap!(args: and_call => {
e = ExprCall {
attrs: Vec::new(),
func: Box::new(e),
paren_token: args.0,
args: args.1,
}.into();
})
|
tap!(field: and_field => {
let (token, member) = field;
e = ExprField {
attrs: Vec::new(),
base: Box::new(e),
dot_token: token,
member: member,
}.into();
})
|
tap!(i: and_index => {
e = ExprIndex {
attrs: Vec::new(),
expr: Box::new(e),
bracket_token: i.0,
index: Box::new(i.1),
}.into();
})
)) >>
(e)
));
// Parse all atomic expressions which don't have to worry about precedence
// interactions, as they are fully contained.
#[cfg(feature = "full")]
named!(atom_expr(allow_struct: bool, allow_block: bool) -> Expr, alt!(
syn!(ExprGroup) => { Expr::Group } // must be placed first
|
syn!(ExprLit) => { Expr::Lit } // must be before expr_struct
|
// must be before ExprStruct
call!(unstable_async_block) => { Expr::Verbatim }
|
// must be before expr_path
cond_reduce!(allow_struct, syn!(ExprStruct)) => { Expr::Struct }
|
syn!(ExprParen) => { Expr::Paren } // must be before expr_tup
|
syn!(ExprMacro) => { Expr::Macro } // must be before expr_path
|
call!(expr_break, allow_struct) // must be before expr_path
|
syn!(ExprContinue) => { Expr::Continue } // must be before expr_path
|
call!(expr_ret, allow_struct) // must be before expr_path
|
syn!(ExprArray) => { Expr::Array }
|
syn!(ExprTuple) => { Expr::Tuple }
|
syn!(ExprIf) => { Expr::If }
|
syn!(ExprIfLet) => { Expr::IfLet }
|
syn!(ExprWhile) => { Expr::While }
|
syn!(ExprWhileLet) => { Expr::WhileLet }
|
syn!(ExprForLoop) => { Expr::ForLoop }
|
syn!(ExprLoop) => { Expr::Loop }
|
syn!(ExprMatch) => { Expr::Match }
|
syn!(ExprCatch) => { Expr::Catch }
|
syn!(ExprYield) => { Expr::Yield }
|
syn!(ExprUnsafe) => { Expr::Unsafe }
|
call!(expr_closure, allow_struct)
|
cond_reduce!(allow_block, syn!(ExprBlock)) => { Expr::Block }
|
call!(unstable_labeled_block) => { Expr::Verbatim }
|
// NOTE: This is the prefix-form of range
call!(expr_range, allow_struct)
|
syn!(ExprPath) => { Expr::Path }
|
syn!(ExprRepeat) => { Expr::Repeat }
));
#[cfg(not(feature = "full"))]
named!(atom_expr(_allow_struct: bool, _allow_block: bool) -> Expr, alt!(
syn!(ExprLit) => { Expr::Lit }
|
syn!(ExprParen) => { Expr::Paren }
|
syn!(ExprPath) => { Expr::Path }
));
#[cfg(feature = "full")]
named!(expr_nosemi -> Expr, do_parse!(
nosemi: alt!(
syn!(ExprIf) => { Expr::If }
|
syn!(ExprIfLet) => { Expr::IfLet }
|
syn!(ExprWhile) => { Expr::While }
|
syn!(ExprWhileLet) => { Expr::WhileLet }
|
syn!(ExprForLoop) => { Expr::ForLoop }
|
syn!(ExprLoop) => { Expr::Loop }
|
syn!(ExprMatch) => { Expr::Match }
|
syn!(ExprCatch) => { Expr::Catch }
|
syn!(ExprYield) => { Expr::Yield }
|
syn!(ExprUnsafe) => { Expr::Unsafe }
|
syn!(ExprBlock) => { Expr::Block }
|
call!(unstable_labeled_block) => { Expr::Verbatim }
) >>
// If the next token is a `.` or a `?` it is special-cased to parse
// as an expression instead of a blockexpression.
not!(punct!(.)) >>
not!(punct!(?)) >>
(nosemi)
));
impl Synom for ExprLit {
#[cfg(not(feature = "full"))]
named!(parse -> Self, do_parse!(
lit: syn!(Lit) >>
(ExprLit {
attrs: Vec::new(),
lit: lit,
})
));
#[cfg(feature = "full")]
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
lit: syn!(Lit) >>
(ExprLit {
attrs: attrs,
lit: lit,
})
));
fn description() -> Option<&'static str> {
Some("literal")
}
}
#[cfg(feature = "full")]
impl Synom for ExprMacro {
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
mac: syn!(Macro) >>
(ExprMacro {
attrs: attrs,
mac: mac,
})
));
fn description() -> Option<&'static str> {
Some("macro invocation expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprGroup {
named!(parse -> Self, do_parse!(
e: grouped!(syn!(Expr)) >>
(ExprGroup {
attrs: Vec::new(),
expr: Box::new(e.1),
group_token: e.0,
})
));
fn description() -> Option<&'static str> {
Some("expression surrounded by invisible delimiters")
}
}
impl Synom for ExprParen {
#[cfg(not(feature = "full"))]
named!(parse -> Self, do_parse!(
e: parens!(syn!(Expr)) >>
(ExprParen {
attrs: Vec::new(),
paren_token: e.0,
expr: Box::new(e.1),
})
));
#[cfg(feature = "full")]
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
e: parens!(tuple!(
many0!(Attribute::parse_inner),
syn!(Expr),
)) >>
(ExprParen {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((e.1).0);
attrs
},
paren_token: e.0,
expr: Box::new((e.1).1),
})
));
fn description() -> Option<&'static str> {
Some("parenthesized expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprArray {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
elems: brackets!(tuple!(
many0!(Attribute::parse_inner),
call!(Punctuated::parse_terminated),
)) >>
(ExprArray {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((elems.1).0);
attrs
},
bracket_token: elems.0,
elems: (elems.1).1,
})
));
fn description() -> Option<&'static str> {
Some("array expression")
}
}
named!(and_call -> (token::Paren, Punctuated<Expr, Token![,]>),
parens!(Punctuated::parse_terminated)
);
#[cfg(feature = "full")]
named!(and_method_call -> ExprMethodCall, do_parse!(
dot: punct!(.) >>
method: syn!(Ident) >>
turbofish: option!(tuple!(
punct!(::),
punct!(<),
call!(Punctuated::parse_terminated),
punct!(>),
)) >>
args: parens!(Punctuated::parse_terminated) >>
({
ExprMethodCall {
attrs: Vec::new(),
// this expr will get overwritten after being returned
receiver: Box::new(Expr::Verbatim(ExprVerbatim {
tts: TokenStream::new(),
})),
method: method,
turbofish: turbofish.map(|fish| MethodTurbofish {
colon2_token: fish.0,
lt_token: fish.1,
args: fish.2,
gt_token: fish.3,
}),
args: args.1,
paren_token: args.0,
dot_token: dot,
}
})
));
#[cfg(feature = "full")]
impl Synom for GenericMethodArgument {
// TODO parse const generics as well
named!(parse -> Self, map!(ty_no_eq_after, GenericMethodArgument::Type));
fn description() -> Option<&'static str> {
Some("generic method argument")
}
}
#[cfg(feature = "full")]
impl Synom for ExprTuple {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
elems: parens!(tuple!(
many0!(Attribute::parse_inner),
call!(Punctuated::parse_terminated),
)) >>
(ExprTuple {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((elems.1).0);
attrs
},
elems: (elems.1).1,
paren_token: elems.0,
})
));
fn description() -> Option<&'static str> {
Some("tuple")
}
}
#[cfg(feature = "full")]
impl Synom for ExprIfLet {
named!(parse -> Self, do_parse!(
if_: keyword!(if) >>
let_: keyword!(let) >>
pats: call!(Punctuated::parse_separated_nonempty) >>
eq: punct!(=) >>
cond: expr_no_struct >>
then_block: braces!(Block::parse_within) >>
else_block: option!(else_block) >>
(ExprIfLet {
attrs: Vec::new(),
pats: pats,
let_token: let_,
eq_token: eq,
expr: Box::new(cond),
then_branch: Block {
brace_token: then_block.0,
stmts: then_block.1,
},
if_token: if_,
else_branch: else_block,
})
));
fn description() -> Option<&'static str> {
Some("`if let` expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprIf {
named!(parse -> Self, do_parse!(
if_: keyword!(if) >>
cond: expr_no_struct >>
then_block: braces!(Block::parse_within) >>
else_block: option!(else_block) >>
(ExprIf {
attrs: Vec::new(),
cond: Box::new(cond),
then_branch: Block {
brace_token: then_block.0,
stmts: then_block.1,
},
if_token: if_,
else_branch: else_block,
})
));
fn description() -> Option<&'static str> {
Some("`if` expression")
}
}
#[cfg(feature = "full")]
named!(else_block -> (Token![else], Box<Expr>), do_parse!(
else_: keyword!(else) >>
expr: alt!(
syn!(ExprIf) => { Expr::If }
|
syn!(ExprIfLet) => { Expr::IfLet }
|
do_parse!(
else_block: braces!(Block::parse_within) >>
(Expr::Block(ExprBlock {
attrs: Vec::new(),
block: Block {
brace_token: else_block.0,
stmts: else_block.1,
},
}))
)
) >>
(else_, Box::new(expr))
));
#[cfg(feature = "full")]
impl Synom for ExprForLoop {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
label: option!(syn!(Label)) >>
for_: keyword!(for) >>
pat: syn!(Pat) >>
in_: keyword!(in) >>
expr: expr_no_struct >>
block: braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
(ExprForLoop {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((block.1).0);
attrs
},
label: label,
for_token: for_,
pat: Box::new(pat),
in_token: in_,
expr: Box::new(expr),
body: Block {
brace_token: block.0,
stmts: (block.1).1,
},
})
));
fn description() -> Option<&'static str> {
Some("`for` loop")
}
}
#[cfg(feature = "full")]
impl Synom for ExprLoop {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
label: option!(syn!(Label)) >>
loop_: keyword!(loop) >>
block: braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
(ExprLoop {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((block.1).0);
attrs
},
label: label,
loop_token: loop_,
body: Block {
brace_token: block.0,
stmts: (block.1).1,
},
})
));
fn description() -> Option<&'static str> {
Some("`loop`")
}
}
#[cfg(feature = "full")]
impl Synom for ExprMatch {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
match_: keyword!(match) >>
obj: expr_no_struct >>
braced_content: braces!(tuple!(
many0!(Attribute::parse_inner),
many0!(syn!(Arm)),
)) >>
(ExprMatch {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((braced_content.1).0);
attrs
},
expr: Box::new(obj),
match_token: match_,
brace_token: braced_content.0,
arms: (braced_content.1).1,
})
));
fn description() -> Option<&'static str> {
Some("`match` expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprCatch {
named!(parse -> Self, do_parse!(
do_: keyword!(do) >>
catch_: keyword!(catch) >>
catch_block: syn!(Block) >>
(ExprCatch {
attrs: Vec::new(),
block: catch_block,
do_token: do_,
catch_token: catch_,
})
));
fn description() -> Option<&'static str> {
Some("`catch` expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprYield {
named!(parse -> Self, do_parse!(
yield_: keyword!(yield) >>
expr: option!(syn!(Expr)) >>
(ExprYield {
attrs: Vec::new(),
yield_token: yield_,
expr: expr.map(Box::new),
})
));
fn description() -> Option<&'static str> {
Some("`yield` expression")
}
}
#[cfg(feature = "full")]
impl Synom for Arm {
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
leading_vert: option!(punct!(|)) >>
pats: call!(Punctuated::parse_separated_nonempty) >>
guard: option!(tuple!(keyword!(if), syn!(Expr))) >>
fat_arrow: punct!(=>) >>
body: do_parse!(
expr: alt!(expr_nosemi | syn!(Expr)) >>
comma: switch!(value!(arm_expr_requires_comma(&expr)),
true => alt!(
input_end!() => { |_| None }
|
punct!(,) => { Some }
)
|
false => option!(punct!(,))
) >>
(expr, comma)
) >>
(Arm {
fat_arrow_token: fat_arrow,
attrs: attrs,
leading_vert: leading_vert,
pats: pats,
guard: guard.map(|(if_, guard)| (if_, Box::new(guard))),
body: Box::new(body.0),
comma: body.1,
})
));
fn description() -> Option<&'static str> {
Some("`match` arm")
}
}
#[cfg(feature = "full")]
named!(expr_closure(allow_struct: bool) -> Expr, do_parse!(
begin: call!(verbatim::grab_cursor) >>
attrs: many0!(Attribute::parse_outer) >>
asyncness: option!(keyword!(async)) >>
movability: option!(cond_reduce!(asyncness.is_none(), keyword!(static))) >>
capture: option!(keyword!(move)) >>
or1: punct!(|) >>
inputs: call!(Punctuated::parse_terminated_with, fn_arg) >>
or2: punct!(|) >>
ret_and_body: alt!(
do_parse!(
arrow: punct!(->) >>
ty: syn!(Type) >>
body: syn!(Block) >>
(
ReturnType::Type(arrow, Box::new(ty)),
Expr::Block(ExprBlock {
attrs: Vec::new(),
block: body,
},
))
)
|
map!(ambiguous_expr!(allow_struct), |e| (ReturnType::Default, e))
) >>
end: call!(verbatim::grab_cursor) >>
({
if asyncness.is_some() {
// TODO: include asyncness in ExprClosure
// https://github.com/dtolnay/syn/issues/396
Expr::Verbatim(ExprVerbatim {
tts: verbatim::token_range(begin..end),
})
} else {
Expr::Closure(ExprClosure {
attrs: attrs,
movability: movability,
capture: capture,
or1_token: or1,
inputs: inputs,
or2_token: or2,
output: ret_and_body.0,
body: Box::new(ret_and_body.1),
})
}
})
));
#[cfg(feature = "full")]
named!(unstable_async_block -> ExprVerbatim, do_parse!(
begin: call!(verbatim::grab_cursor) >>
many0!(Attribute::parse_outer) >>
keyword!(async) >>
option!(keyword!(move)) >>
syn!(Block) >>
end: call!(verbatim::grab_cursor) >>
(ExprVerbatim {
tts: verbatim::token_range(begin..end),
})
));
#[cfg(feature = "full")]
named!(fn_arg -> FnArg, do_parse!(
pat: syn!(Pat) >>
ty: option!(tuple!(punct!(:), syn!(Type))) >>
({
if let Some((colon, ty)) = ty {
FnArg::Captured(ArgCaptured {
pat: pat,
colon_token: colon,
ty: ty,
})
} else {
FnArg::Inferred(pat)
}
})
));
#[cfg(feature = "full")]
impl Synom for ExprWhile {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
label: option!(syn!(Label)) >>
while_: keyword!(while) >>
cond: expr_no_struct >>
block: braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
(ExprWhile {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((block.1).0);
attrs
},
label: label,
while_token: while_,
cond: Box::new(cond),
body: Block {
brace_token: block.0,
stmts: (block.1).1,
},
})
));
fn description() -> Option<&'static str> {
Some("`while` expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprWhileLet {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
label: option!(syn!(Label)) >>
while_: keyword!(while) >>
let_: keyword!(let) >>
pats: call!(Punctuated::parse_separated_nonempty) >>
eq: punct!(=) >>
value: expr_no_struct >>
block: braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
(ExprWhileLet {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((block.1).0);
attrs
},
label: label,
while_token: while_,
let_token: let_,
pats: pats,
eq_token: eq,
expr: Box::new(value),
body: Block {
brace_token: block.0,
stmts: (block.1).1,
},
})
));
fn description() -> Option<&'static str> {
Some("`while let` expression")
}
}
#[cfg(feature = "full")]
impl Synom for Label {
named!(parse -> Self, do_parse!(
name: syn!(Lifetime) >>
colon: punct!(:) >>
(Label {
name: name,
colon_token: colon,
})
));
fn description() -> Option<&'static str> {
Some("`while let` expression")
}
}
#[cfg(feature = "full")]
impl Synom for ExprContinue {
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
cont: keyword!(continue) >>
label: option!(syn!(Lifetime)) >>
(ExprContinue {
attrs: attrs,
continue_token: cont,
label: label,
})
));
fn description() -> Option<&'static str> {
Some("`continue`")
}
}
#[cfg(feature = "full")]
named!(expr_break(allow_struct: bool) -> Expr, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
break_: keyword!(break) >>
label: option!(syn!(Lifetime)) >>
// We can't allow blocks after a `break` expression when we wouldn't
// allow structs, as this expression is ambiguous.
val: opt_ambiguous_expr!(allow_struct) >>
(ExprBreak {
attrs: attrs,
label: label,
expr: val.map(Box::new),
break_token: break_,
}.into())
));
#[cfg(feature = "full")]
named!(expr_ret(allow_struct: bool) -> Expr, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
return_: keyword!(return) >>
// NOTE: return is greedy and eats blocks after it even when in a
// position where structs are not allowed, such as in if statement
// conditions. For example:
//
// if return { println!("A") } {} // Prints "A"
ret_value: option!(ambiguous_expr!(allow_struct)) >>
(ExprReturn {
attrs: attrs,
expr: ret_value.map(Box::new),
return_token: return_,
}.into())
));
#[cfg(feature = "full")]
impl Synom for ExprStruct {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
path: syn!(Path) >>
data: braces!(do_parse!(
inner_attrs: many0!(Attribute::parse_inner) >>
fields: call!(Punctuated::parse_terminated) >>
base: option!(cond!(fields.empty_or_trailing(), do_parse!(
dots: punct!(..) >>
base: syn!(Expr) >>
(dots, base)
))) >>
(inner_attrs, fields, base)
)) >>
({
let (brace, (inner_attrs, fields, base)) = data;
let (dots, rest) = match base.and_then(|b| b) {
Some((dots, base)) => (Some(dots), Some(base)),
None => (None, None),
};
ExprStruct {
attrs: {
let mut attrs = outer_attrs;
attrs.extend(inner_attrs);
attrs
},
brace_token: brace,
path: path,
fields: fields,
dot2_token: dots,
rest: rest.map(Box::new),
}
})
));
fn description() -> Option<&'static str> {
Some("struct literal expression")
}
}
#[cfg(feature = "full")]
impl Synom for FieldValue {
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
field_value: alt!(
tuple!(syn!(Member), map!(punct!(:), Some), syn!(Expr))
|
map!(syn!(Ident), |name| (
Member::Named(name.clone()),
None,
Expr::Path(ExprPath {
attrs: Vec::new(),
qself: None,
path: name.into(),
}),
))
) >>
(FieldValue {
attrs: attrs,
member: field_value.0,
colon_token: field_value.1,
expr: field_value.2,
})
));
fn description() -> Option<&'static str> {
Some("field-value pair: `field: value`")
}
}
#[cfg(feature = "full")]
impl Synom for ExprRepeat {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
data: brackets!(tuple!(
many0!(Attribute::parse_inner),
syn!(Expr),
punct!(;),
syn!(Expr),
)) >>
(ExprRepeat {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((data.1).0);
attrs
},
expr: Box::new((data.1).1),
len: Box::new((data.1).3),
bracket_token: data.0,
semi_token: (data.1).2,
})
));
fn description() -> Option<&'static str> {
Some("repeated array literal: `[val; N]`")
}
}
#[cfg(feature = "full")]
impl Synom for ExprUnsafe {
named!(parse -> Self, do_parse!(
unsafe_: keyword!(unsafe) >>
b: syn!(Block) >>
(ExprUnsafe {
attrs: Vec::new(),
unsafe_token: unsafe_,
block: b,
})
));
fn description() -> Option<&'static str> {
Some("unsafe block: `unsafe { .. }`")
}
}
#[cfg(feature = "full")]
impl Synom for ExprBlock {
named!(parse -> Self, do_parse!(
outer_attrs: many0!(Attribute::parse_outer) >>
block: braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
(ExprBlock {
attrs: {
let mut attrs = outer_attrs;
attrs.extend((block.1).0);
attrs
},
block: Block {
brace_token: block.0,
stmts: (block.1).1,
},
})
));
fn description() -> Option<&'static str> {
Some("block: `{ .. }`")
}
}
#[cfg(feature = "full")]
named!(unstable_labeled_block -> ExprVerbatim, do_parse!(
begin: call!(verbatim::grab_cursor) >>
many0!(Attribute::parse_outer) >>
syn!(Label) >>
braces!(tuple!(
many0!(Attribute::parse_inner),
call!(Block::parse_within),
)) >>
end: call!(verbatim::grab_cursor) >>
(ExprVerbatim {
tts: verbatim::token_range(begin..end),
})
));
#[cfg(feature = "full")]
named!(expr_range(allow_struct: bool) -> Expr, do_parse!(
limits: syn!(RangeLimits) >>
hi: opt_ambiguous_expr!(allow_struct) >>
(ExprRange {
attrs: Vec::new(),
from: None,
to: hi.map(Box::new),
limits: limits,
}.into())
));
#[cfg(feature = "full")]
impl Synom for RangeLimits {
named!(parse -> Self, alt!(
// Must come before Dot2
punct!(..=) => { RangeLimits::Closed }
|
// Must come before Dot2
punct!(...) => { |dot3| RangeLimits::Closed(Token![..=](dot3.0)) }
|
punct!(..) => { RangeLimits::HalfOpen }
));
fn description() -> Option<&'static str> {
Some("range limit: `..`, `...` or `..=`")
}
}
impl Synom for ExprPath {
#[cfg(not(feature = "full"))]
named!(parse -> Self, do_parse!(
pair: qpath >>
(ExprPath {
attrs: Vec::new(),
qself: pair.0,
path: pair.1,
})
));
#[cfg(feature = "full")]
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
pair: qpath >>
(ExprPath {
attrs: attrs,
qself: pair.0,
path: pair.1,
})
));
fn description() -> Option<&'static str> {
Some("path: `a::b::c`")
}
}
named!(and_field -> (Token![.], Member), tuple!(punct!(.), syn!(Member)));
named!(and_index -> (token::Bracket, Expr), brackets!(syn!(Expr)));
#[cfg(feature = "full")]
impl Synom for Block {
named!(parse -> Self, do_parse!(
stmts: braces!(Block::parse_within) >>
(Block {
brace_token: stmts.0,
stmts: stmts.1,
})
));
fn description() -> Option<&'static str> {
Some("block: `{ .. }`")
}
}
#[cfg(feature = "full")]
impl Block {
named!(pub parse_within -> Vec<Stmt>, do_parse!(
many0!(punct!(;)) >>
mut standalone: many0!(do_parse!(
stmt: syn!(Stmt) >>
many0!(punct!(;)) >>
(stmt)
)) >>
last: option!(do_parse!(
attrs: many0!(Attribute::parse_outer) >>
mut e: syn!(Expr) >>
({
e.replace_attrs(attrs);
Stmt::Expr(e)
})
)) >>
(match last {
None => standalone,
Some(last) => {
standalone.push(last);
standalone
}
})
));
}
#[cfg(feature = "full")]
impl Synom for Stmt {
named!(parse -> Self, alt!(
stmt_mac
|
stmt_local
|
stmt_item
|
stmt_blockexpr
|
stmt_expr
));
fn description() -> Option<&'static str> {
Some("statement")
}
}
#[cfg(feature = "full")]
named!(stmt_mac -> Stmt, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
what: call!(Path::parse_mod_style) >>
bang: punct!(!) >>
// Only parse braces here; paren and bracket will get parsed as
// expression statements
data: braces!(syn!(TokenStream)) >>
semi: option!(punct!(;)) >>
(Stmt::Item(Item::Macro(ItemMacro {
attrs: attrs,
ident: None,
mac: Macro {
path: what,
bang_token: bang,
delimiter: MacroDelimiter::Brace(data.0),
tts: data.1,
},
semi_token: semi,
})))
));
#[cfg(feature = "full")]
named!(stmt_local -> Stmt, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
let_: keyword!(let) >>
pats: call!(Punctuated::parse_separated_nonempty) >>
ty: option!(tuple!(punct!(:), syn!(Type))) >>
init: option!(tuple!(punct!(=), syn!(Expr))) >>
semi: punct!(;) >>
(Stmt::Local(Local {
attrs: attrs,
let_token: let_,
pats: pats,
ty: ty.map(|(colon, ty)| (colon, Box::new(ty))),
init: init.map(|(eq, expr)| (eq, Box::new(expr))),
semi_token: semi,
}))