Skip to content

Commit 6cca4ca

Browse files
committed
Implement asymmetrical precedence for closures and jumps
1 parent 1bea580 commit 6cca4ca

File tree

7 files changed

+150
-75
lines changed

7 files changed

+150
-75
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,11 +1447,15 @@ impl Expr {
14471447
}
14481448
}
14491449

1450-
ExprKind::Break(..)
1451-
| ExprKind::Ret(..)
1452-
| ExprKind::Yield(..)
1453-
| ExprKind::Yeet(..)
1454-
| ExprKind::Become(..) => ExprPrecedence::Jump,
1450+
ExprKind::Break(_ /*label*/, value)
1451+
| ExprKind::Ret(value)
1452+
| ExprKind::Yield(YieldKind::Prefix(value))
1453+
| ExprKind::Yeet(value) => match value {
1454+
Some(_) => ExprPrecedence::Jump,
1455+
None => ExprPrecedence::Unambiguous,
1456+
},
1457+
1458+
ExprKind::Become(_) => ExprPrecedence::Jump,
14551459

14561460
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
14571461
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
@@ -1508,6 +1512,7 @@ impl Expr {
15081512
| ExprKind::Underscore
15091513
| ExprKind::UnsafeBinderCast(..)
15101514
| ExprKind::While(..)
1515+
| ExprKind::Yield(YieldKind::Postfix(..))
15111516
| ExprKind::Err(_)
15121517
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
15131518
}

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use rustc_ast::util::classify;
77
use rustc_ast::util::literal::escape_byte_str_symbol;
88
use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
99
use rustc_ast::{
10-
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
11-
FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
10+
self as ast, BinOpKind, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece,
11+
FormatCount, FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
1212
};
1313

1414
use crate::pp::Breaks::Inconsistent;
@@ -214,13 +214,6 @@ impl<'a> State<'a> {
214214
}
215215

216216
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
217-
let needs_paren = match func.kind {
218-
// In order to call a named field, needs parens: `(self.fun)()`
219-
// But not for an unnamed field: `self.0()`
220-
ast::ExprKind::Field(_, name) => !name.is_numeric(),
221-
_ => func.precedence() < ExprPrecedence::Unambiguous,
222-
};
223-
224217
// Independent of parenthesization related to precedence, we must
225218
// parenthesize `func` if this is a statement context in which without
226219
// parentheses, a statement boundary would occur inside `func` or
@@ -237,8 +230,16 @@ impl<'a> State<'a> {
237230
// because the latter is valid syntax but with the incorrect meaning.
238231
// It's a match-expression followed by tuple-expression, not a function
239232
// call.
240-
self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression());
233+
let func_fixup = fixup.leftmost_subexpression_with_operator(true);
234+
235+
let needs_paren = match func.kind {
236+
// In order to call a named field, needs parens: `(self.fun)()`
237+
// But not for an unnamed field: `self.0()`
238+
ast::ExprKind::Field(_, name) => !name.is_numeric(),
239+
_ => func_fixup.precedence(func) < ExprPrecedence::Unambiguous,
240+
};
241241

242+
self.print_expr_cond_paren(func, needs_paren, func_fixup);
242243
self.print_call_post(args)
243244
}
244245

@@ -281,9 +282,24 @@ impl<'a> State<'a> {
281282
rhs: &ast::Expr,
282283
fixup: FixupContext,
283284
) {
285+
let operator_can_begin_expr = match op {
286+
| BinOpKind::Sub // -x
287+
| BinOpKind::Mul // *x
288+
| BinOpKind::And // &&x
289+
| BinOpKind::Or // || x
290+
| BinOpKind::BitAnd // &x
291+
| BinOpKind::BitOr // |x| x
292+
| BinOpKind::Shl // <<T as Trait>::Type as Trait>::CONST
293+
| BinOpKind::Lt // <T as Trait>::CONST
294+
=> true,
295+
_ => false,
296+
};
297+
298+
let left_fixup = fixup.leftmost_subexpression_with_operator(operator_can_begin_expr);
299+
284300
let binop_prec = op.precedence();
285-
let left_prec = lhs.precedence();
286-
let right_prec = rhs.precedence();
301+
let left_prec = left_fixup.precedence(lhs);
302+
let right_prec = fixup.precedence(rhs);
287303

288304
let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
289305
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
@@ -312,18 +328,18 @@ impl<'a> State<'a> {
312328
_ => {}
313329
}
314330

315-
self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
331+
self.print_expr_cond_paren(lhs, left_needs_paren, left_fixup);
316332
self.space();
317333
self.word_space(op.as_str());
318-
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
334+
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.rightmost_subexpression());
319335
}
320336

