diff options
| author | bors <bors@rust-lang.org> | 2019-06-23 12:28:12 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-06-23 12:28:12 +0000 |
| commit | 5d677b2efdb00031564d30ee9f63f4d4f936a39f (patch) | |
| tree | cf7f1b1a463a8a5ebbd802bd9cb61fe10d3fea0a /src/libsyntax | |
| parent | a96ba969156d257e5d5b692946fa8fe40ed6543a (diff) | |
| parent | c75f7ecaee508c568c0bc01c102965ce8b2246ef (diff) | |
Auto merge of #60861 - Centril:let-chains-ast-intro, r=petrochenkov
[let_chains, 2/6] Introduce `Let(..)` in AST, remove IfLet + WhileLet and parse let chains
Here we remove `ast::ExprKind::{IfLet, WhileLet}` and introduce `ast::ExprKind::Let`.
Moreover, we also:
+ connect the parsing logic for let chains
+ introduce the feature gate
+ rewire HIR lowering a bit.
However, this does not connect the new syntax to semantics in HIR.
That will be the subject of a subsequent PR.
Per https://github.com/rust-lang/rust/issues/53667#issuecomment-471583239.
Next step after https://github.com/rust-lang/rust/pull/59288.
cc @Manishearth re. Clippy.
r? @oli-obk
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 23 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/macro_parser.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax/mut_visit.rs | 16 | ||||
| -rw-r--r-- | src/libsyntax/parse/classify.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 91 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 92 | ||||
| -rw-r--r-- | src/libsyntax/util/parser.rs | 23 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 16 |
12 files changed, 138 insertions, 153 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index b1a62ac81d0..21704206cbf 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1032,10 +1032,9 @@ impl Expr { ExprKind::Unary(..) => ExprPrecedence::Unary, ExprKind::Lit(_) => ExprPrecedence::Lit, ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, + ExprKind::Let(..) => ExprPrecedence::Let, ExprKind::If(..) => ExprPrecedence::If, - ExprKind::IfLet(..) => ExprPrecedence::IfLet, ExprKind::While(..) => ExprPrecedence::While, - ExprKind::WhileLet(..) => ExprPrecedence::WhileLet, ExprKind::ForLoop(..) => ExprPrecedence::ForLoop, ExprKind::Loop(..) => ExprPrecedence::Loop, ExprKind::Match(..) => ExprPrecedence::Match, @@ -1116,26 +1115,20 @@ pub enum ExprKind { Cast(P<Expr>, P<Ty>), /// A type ascription (e.g., `42: usize`). Type(P<Expr>, P<Ty>), + /// A `let pats = expr` expression that is only semantically allowed in the condition + /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`). + /// + /// The `Vec<P<Pat>>` is for or-patterns at the top level. + /// FIXME(54883): Change this to just `P<Pat>`. + Let(Vec<P<Pat>>, P<Expr>), /// An `if` block, with an optional `else` block. /// /// `if expr { block } else { expr }` If(P<Expr>, P<Block>, Option<P<Expr>>), - /// An `if let` expression with an optional else block - /// - /// `if let pat = expr { block } else { expr }` - /// - /// This is desugared to a `match` expression. - IfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>), - /// A while loop, with an optional label + /// A while loop, with an optional label. /// /// `'label: while expr { block }` While(P<Expr>, P<Block>, Option<Label>), - /// A `while let` loop, with an optional label. - /// - /// `'label: while let pat = expr { block }` - /// - /// This is desugared to a combination of `loop` and `match` expressions. - WhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Label>), /// A `for` loop, with an optional label. /// /// `'label: for pat in expr { block }` diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index d5da4c920bc..92ce3779a3c 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -825,7 +825,9 @@ fn may_begin_with(token: &Token, name: Name) -> bool { } match name { - sym::expr => token.can_begin_expr(), + sym::expr => token.can_begin_expr() + // This exception is here for backwards compatibility. + && !token.is_keyword(kw::Let), sym::ty => token.can_begin_type(), sym::ident => get_macro_name(token).is_some(), sym::literal => token.can_begin_literal_or_bool(), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7f80e2099f6..8ec07de5fab 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -560,6 +560,9 @@ declare_features! ( // Allows calling constructor functions in `const fn`. (active, const_constructor, "1.37.0", Some(61456), None), + // Allows `if/while p && let q = r && ...` chains. + (active, let_chains, "1.37.0", Some(53667), None), + // #[repr(transparent)] on enums. (active, transparent_enums, "1.37.0", Some(60405), None), @@ -577,7 +580,8 @@ declare_features! ( const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::impl_trait_in_bindings, sym::generic_associated_types, - sym::const_generics + sym::const_generics, + sym::let_chains, ]; declare_features! ( @@ -2517,6 +2521,17 @@ pub fn check_crate(krate: &ast::Crate, "attributes on function parameters are unstable" )); + sess + .let_chains_spans + .borrow() + .iter() + .for_each(|span| gate_feature!( + &ctx, + let_chains, + *span, + "`let` expressions in this position are experimental" + )); + let visitor = &mut PostExpansionVisitor { context: &ctx, builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP, diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 5a5b633e315..35aa2eead0b 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -1110,28 +1110,20 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr, vis.visit_ty(ty); } ExprKind::AddrOf(_m, ohs) => vis.visit_expr(ohs), + ExprKind::Let(pats, scrutinee) => { + visit_vec(pats, |pat| vis.visit_pat(pat)); + vis.visit_expr(scrutinee); + } ExprKind::If(cond, tr, fl) => { vis.visit_expr(cond); vis.visit_block(tr); visit_opt(fl, |fl| vis.visit_expr(fl)); } - ExprKind::IfLet(pats, expr, tr, fl) => { - visit_vec(pats, |pat| vis.visit_pat(pat)); - vis.visit_expr(expr); - vis.visit_block(tr); - visit_opt(fl, |fl| vis.visit_expr(fl)); - } ExprKind::While(cond, body, label) => { vis.visit_expr(cond); vis.visit_block(body); visit_opt(label, |label| vis.visit_label(label)); } - ExprKind::WhileLet(pats, expr, body, label) => { - visit_vec(pats, |pat| vis.visit_pat(pat)); - vis.visit_expr(expr); - vis.visit_block(body); - visit_opt(label, |label| vis.visit_label(label)); - } ExprKind::ForLoop(pat, iter, body, label) => { vis.visit_pat(pat); vis.visit_expr(iter); diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index dfd6f451c28..6ebfab3a133 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -14,11 +14,9 @@ use crate::ast; pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { match e.node { ast::ExprKind::If(..) | - ast::ExprKind::IfLet(..) | ast::ExprKind::Match(..) | ast::ExprKind::Block(..) | ast::ExprKind::While(..) | - ast::ExprKind::WhileLet(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) | ast::ExprKind::TryBlock(..) => false, diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 1c44155150c..ead5d543bec 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1491,6 +1491,7 @@ mod tests { edition: Edition::from_session(), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), param_attr_spans: Lock::new(Vec::new()), + let_chains_spans: Lock::new(Vec::new()), } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index ba5d1d009d1..e19eab371f4 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -54,7 +54,9 @@ pub struct ParseSess { /// operation token that followed it, but that the parser cannot identify without further /// analysis. pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>, - pub param_attr_spans: Lock<Vec<Span>> + pub param_attr_spans: Lock<Vec<Span>>, + // Places where `let` exprs were used and should be feature gated according to `let_chains`. + pub let_chains_spans: Lock<Vec<Span>>, } impl ParseSess { @@ -81,6 +83,7 @@ impl ParseSess { edition: Edition::from_session(), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), param_attr_spans: Lock::new(Vec::new()), + let_chains_spans: Lock::new(Vec::new()), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index fa697e06d26..b2003e2d6bd 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -41,7 +41,7 @@ use crate::parse::lexer::UnmatchedBrace; use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use crate::parse::token::{Token, TokenKind, DelimToken}; use crate::parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership}; -use crate::util::parser::{AssocOp, Fixity}; +use crate::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par}; use crate::print::pprust; use crate::ptr::P; use crate::parse::PResult; @@ -2215,13 +2215,8 @@ impl<'a> Parser<'a> { } else { ex = ExprKind::Yield(None); } - } else if self.token.is_keyword(kw::Let) { - // Catch this syntax error here, instead of in `parse_ident`, so - // that we can explicitly mention that let is not to be used as an expression - let mut db = self.fatal("expected expression, found statement (`let`)"); - db.span_label(self.token.span, "expected expression"); - db.note("variable declaration using `let` is a statement"); - return Err(db); + } else if self.eat_keyword(kw::Let) { + return self.parse_let_expr(attrs); } else if is_span_rust_2018 && self.eat_keyword(kw::Await) { let (await_hi, e_kind) = self.parse_await_macro_or_alt(lo, self.prev_span)?; hi = await_hi; @@ -2483,15 +2478,13 @@ impl<'a> Parser<'a> { attrs.extend::<Vec<_>>(expr.attrs.into()); expr.attrs = attrs; match expr.node { - ExprKind::If(..) | ExprKind::IfLet(..) => { - if !expr.attrs.is_empty() { - // Just point to the first attribute in there... - let span = expr.attrs[0].span; - - self.span_err(span, - "attributes are not yet allowed on `if` \ - expressions"); - } + ExprKind::If(..) if !expr.attrs.is_empty() => { + // Just point to the first attribute in there... + let span = expr.attrs[0].span; + + self.span_err(span, + "attributes are not yet allowed on `if` \ + expressions"); } _ => {} } @@ -3161,13 +3154,10 @@ impl<'a> Parser<'a> { } } - /// Parses an `if` or `if let` expression (`if` token already eaten). + /// Parses an `if` expression (`if` token already eaten). fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { - if self.check_keyword(kw::Let) { - return self.parse_if_let_expr(attrs); - } let lo = self.prev_span; - let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let cond = self.parse_cond_expr()?; // Verify that the parsed `if` condition makes sense as a condition. If it is a block, then // verify that the last statement is either an implicit return (no `;`) or an explicit @@ -3197,22 +3187,32 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(hi), ExprKind::If(cond, thn, els), attrs)) } - /// Parses an `if let` expression (`if` token already eaten). - fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>) - -> PResult<'a, P<Expr>> { + /// Parse the condition of a `if`- or `while`-expression + fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> { + let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + if let ExprKind::Let(..) = cond.node { + // Remove the last feature gating of a `let` expression since it's stable. + let last = self.sess.let_chains_spans.borrow_mut().pop(); + debug_assert_eq!(cond.span, last.unwrap()); + } + + Ok(cond) + } + + /// Parses a `let $pats = $expr` pseudo-expression. + /// The `let` token has already been eaten. + fn parse_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { let lo = self.prev_span; - self.expect_keyword(kw::Let)?; let pats = self.parse_pats()?; self.expect(&token::Eq)?; - let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; - let thn = self.parse_block()?; - let (hi, els) = if self.eat_keyword(kw::Else) { - let expr = self.parse_else_expr()?; - (expr.span, Some(expr)) - } else { - (thn.span, None) - }; - Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pats, expr, thn, els), attrs)) + let expr = self.with_res( + Restrictions::NO_STRUCT_LITERAL, + |this| this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) + )?; + let span = lo.to(expr.span); + self.sess.let_chains_spans.borrow_mut().push(span); + Ok(self.mk_expr(span, ExprKind::Let(pats, expr), attrs)) } /// Parses `move |args| expr`. @@ -3299,28 +3299,11 @@ impl<'a> Parser<'a> { fn parse_while_expr(&mut self, opt_label: Option<Label>, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { - if self.token.is_keyword(kw::Let) { - return self.parse_while_let_expr(opt_label, span_lo, attrs); - } - let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; - let (iattrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(iattrs); - let span = span_lo.to(body.span); - return Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs)); - } - - /// Parses a `while let` expression (`while` token already eaten). - fn parse_while_let_expr(&mut self, opt_label: Option<Label>, - span_lo: Span, - mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { - self.expect_keyword(kw::Let)?; - let pats = self.parse_pats()?; - self.expect(&token::Eq)?; - let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let cond = self.parse_cond_expr()?; let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); let span = span_lo.to(body.span); - return Ok(self.mk_expr(span, ExprKind::WhileLet(pats, expr, body, opt_label), attrs)); + Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs)) } // parse `loop {...}`, `loop` token already eaten diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index cc34883e2e8..ebd0decacb5 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -135,6 +135,7 @@ pub(crate) fn ident_can_begin_expr(name: ast::Name, span: Span, is_raw: bool) -> kw::False, kw::For, kw::If, + kw::Let, kw::Loop, kw::Match, kw::Move, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 0aac4f83658..164fe2f36e1 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1715,6 +1715,21 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Block(blk)) } + /// Print a `let pats = scrutinee` expression. + pub fn print_let(&mut self, pats: &[P<ast::Pat>], scrutinee: &ast::Expr) -> io::Result<()> { + self.s.word("let ")?; + + self.print_pats(pats)?; + self.s.space()?; + + self.word_space("=")?; + self.print_expr_cond_paren( + scrutinee, + Self::cond_needs_par(scrutinee) + || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()) + ) + } + fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> { match els { Some(_else) => { @@ -1729,19 +1744,6 @@ impl<'a> State<'a> { self.print_block(then)?; self.print_else(e.as_ref().map(|e| &**e)) } - // Another `else if let` block. - ast::ExprKind::IfLet(ref pats, ref expr, ref then, ref e) => { - self.cbox(INDENT_UNIT - 1)?; - self.ibox(0)?; - self.s.word(" else if let ")?; - self.print_pats(pats)?; - self.s.space()?; - self.word_space("=")?; - self.print_expr_as_cond(expr)?; - self.s.space()?; - self.print_block(then)?; - self.print_else(e.as_ref().map(|e| &**e)) - } // Final `else` block. ast::ExprKind::Block(ref b, _) => { self.cbox(INDENT_UNIT - 1)?; @@ -1762,20 +1764,10 @@ impl<'a> State<'a> { pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) -> io::Result<()> { self.head("if")?; + self.print_expr_as_cond(test)?; self.s.space()?; - self.print_block(blk)?; - self.print_else(elseopt) - } - pub fn print_if_let(&mut self, pats: &[P<ast::Pat>], expr: &ast::Expr, blk: &ast::Block, - elseopt: Option<&ast::Expr>) -> io::Result<()> { - self.head("if let")?; - self.print_pats(pats)?; - self.s.space()?; - self.word_space("=")?; - self.print_expr_as_cond(expr)?; - self.s.space()?; self.print_block(blk)?; self.print_else(elseopt) } @@ -1807,21 +1799,18 @@ impl<'a> State<'a> { } pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) -> io::Result<()> { - let needs_par = expr.precedence().order() < prec; - if needs_par { - self.popen()?; - } - self.print_expr(expr)?; - if needs_par { - self.pclose()?; - } - Ok(()) + self.print_expr_cond_paren(expr, expr.precedence().order() < prec) } /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. pub fn print_expr_as_cond(&mut self, expr: &ast::Expr) -> io::Result<()> { - let needs_par = match expr.node { + self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) + } + + /// Does `expr` need parenthesis when printed in a condition position? + fn cond_needs_par(expr: &ast::Expr) -> bool { + match expr.node { // These cases need parens due to the parse error observed in #26461: `if return {}` // parses as the erroneous construct `if (return {})`, not `if (return) {}`. ast::ExprKind::Closure(..) | @@ -1829,8 +1818,11 @@ impl<'a> State<'a> { ast::ExprKind::Break(..) => true, _ => parser::contains_exterior_struct_lit(expr), - }; + } + } + /// Print `expr` or `(expr)` when `needs_par` holds. + fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) -> io::Result<()> { if needs_par { self.popen()?; } @@ -1962,6 +1954,17 @@ impl<'a> State<'a> { // of `(x as i32) < ...`. We need to convince it _not_ to do that. (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt) | (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN, + // We are given `(let _ = a) OP b`. + // + // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens + // as the parser will interpret this as `(let _ = a) OP b`. + // + // - Otherwise, e.g. when we have `(let a = b) < c` in AST, + // parens are required since the parser would interpret `let a = b < c` as + // `let a = (b < c)`. To achieve this, we force parens. + (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { + parser::PREC_FORCE_PAREN + } _ => left_prec, }; @@ -2052,12 +2055,12 @@ impl<'a> State<'a> { self.word_space(":")?; self.print_type(ty)?; } + ast::ExprKind::Let(ref pats, ref scrutinee) => { + self.print_let(pats, scrutinee)?; + } ast::ExprKind::If(ref test, ref blk, ref elseopt) => { self.print_if(test, blk, elseopt.as_ref().map(|e| &**e))?; } - ast::ExprKind::IfLet(ref pats, ref expr, ref blk, ref elseopt) => { - self.print_if_let(pats, expr, blk, elseopt.as_ref().map(|e| &**e))?; - } ast::ExprKind::While(ref test, ref blk, opt_label) => { if let Some(label) = opt_label { self.print_ident(label.ident)?; @@ -2068,19 +2071,6 @@ impl<'a> State<'a> { self.s.space()?; self.print_block_with_attrs(blk, attrs)?; } - ast::ExprKind::WhileLet(ref pats, ref expr, ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident)?; - self.word_space(":")?; - } - self.head("while let")?; - self.print_pats(pats)?; - self.s.space()?; - self.word_space("=")?; - self.print_expr_as_cond(expr)?; - self.s.space()?; - self.print_block_with_attrs(blk, attrs)?; - } ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => { if let Some(label) = opt_label { self.print_ident(label.ident)?; diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index fcecee8c57f..1e52186a106 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -260,6 +260,7 @@ pub enum ExprPrecedence { Box, AddrOf, + Let, Unary, Call, @@ -277,9 +278,7 @@ pub enum ExprPrecedence { Path, Paren, If, - IfLet, While, - WhileLet, ForLoop, Loop, Match, @@ -318,6 +317,11 @@ impl ExprPrecedence { // Unary, prefix ExprPrecedence::Box | ExprPrecedence::AddrOf | + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + ExprPrecedence::Let | ExprPrecedence::Unary => PREC_PREFIX, // Unary, postfix @@ -338,9 +342,7 @@ impl ExprPrecedence { ExprPrecedence::Path | ExprPrecedence::Paren | ExprPrecedence::If | - ExprPrecedence::IfLet | ExprPrecedence::While | - ExprPrecedence::WhileLet | ExprPrecedence::ForLoop | ExprPrecedence::Loop | ExprPrecedence::Match | @@ -353,6 +355,19 @@ impl ExprPrecedence { } } +/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`. +crate fn prec_let_scrutinee_needs_par() -> usize { + AssocOp::LAnd.precedence() +} + +/// Suppose we have `let _ = e` and the `order` of `e`. +/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS? +/// +/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. +/// Can we print this as `let _ = a OP b`? +crate fn needs_par_as_let_scrutinee(order: i8) -> bool { + order <= prec_let_scrutinee_needs_par() as i8 +} /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 8132024416a..4d961142ff1 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -719,6 +719,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); visitor.visit_ty(typ) } + ExprKind::Let(ref pats, ref scrutinee) => { + walk_list!(visitor, visit_pat, pats); + visitor.visit_expr(scrutinee); + } ExprKind::If(ref head_expression, ref if_block, ref optional_else) => { visitor.visit_expr(head_expression); visitor.visit_block(if_block); @@ -729,18 +733,6 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); visitor.visit_block(block); } - ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => { - walk_list!(visitor, visit_pat, pats); - visitor.visit_expr(subexpression); - visitor.visit_block(if_block); - walk_list!(visitor, visit_expr, optional_else); - } - ExprKind::WhileLet(ref pats, ref subexpression, ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - walk_list!(visitor, visit_pat, pats); - visitor.visit_expr(subexpression); - visitor.visit_block(block); - } ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_label) => { walk_list!(visitor, visit_label, opt_label); visitor.visit_pat(pattern); |
