Skip to content

Commit 851066f

Browse files
committed
let_chains: Fix bugs in pretty printing.
1 parent 7465eb4 commit 851066f

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

src/libsyntax/parse/parser.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::parse::lexer::UnmatchedBrace;
4141
use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
4242
use crate::parse::token::{Token, TokenKind, DelimToken};
4343
use crate::parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership};
44-
use crate::util::parser::{AssocOp, Fixity};
44+
use crate::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par};
4545
use crate::print::pprust;
4646
use crate::ptr::P;
4747
use crate::parse::PResult;
@@ -3208,7 +3208,7 @@ impl<'a> Parser<'a> {
32083208
self.expect(&token::Eq)?;
32093209
let expr = self.with_res(
32103210
Restrictions::NO_STRUCT_LITERAL,
3211-
|this| this.parse_assoc_expr_with(1 + AssocOp::LAnd.precedence(), None.into())
3211+
|this| this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
32123212
)?;
32133213
let span = lo.to(expr.span);
32143214
self.sess.let_chains_spans.borrow_mut().push(span);

src/libsyntax/print/pprust.rs

+28-12
Original file line numberDiff line numberDiff line change
@@ -1715,14 +1715,19 @@ impl<'a> State<'a> {
17151715
self.ann.post(self, AnnNode::Block(blk))
17161716
}
17171717

1718+
/// Print a `let pats = scrutinee` expression.
17181719
pub fn print_let(&mut self, pats: &[P<ast::Pat>], scrutinee: &ast::Expr) -> io::Result<()> {
17191720
self.s.word("let ")?;
17201721

17211722
self.print_pats(pats)?;
17221723
self.s.space()?;
17231724

17241725
self.word_space("=")?;
1725-
self.print_expr_as_cond(scrutinee)
1726+
self.print_expr_cond_paren(
1727+
scrutinee,
1728+
Self::cond_needs_par(scrutinee)
1729+
|| parser::needs_par_as_let_scrutinee(scrutinee.precedence().order())
1730+
)
17261731
}
17271732

17281733
fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
@@ -1794,30 +1799,30 @@ impl<'a> State<'a> {
17941799
}
17951800

17961801
pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) -> io::Result<()> {
1797-
let needs_par = expr.precedence().order() < prec;
1798-
if needs_par {
1799-
self.popen()?;
1800-
}
1801-
self.print_expr(expr)?;
1802-
if needs_par {
1803-
self.pclose()?;
1804-
}
1805-
Ok(())
1802+
self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
18061803
}
18071804

18081805
/// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in
18091806
/// `if cond { ... }`.
18101807
pub fn print_expr_as_cond(&mut self, expr: &ast::Expr) -> io::Result<()> {
1811-
let needs_par = match expr.node {
1808+
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
1809+
}
1810+
1811+
/// Does `expr` need parenthesis when printed in a condition position?
1812+
fn cond_needs_par(expr: &ast::Expr) -> bool {
1813+
match expr.node {
18121814
// These cases need parens due to the parse error observed in #26461: `if return {}`
18131815
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
18141816
ast::ExprKind::Closure(..) |
18151817
ast::ExprKind::Ret(..) |
18161818
ast::ExprKind::Break(..) => true,
18171819

18181820
_ => parser::contains_exterior_struct_lit(expr),
1819-
};
1821+
}
1822+
}
18201823

1824+
/// Print `expr` or `(expr)` when `needs_par` holds.
1825+
fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) -> io::Result<()> {
18211826
if needs_par {
18221827
self.popen()?;
18231828
}
@@ -1949,6 +1954,17 @@ impl<'a> State<'a> {
19491954
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
19501955
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt) |
19511956
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN,
1957+
// We are given `(let _ = a) OP b`.
1958+
//
1959+
// - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
1960+
// as the parser will interpret this as `(let _ = a) OP b`.
1961+
//
1962+
// - Otherwise, e.g. when we have `(let a = b) < c` in AST,
1963+
// parens are required since the parser would interpret `let a = b < c` as
1964+
// `let a = (b < c)`. To achieve this, we force parens.
1965+
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
1966+
parser::PREC_FORCE_PAREN
1967+
}
19521968
_ => left_prec,
19531969
};
19541970

src/libsyntax/util/parser.rs

+16
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ impl ExprPrecedence {
318318
ExprPrecedence::Box |
319319
ExprPrecedence::AddrOf |
320320
// Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
321+
// However, this is not exactly right. When `let _ = a` is the LHS of a binop we
322+
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
323+
// but we need to print `(let _ = a) < b` as-is with parens.
321324
ExprPrecedence::Let |
322325
ExprPrecedence::Unary => PREC_PREFIX,
323326

@@ -352,6 +355,19 @@ impl ExprPrecedence {
352355
}
353356
}
354357

358+
/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`.
359+
crate fn prec_let_scrutinee_needs_par() -> usize {
360+
AssocOp::LAnd.precedence()
361+
}
362+
363+
/// Suppose we have `let _ = e` and the `order` of `e`.
364+
/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS?
365+
///
366+
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
367+
/// Can we print this as `let _ = a OP b`?
368+
crate fn needs_par_as_let_scrutinee(order: i8) -> bool {
369+
order <= prec_let_scrutinee_needs_par() as i8
370+
}
355371

356372
/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
357373
/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and

0 commit comments

Comments
 (0)