321337
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
322338
self.word(op.as_str());
323339
self.print_expr_cond_paren(
324340
expr,
325-
expr.precedence() < ExprPrecedence::Prefix,
326-
fixup.subsequent_subexpression(),
341+
fixup.precedence(expr) < ExprPrecedence::Prefix,
342+
fixup.rightmost_subexpression(),
327343
);
328344
}
329345

@@ -344,8 +360,8 @@ impl<'a> State<'a> {
344360
}
345361
self.print_expr_cond_paren(
346362
expr,
347-
expr.precedence() < ExprPrecedence::Prefix,
348-
fixup.subsequent_subexpression(),
363+
fixup.precedence(expr) < ExprPrecedence::Prefix,
364+
fixup.rightmost_subexpression(),
349365
);
350366
}
351367

@@ -590,8 +606,8 @@ impl<'a> State<'a> {
590606
self.word_space("=");
591607
self.print_expr_cond_paren(
592608
rhs,
593-
rhs.precedence() < ExprPrecedence::Assign,
594-
fixup.subsequent_subexpression(),
609+
fixup.precedence(rhs) < ExprPrecedence::Assign,
610+
fixup.rightmost_subexpression(),
595611
);
596612
}
597613
ast::ExprKind::AssignOp(op, lhs, rhs) => {
@@ -604,8 +620,8 @@ impl<'a> State<'a> {
604620
self.word_space(op.node.as_str());
605621
self.print_expr_cond_paren(
606622
rhs,
607-
rhs.precedence() < ExprPrecedence::Assign,
608-
fixup.subsequent_subexpression(),
623+
fixup.precedence(rhs) < ExprPrecedence::Assign,
624+
fixup.rightmost_subexpression(),
609625
);
610626
}
611627
ast::ExprKind::Field(expr, ident) => {
@@ -618,10 +634,11 @@ impl<'a> State<'a> {
618634
self.print_ident(*ident);
619635
}
620636
ast::ExprKind::Index(expr, index, _) => {
637+
let expr_fixup = fixup.leftmost_subexpression_with_operator(true);
621638
self.print_expr_cond_paren(
622639
expr,
623-
expr.precedence() < ExprPrecedence::Unambiguous,
624-
fixup.leftmost_subexpression(),
640+
expr_fixup.precedence(expr) < ExprPrecedence::Unambiguous,
641+
expr_fixup,
625642
);
626643
self.word("[");
627644
self.print_expr(index, FixupContext::default());
@@ -634,10 +651,11 @@ impl<'a> State<'a> {
634651
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
635652
let fake_prec = ExprPrecedence::LOr;
636653
if let Some(e) = start {
654+
let start_fixup = fixup.leftmost_subexpression_with_operator(true);
637655
self.print_expr_cond_paren(
638656
e,
639-
e.precedence() < fake_prec,
640-
fixup.leftmost_subexpression(),
657+
start_fixup.precedence(e) < fake_prec,
658+
start_fixup,
641659
);
642660
}
643661
match limits {
@@ -647,8 +665,8 @@ impl<'a> State<'a> {
647665
if let Some(e) = end {
648666
self.print_expr_cond_paren(
649667
e,
650-
e.precedence() < fake_prec,
651-
fixup.subsequent_subexpression(),
668+
fixup.precedence(e) < fake_prec,
669+
fixup.rightmost_subexpression(),
652670
);
653671
}
654672
}
@@ -665,11 +683,10 @@ impl<'a> State<'a> {
665683
self.space();
666684
self.print_expr_cond_paren(
667685
expr,
668-
// Parenthesize if required by precedence, or in the
669-
// case of `break 'inner: loop { break 'inner 1 } + 1`
670-
expr.precedence() < ExprPrecedence::Jump
671-
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
672-
fixup.subsequent_subexpression(),
686+
// Parenthesize `break 'inner: loop { break 'inner 1 } + 1`
687+
// ^---------------------------------^
688+
opt_label.is_none() && classify::leading_labeled_expr(expr),
689+
fixup.rightmost_subexpression(),
673690
);
674691
}
675692
}
@@ -684,11 +701,7 @@ impl<'a> State<'a> {
684701
self.word("return");
685702
if let Some(expr) = result {
686703
self.word(" ");
687-
self.print_expr_cond_paren(
688-
expr,
689-
expr.precedence() < ExprPrecedence::Jump,
690-
fixup.subsequent_subexpression(),
691-
);
704+
self.print_expr(expr, fixup.rightmost_subexpression());
692705
}
693706
}
694707
ast::ExprKind::Yeet(result) => {
@@ -697,21 +710,13 @@ impl<'a> State<'a> {
697710
self.word("yeet");
698711
if let Some(expr) = result {
699712
self.word(" ");
700-
self.print_expr_cond_paren(
701-
expr,
702-
expr.precedence() < ExprPrecedence::Jump,
703-
fixup.subsequent_subexpression(),
704-
);
713+
self.print_expr(expr, fixup.rightmost_subexpression());
705714
}
706715
}
707716
ast::ExprKind::Become(result) => {
708717
self.word("become");
709718
self.word(" ");
710-
self.print_expr_cond_paren(
711-
result,
712-
result.precedence() < ExprPrecedence::Jump,
713-
fixup.subsequent_subexpression(),
714-
);
719+
self.print_expr(result, fixup.rightmost_subexpression());
715720
}
716721
ast::ExprKind::InlineAsm(a) => {
717722
// FIXME: Print `builtin # asm` once macro `asm` uses `builtin_syntax`.
@@ -761,11 +766,7 @@ impl<'a> State<'a> {
761766

762767
if let Some(expr) = e {
763768
self.space();
764-
self.print_expr_cond_paren(
765-
expr,
766-
expr.precedence() < ExprPrecedence::Jump,
767-
fixup.subsequent_subexpression(),
768-
);
769+
self.print_expr(expr, fixup.rightmost_subexpression());
769770
}
770771
}
771772
ast::ExprKind::Yield(YieldKind::Postfix(e)) => {

compiler/rustc_ast_pretty/src/pprust/state/fixup.rs

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use rustc_ast::Expr;
2-
use rustc_ast::util::{classify, parser};
1+
use rustc_ast::util::classify;
2+
use rustc_ast::util::parser::{self, ExprPrecedence};
3+
use rustc_ast::{Expr, ExprKind, YieldKind};
34

45
// The default amount of fixing is minimal fixing, so all fixups are set to `false` by `Default`.
56
// Fixups should be turned on in a targeted fashion where needed.
@@ -93,6 +94,24 @@ pub(crate) struct FixupContext {
9394
/// }
9495
/// ```
9596
parenthesize_exterior_struct_lit: bool,
97+
98+
/// This is the difference between:
99+
///
100+
/// ```ignore (illustrative)
101+
/// let _ = (return) - 1; // without paren, this would return -1
102+
///
103+
/// let _ = return + 1; // no paren because '+' cannot begin expr
104+
/// ```
105+
next_operator_can_begin_expr: bool,
106+
107+
/// This is the difference between:
108+
///
109+
/// ```ignore (illustrative)
110+
/// let _ = 1 + return 1; // no parens if rightmost subexpression
111+
///
112+
/// let _ = 1 + (return 1) + 1; // needs parens
113+
/// ```
114+
next_operator_can_continue_expr: bool,
96115
}
97116

98117
impl FixupContext {
@@ -134,6 +153,8 @@ impl FixupContext {
134153
match_arm: false,
135154
leftmost_subexpression_in_match_arm: self.match_arm
136155
|| self.leftmost_subexpression_in_match_arm,
156+
next_operator_can_begin_expr: false,
157+
next_operator_can_continue_expr: true,
137158
..self
138159
}
139160
}
@@ -148,19 +169,34 @@ impl FixupContext {
148169
leftmost_subexpression_in_stmt: false,
149170
match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
150171
leftmost_subexpression_in_match_arm: false,
172+
next_operator_can_begin_expr: false,
173+
next_operator_can_continue_expr: true,
151174
..self
152175
}
153176
}
154177

155-
/// Transform this fixup into the one that should apply when printing any
156-
/// subexpression that is neither a leftmost subexpression nor surrounded in
157-
/// delimiters.
178+
/// Transform this fixup into the one that should apply when printing a
179+
/// leftmost subexpression followed by punctuation that is legal as the
180+
/// first token of an expression.
181+
pub(crate) fn leftmost_subexpression_with_operator(
182+
self,
183+
next_operator_can_begin_expr: bool,
184+
) -> Self {
185+
FixupContext { next_operator_can_begin_expr, ..self.leftmost_subexpression() }
186+
}
187+
188+
/// Transform this fixup into the one that should apply when printing the
189+
/// rightmost subexpression of the current expression.
190+
///
191+
/// The rightmost subexpression is any subexpression that has a different
192+
/// first token than the current expression, but has the same last token.
193+
///
194+
/// For example in `$a + $b` and `-$b`, the subexpression `$b` is a
195+
/// rightmost subexpression.
158196
///
159-
/// This is for any subexpression that has a different first token than the
160-
/// current expression, and is not surrounded by a paren/bracket/brace. For
161-
/// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
162-
/// `$a.f($b)`.
163-
pub(crate) fn subsequent_subexpression(self) -> Self {
197+
/// Not every expression has a rightmost subexpression. For example neither
198+
/// `[$b]` nor `$a.f($b)` have one.
199+
pub(crate) fn rightmost_subexpression(self) -> Self {
164200
FixupContext {
165201
stmt: false,
166202
leftmost_subexpression_in_stmt: false,
@@ -193,6 +229,39 @@ impl FixupContext {
193229
/// "let chain".
194230
pub(crate) fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool {
195231
self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
196-
|| parser::needs_par_as_let_scrutinee(expr.precedence())
232+
|| parser::needs_par_as_let_scrutinee(self.precedence(expr))
233+
}
234+
235+
/// Determines the effective precedence of a subexpression. Some expressions
236+
/// have higher or lower precedence when adjacent to particular operators.
237+
pub(crate) fn precedence(self, expr: &Expr) -> ExprPrecedence {
238+
if self.next_operator_can_begin_expr {
239+
// Decrease precedence of value-less jumps when followed by an
240+
// operator that would otherwise get interpreted as beginning a
241+
// value for the jump.
242+
if let ExprKind::Break(..)
243+
| ExprKind::Ret(..)
244+
| ExprKind::Yeet(..)
245+
| ExprKind::Yield(YieldKind::Prefix(..)) = expr.kind
246+
{
247+
return ExprPrecedence::Jump;
248+
}
249+
}
250+
251+
if !self.next_operator_can_continue_expr {
252+
// Increase precedence of expressions that extend to the end of
253+
// current statement or group.
254+
if let ExprKind::Break(..)
255+
| ExprKind::Closure(..)
256+
| ExprKind::Ret(..)
257+
| ExprKind::Yeet(..)
258+
| ExprKind::Yield(YieldKind::Prefix(..))
259+
| ExprKind::Range(None, ..) = expr.kind
260+
{
261+
return ExprPrecedence::Prefix;
262+
}
263+
}
264+
265+
expr.precedence()
197266
}
198267
}

tests/pretty/postfix-match/precedence.pp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
_ => {}
2727
};
2828
(4 as usize).match { _ => {} };
29-
(return).match { _ => {} };
29+
return.match { _ => {} };
3030
(a = 42).match { _ => {} };
3131
(|| {}).match { _ => {} };
3232
(42..101).match { _ => {} };

0 commit comments

Comments
 (0)