ast_enum_of_structs! { /// A Rust expression. /// /// *This type is available only if Syn is built with the `"derive"` or `"full"` /// feature, but most of the variants are not available unless "full" is enabled.* /// /// # Syntax tree enums /// /// This type is a syntax tree enum. In Syn this and other syntax tree enums /// are designed to be traversed using the following rebinding idiom. /// /// ``` /// # use syn::Expr; /// # /// # fn example(expr: Expr) { /// # const IGNORE: &str = stringify! { /// let expr: Expr = /* ... */; /// # }; /// match expr { /// Expr::MethodCall(expr) => { /// /* ... */ /// } /// Expr::Cast(expr) => { /// /* ... */ /// } /// Expr::If(expr) => { /// /* ... */ /// } /// /// /* ... */ /// # _ => {} /// # } /// # } /// ``` /// /// We begin with a variable `expr` of type `Expr` that has no fields /// (because it is an enum), and by matching on it and rebinding a variable /// with the same name `expr` we effectively imbue our variable with all of /// the data fields provided by the variant that it turned out to be. So for /// example above if we ended up in the `MethodCall` case then we get to use /// `expr.receiver`, `expr.args` etc; if we ended up in the `If` case we get /// to use `expr.cond`, `expr.then_branch`, `expr.else_branch`. /// /// This approach avoids repeating the variant names twice on every line. /// /// ``` /// # use syn::{Expr, ExprMethodCall}; /// # /// # fn example(expr: Expr) { /// // Repetitive; recommend not doing this. /// match expr { /// Expr::MethodCall(ExprMethodCall { method, args, .. }) => { /// # } /// # _ => {} /// # } /// # } /// ``` /// /// In general, the name to which a syntax tree enum variant is bound should /// be a suitable name for the complete syntax tree enum type. /// /// ``` /// # use syn::{Expr, ExprField}; /// # /// # fn example(discriminant: ExprField) { /// // Binding is called `base` which is the name I would use if I were /// // assigning `*discriminant.base` without an `if let`. /// if let Expr::Tuple(base) = *discriminant.base { /// # } /// # } /// ``` /// /// A sign that you may not be choosing the right variable names is if you /// see names getting repeated in your code, like accessing /// `receiver.receiver` or `pat.pat` or `cond.cond`. #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] #[non_exhaustive] pubenum Expr { /// A slice literal expression: `[a, b, c, d]`.
Array(ExprArray),
/// An assignment expression: `a = compute()`.
Assign(ExprAssign),
/// An async block: `async { ... }`. Async(ExprAsync),
/// An await expression: `fut.await`. Await(ExprAwait),
/// A `break`, with an optional label to break and an optional /// expression. Break(ExprBreak),
/// A function call expression: `invoke(a, b)`.
Call(ExprCall),
/// A cast expression: `foo as f64`.
Cast(ExprCast),
/// A closure expression: `|a, b| a + b`.
Closure(ExprClosure),
/// A const block: `const { ... }`. Const(ExprConst),
/// A `continue`, with an optional label. Continue(ExprContinue),
/// Access of a named struct field (`obj.k`) or unnamed tuple struct /// field (`obj.0`).
Field(ExprField),
/// A for loop: `for pat in expr { ... }`.
ForLoop(ExprForLoop),
/// An expression contained within invisible delimiters. /// /// This variant is important for faithfully representing the precedence /// of expressions and is related to `None`-delimited spans in a /// `TokenStream`.
Group(ExprGroup),
/// An `if` expression with an optional `else` block: `if expr { ... } /// else { ... }`. /// /// The `else` branch expression may only be an `If` or `Block` /// expression, not any of the other types of expression. If(ExprIf),
/// A square bracketed indexing expression: `vector[2]`.
Index(ExprIndex),
/// The inferred value of a const generic argument, denoted `_`.
Infer(ExprInfer),
/// A `let` guard: `let Some(x) = opt`. Let(ExprLet),
/// A literal in place of an expression: `1`, `"foo"`.
Lit(ExprLit),
/// A macro invocation expression: `format!("{}", q)`. Macro(ExprMacro),
/// A `match` expression: `match n { Some(n) => {}, None => {} }`. Match(ExprMatch),
/// A method call expression: `x.foo::<T>(a, b)`.
MethodCall(ExprMethodCall),
/// A parenthesized expression: `(a + b)`.
Paren(ExprParen),
/// A path like `std::mem::replace` possibly containing generic /// parameters and a qualified self-type. /// /// A plain identifier like `x` is a path of length 1.
Path(ExprPath),
/// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
Range(ExprRange),
/// A referencing operation: `&a` or `&mut a`.
Reference(ExprReference),
/// An array literal constructed from one repeated element: `[0u8; N]`.
Repeat(ExprRepeat),
/// A `return`, with an optional value to be returned. Return(ExprReturn),
/// A struct literal expression: `Point { x: 1, y: 1 }`. /// /// The `rest` provides the value of the remaining fields as in `S { a: /// 1, b: 1, ..rest }`. Struct(ExprStruct),
/// A try-expression: `expr?`. Try(ExprTry),
/// A try block: `try { ... }`.
TryBlock(ExprTryBlock),
/// A tuple expression: `(a, b, c, d)`.
Tuple(ExprTuple),
/// A unary operation: `!x`, `*x`.
Unary(ExprUnary),
/// An unsafe block: `unsafe { ... }`. Unsafe(ExprUnsafe),
/// Tokens in expression position not interpreted by Syn.
Verbatim(TokenStream),
/// A while loop: `while expr { ... }`. While(ExprWhile),
/// A yield expression: `yield expr`. Yield(ExprYield),
// For testing exhaustiveness in downstream code, use the following idiom: // // match expr { // #![cfg_attr(test, deny(non_exhaustive_omitted_patterns))] // // Expr::Array(expr) => {...} // Expr::Assign(expr) => {...} // ... // Expr::Yield(expr) => {...} // // _ => { /* some sane fallback */ } // } // // This way we fail your tests but don't break your library when adding // a variant. You will be notified by a test failure when a variant is // added, so that you can add code to handle it, but your library will // continue to compile and work for downstream users in the interim.
}
}
ast_struct! { /// An expression contained within invisible delimiters. /// /// This variant is important for faithfully representing the precedence /// of expressions and is related to `None`-delimited spans in a /// `TokenStream`. #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubstruct ExprGroup { pub attrs: Vec<Attribute>, pub group_token: token::Group, pub expr: Box<Expr>,
}
}
ast_struct! { /// An `if` expression with an optional `else` block: `if expr { ... } /// else { ... }`. /// /// The `else` branch expression may only be an `If` or `Block` /// expression, not any of the other types of expression. #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubstruct 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 alternative to the primary `Expr::parse` parser (from the [`Parse`] /// trait) for ambiguous syntactic positions in which a trailing brace /// should not be taken as part of the expression. /// /// [`Parse`]: crate::parse::Parse /// /// Rust grammar has an ambiguity where braces sometimes turn a path /// expression into a struct initialization and sometimes do not. In the /// following code, the expression `S {}` is one expression. Presumably /// there is an empty struct `struct S {}` defined somewhere which it is /// instantiating. /// /// ``` /// # struct S; /// # impl std::ops::Deref for S { /// # type Target = bool; /// # fn deref(&self) -> &Self::Target { /// # &true /// # } /// # } /// let _ = *S {}; /// /// // parsed by rustc as: `*(S {})` /// ``` /// /// We would want to parse the above using `Expr::parse` after the `=` /// token. /// /// But in the following, `S {}` is *not* a struct init expression. /// /// ``` /// # const S: &bool = &true; /// if *S {} {} /// /// // parsed by rustc as: /// // /// // if (*S) { /// // /* empty block */ /// // } /// // { /// // /* another empty block */ /// // } /// ``` /// /// For that reason we would want to parse if-conditions using /// `Expr::parse_without_eager_brace` after the `if` token. Same for similar /// syntactic positions such as the condition expr after a `while` token or /// the expr at the top of a `match`. /// /// The Rust grammar's choices around which way this ambiguity is resolved /// at various syntactic positions is fairly arbitrary. Really either parse /// behavior could work in most positions, and language designers just /// decide each case based on which is more likely to be what the programmer /// had in mind most of the time. /// /// ``` /// # struct S; /// # fn doc() -> S { /// if return S {} {} /// # unreachable!() /// # } /// /// // parsed by rustc as: /// // /// // if (return (S {})) { /// // } /// // /// // but could equally well have been this other arbitrary choice: /// // /// // if (return S) { /// // } /// // {} /// ``` /// /// Note the grammar ambiguity on trailing braces is distinct from /// precedence and is not captured by assigning a precedence level to the /// braced struct init expr in relation to other operators. This can be /// illustrated by `return 0..S {}` vs `match 0..S {}`. The former parses as /// `return (0..(S {}))` implying tighter precedence for struct init than /// `..`, while the latter parses as `match (0..S) {}` implying tighter /// precedence for `..` than struct init, a contradiction. #[cfg(all(feature = "full", feature = "parsing"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "full", feature = "parsing"))))] pubfn parse_without_eager_brace(input: ParseStream) -> Result<Expr> {
parsing::ambiguous_expr(input, parsing::AllowStruct(false))
}
/// An alternative to the primary `Expr::parse` parser (from the [`Parse`] /// trait) for syntactic positions in which expression boundaries are placed /// more eagerly than done by the typical expression grammar. This includes /// expressions at the head of a statement or in the right-hand side of a /// `match` arm. /// /// [`Parse`]: crate::parse::Parse /// /// Compare the following cases: /// /// 1. /// ``` /// # let result = (); /// # let guard = false; /// # let cond = true; /// # let f = true; /// # let g = f; /// # /// let _ = match result { /// () if guard => if cond { f } else { g } /// () => false, /// }; /// ``` /// /// 2. /// ``` /// # let cond = true; /// # let f = (); /// # let g = f; /// # /// let _ = || { /// if cond { f } else { g } /// () /// }; /// ``` /// /// 3. /// ``` /// # let cond = true; /// # let f = || (); /// # let g = f; /// # /// let _ = [if cond { f } else { g } ()]; /// ``` /// /// The same sequence of tokens `if cond { f } else { g } ()` appears in /// expression position 3 times. The first two syntactic positions use eager /// placement of expression boundaries, and parse as `Expr::If`, with the /// adjacent `()` becoming `Pat::Tuple` or `Expr::Tuple`. In contrast, the /// third case uses standard expression boundaries and parses as /// `Expr::Call`. /// /// As with [`parse_without_eager_brace`], this ambiguity in the Rust /// grammar is independent of precedence. /// /// [`parse_without_eager_brace`]: Self::parse_without_eager_brace #[cfg(all(feature = "full", feature = "parsing"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "full", feature = "parsing"))))] pubfn parse_with_earlier_boundary_rule(input: ParseStream) -> Result<Expr> {
parsing::parse_with_earlier_boundary_rule(input)
}
/// Returns whether the next token in the parse stream is one that might /// possibly form the beginning of an expr. /// /// This classification is a load-bearing part of the grammar of some Rust /// expressions, notably `return` and `break`. For example `return < …` will /// never parse `<` as a binary operator regardless of what comes after, /// because `<` is a legal starting token for an expression and so it's /// required to be continued as a return value, such as `return <Struct as /// Trait>::CONST`. Meanwhile `return > …` treats the `>` as a binary /// operator because it cannot be a starting token for any Rust expression. #[cfg(feature = "parsing")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] pubfn peek(input: ParseStream) -> bool {
input.peek(Ident::peek_any) // value name or keyword
|| input.peek(token::Paren) // tuple
|| input.peek(token::Bracket) // array
|| input.peek(token::Brace) // block
|| input.peek(Lit) // literal
|| input.peek(Token![!]) && !input.peek(Token![!=]) // operator not
|| input.peek(Token![-]) && !input.peek(Token![-=]) && !input.peek(Token![->]) // unary minus
|| input.peek(Token![*]) && !input.peek(Token![*=]) // dereference
|| input.peek(Token![|]) && !input.peek(Token![|=]) // closure
|| input.peek(Token![&]) && !input.peek(Token![&=]) // reference
|| input.peek(Token![..]) // range
|| input.peek(Token![<]) && !input.peek(Token![<=]) && !input.peek(Token![<<=]) // associated path
|| input.peek(Token![::]) // absolute path
|| input.peek(Lifetime) // labeled loop
|| input.peek(Token![#]) // expression attributes
}
ast_enum! { /// A struct or tuple struct field accessed in a struct literal or field /// expression. #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] pubenum Member { /// A named field like `self.x`.
Named(Ident), /// An unnamed field like `self.0`.
Unnamed(Index),
}
}
impl From<Ident> for Member { fn from(ident: Ident) -> Member {
Member::Named(ident)
}
}
impl From<Index> for Member { fn from(index: Index) -> Member {
Member::Unnamed(index)
}
}
impl From<usize> for Member { fn from(index: usize) -> Member {
Member::Unnamed(Index::from(index))
}
}
impl Eq for Member {}
impl PartialEq for Member { fn eq(&self, other: &Self) -> bool { match (self, other) {
(Member::Named(this), Member::Named(other)) => this == other,
(Member::Unnamed(this), Member::Unnamed(other)) => this == other,
_ => false,
}
}
}
ast_struct! { /// The index of an unnamed tuple struct field. #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] pubstruct 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)
}
ast_struct! { /// A field-value pair in a struct literal. #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] pubstruct FieldValue { pub attrs: Vec<Attribute>, pub member: Member,
/// The colon in `Struct { x: x }`. If written in shorthand like /// `Struct { x }`, there is no colon. pub colon_token: Option<Token![:]>,
pub expr: Expr,
}
}
#[cfg(feature = "full")]
ast_struct! { /// A lifetime labeling a `for`, `while`, or `loop`. #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubstruct Label { pub name: Lifetime, pub colon_token: Token![:],
}
}
#[cfg(feature = "full")]
ast_struct! { /// One arm of a `match` expression: `0..=10 => { return true; }`. /// /// As in: /// /// ``` /// # fn f() -> bool { /// # let n = 0; /// match n { /// 0..=10 => { /// return true; /// } /// // ... /// # _ => {} /// } /// # false /// # } /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubstruct Arm { pub attrs: Vec<Attribute>, pub pat: Pat, pub guard: Option<(Token![if], Box<Expr>)>, pub fat_arrow_token: Token![=>], pub body: Box<Expr>, pub comma: Option<Token![,]>,
}
}
#[cfg(feature = "full")]
ast_enum! { /// Limit types of a range, inclusive or exclusive. #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubenum RangeLimits { /// Inclusive at the beginning, exclusive at the end.
HalfOpen(Token![..]), /// Inclusive at the beginning and end.
Closed(Token![..=]),
}
}
#[cfg(feature = "full")]
ast_enum! { /// Mutability of a raw pointer (`*const T`, `*mut T`), in which non-mutable /// isn't the implicit default. #[cfg_attr(docsrs, doc(cfg(feature = "full")))] pubenum PointerMutability { Const(Token![const]), Mut(Token![mut]),
}
}
// When we're parsing expressions which occur before blocks, like in an if // statement's condition, we cannot parse a struct literal. // // Struct literals are ambiguous in certain positions // https://github.com/rust-lang/rfcs/pull/92 #[cfg(feature = "full")] pub(super) struct AllowStruct(pub bool);
#[cfg(feature = "full")] fn parse_expr(
input: ParseStream, mut lhs: Expr,
allow_struct: AllowStruct,
base: Precedence,
) -> Result<Expr> { loop { let ahead = input.fork(); iflet Expr::Range(ExprRange { end: Some(_), .. }) = lhs { // A range with an upper bound cannot be the left-hand side of // another binary operator. break;
} elseiflet Ok(op) = ahead.parse::<BinOp>() { let precedence = Precedence::of_binop(&op); if precedence < base { break;
} if precedence == Precedence::Compare { iflet Expr::Binary(lhs) = &lhs { if Precedence::of_binop(&lhs.op) == Precedence::Compare { return Err(input.error("comparison operators cannot be chained"));
}
}
}
input.advance_to(&ahead); let right = parse_binop_rhs(input, allow_struct, precedence)?;
lhs = Expr::Binary(ExprBinary {
attrs: Vec::new(),
left: Box::new(lhs),
op,
right,
});
} elseif 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,
});
} elseif 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,
});
} elseif 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(); iflet Ok(op) = ahead.parse::<BinOp>() { let precedence = Precedence::of_binop(&op); if precedence < base { break;
} if precedence == Precedence::Compare { iflet Expr::Binary(lhs) = &lhs { if Precedence::of_binop(&lhs.op) == Precedence::Compare { return Err(input.error("comparison operators cannot be chained"));
}
}
}
input.advance_to(&ahead); let right = parse_binop_rhs(input, precedence)?;
lhs = Expr::Binary(ExprBinary {
attrs: Vec::new(),
left: Box::new(lhs),
op,
right,
});
} elseif 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>> { letmut rhs = unary_expr(
input, #[cfg(feature = "full")]
allow_struct,
)?; loop { let next = peek_precedence(input); if next > precedence || next == precedence && precedence == Precedence::Assign { let cursor = input.cursor();
rhs = parse_expr(
input,
rhs, #[cfg(feature = "full")]
allow_struct,
next,
)?; if cursor == input.cursor() { // Bespoke grammar restrictions separate from precedence can // cause parsing to not advance, such as `..a` being // disallowed in the left-hand side of binary operators, // even ones that have lower precedence than `..`. break;
}
} else { break;
}
}
Ok(Box::new(rhs))
}
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) { iflet 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;
}
}
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![,])?,
});
} elseif input.peek(Token![.])
&& !input.peek(Token![..])
&& !input.peek2(Token![await])
{ letmut dot_token: Token![.] = input.parse()?;
let float_token: Option<LitFloat> = input.parse()?; iflet 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) { iflet 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,
});
} elseif 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;
}
}
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),
}));
}
letmut 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![,]) { letmut 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,
}))
} elseif content.peek(Token![;]) { let semi_token: Token![;] = content.parse()?; let len: Expr = content.parse()?;
Ok(Expr::Repeat(ExprRepeat {
attrs: Vec::new(),
bracket_token,
expr: Box::new(first),
semi_token,
len: Box::new(len),
}))
} else {
Err(content.error("expected `,` or `;`"))
}
}
#[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for ExprArray { fn parse(input: ParseStream) -> Result<Self> { let content; let bracket_token = bracketed!(content in input); letmut 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);
}
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)?;
let content; let brace_token = braced!(content in input);
attr::parsing::parse_inner(&content, &mut attrs)?; let stmts = content.call(Block::parse_within)?;
#[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for ExprWhile { fn parse(input: ParseStream) -> Result<Self> { letmut 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)?;
let content; let brace_token = braced!(content in input); let inner_attrs = content.call(Attribute::parse_inner)?; let stmts = content.call(Block::parse_within)?;
let content; let brace_token = braced!(content in input); let inner_attrs = content.call(Attribute::parse_inner)?; let stmts = content.call(Block::parse_within)?;
let content; let brace_token = braced!(content in input);
attr::parsing::parse_inner(&content, &mut attrs)?; let stmts = content.call(Block::parse_within)?;
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(); letmut float_repr = float_token.to_string(); let trailing_dot = float_repr.ends_with('.'); if trailing_dot {
float_repr.truncate(float_repr.len() - 1);
}
letmut offset = 0; for part in float_repr.split('.') { letmut index: Index = crate::parse_str(part).map_err(|err| Error::new(float_span, err))?; let part_end = offset + part.len();
index.span = float_token.subspan(offset..part_end).unwrap_or(float_span);
let base = mem::replace(e, Expr::PLACEHOLDER);
*e = Expr::Field(ExprField {
attrs: Vec::new(),
base: Box::new(base),
dot_token: Token,
member: Member::Unnamed(index),
});
fn print_subexpression(
expr: &Expr,
needs_group: bool,
tokens: &mut TokenStream, mut fixup: FixupContext,
) { if needs_group { // If we are surrounding the whole cond in parentheses, such as: // // if (return Struct {}) {} // // then there is no need for parenthesizing the individual struct // expressions within. On the other hand if the whole cond is not // parenthesized, then print_expr must parenthesize exterior struct // literals. // // if x == (Struct {}) {} //
fixup = FixupContext::NONE;
}
let do_print_expr = |tokens: &mut TokenStream| print_expr(expr, tokens, fixup);
let (else_token, else_) = match &expr.else_branch {
Some(else_branch) => else_branch,
None => break,
};
else_token.to_tokens(tokens); match &**else_ {
Expr::If(next) => {
expr = next;
}
Expr::Block(last) => {
last.to_tokens(tokens); break;
} // If this is not one of the valid expressions to exist in // an else clause, wrap it in a block.
other => {
token::Brace::default().surround(tokens, |tokens| {
print_expr(other, tokens, FixupContext::new_stmt());
}); break;
}
}
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for ExprTuple { fn to_tokens(&self, tokens: &mut TokenStream) {
outer_attrs_to_tokens(&self.attrs, tokens); self.paren_token.surround(tokens, |tokens| { self.elems.to_tokens(tokens); // If we only have one argument, we need a trailing comma to // distinguish ExprTuple from ExprParen. ifself.elems.len() == 1 && !self.elems.trailing_punct() {
<Token![,]>::default().to_tokens(tokens);
}
});
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.