Skip to content

Commit f3b19f5

Browse files
authored
Rollup merge of #133782 - dtolnay:closuresjumps, r=spastorino,traviscross
Precedence improvements: closures and jumps This PR fixes some cases where rustc's pretty printers would redundantly parenthesize expressions that didn't need it. <table> <tr><th>Before</th><th>After</th></tr> <tr><td><code>return (|x: i32| x)</code></td><td><code>return |x: i32| x</code></td></tr> <tr><td><code>(|| -> &mut () { std::process::abort() }).clone()</code></td><td><code>|| -> &mut () { std::process::abort() }.clone()</code></td></tr> <tr><td><code>(continue) + 1</code></td><td><code>continue + 1</code></td></tr> </table> Tested by `echo "fn main() { let _ = $AFTER; }" | rustc -Zunpretty=expanded /dev/stdin`. The pretty-printer aims to render the syntax tree as it actually exists in rustc, as faithfully as possible, in Rust syntax. It can insert parentheses where forced by Rust's grammar in order to preserve the meaning of a macro-generated syntax tree, for example in the case of `a * $rhs` where $rhs is `b + c`. But for any expression parsed from source code, without a macro involved, there should never be a reason for inserting additional parentheses not present in the original. For closures and jumps (return, break, continue, yield, do yeet, become) the unneeded parentheses came from the precedence of some of these expressions being misidentified. In the same order as the table above: - Jumps and closures are supposed to have equal precedence. The [Rust Reference](https://doc.rust-lang.org/1.83.0/reference/expressions.html#expression-precedence) says so, and in Syn they do. There is no Rust syntax that would require making a precedence distinction between jumps and closures. But in rustc these were previously 2 distinct levels with the closure being lower, hence the parentheses around a closure inside a jump (but not a jump inside a closure). - When a closure is written with an explicit return type, the grammar [requires](https://doc.rust-lang.org/1.83.0/reference/expressions/closure-expr.html) that the closure body consists of exactly one block expression, not any other arbitrary expression as usual for closures. Parsing of the closure body does not continue after the block expression. So in `|| { 0 }.clone()` the clone is inside the closure body and applies to `{ 0 }`, whereas in `|| -> _ { 0 }.clone()` the clone is outside and applies to the closure as a whole. - Continue never needs parentheses. It was previously marked as having the lowest possible precedence but it should have been the highest, next to paths and loops and function calls, not next to jumps.
2 parents 472bbb9 + fe06c5d commit f3b19f5

File tree

4 files changed

+24
-9
lines changed

4 files changed

+24
-9
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1322,11 +1322,15 @@ impl Expr {
13221322
}
13231323

13241324
pub fn precedence(&self) -> ExprPrecedence {
1325-
match self.kind {
1326-
ExprKind::Closure(..) => ExprPrecedence::Closure,
1325+
match &self.kind {
1326+
ExprKind::Closure(closure) => {
1327+
match closure.fn_decl.output {
1328+
FnRetTy::Default(_) => ExprPrecedence::Jump,
1329+
FnRetTy::Ty(_) => ExprPrecedence::Unambiguous,
1330+
}
1331+
}
13271332

13281333
ExprKind::Break(..)
1329-
| ExprKind::Continue(..)
13301334
| ExprKind::Ret(..)
13311335
| ExprKind::Yield(..)
13321336
| ExprKind::Yeet(..)
@@ -1360,6 +1364,7 @@ impl Expr {
13601364
| ExprKind::Block(..)
13611365
| ExprKind::Call(..)
13621366
| ExprKind::ConstBlock(_)
1367+
| ExprKind::Continue(..)
13631368
| ExprKind::Field(..)
13641369
| ExprKind::ForLoop { .. }
13651370
| ExprKind::FormatArgs(..)

Diff for: compiler/rustc_ast/src/util/parser.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,7 @@ impl AssocOp {
231231

232232
#[derive(Clone, Copy, PartialEq, PartialOrd)]
233233
pub enum ExprPrecedence {
234-
Closure,
235-
// return, break, yield
234+
// return, break, yield, closures
236235
Jump,
237236
// = += -= *= /= %= &= |= ^= <<= >>=
238237
Assign,

Diff for: compiler/rustc_hir/src/hir.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1943,11 +1943,15 @@ pub struct Expr<'hir> {
19431943

19441944
impl Expr<'_> {
19451945
pub fn precedence(&self) -> ExprPrecedence {
1946-
match self.kind {
1947-
ExprKind::Closure { .. } => ExprPrecedence::Closure,
1946+
match &self.kind {
1947+
ExprKind::Closure(closure) => {
1948+
match closure.fn_decl.output {
1949+
FnRetTy::DefaultReturn(_) => ExprPrecedence::Jump,
1950+
FnRetTy::Return(_) => ExprPrecedence::Unambiguous,
1951+
}
1952+
}
19481953

19491954
ExprKind::Break(..)
1950-
| ExprKind::Continue(..)
19511955
| ExprKind::Ret(..)
19521956
| ExprKind::Yield(..)
19531957
| ExprKind::Become(..) => ExprPrecedence::Jump,
@@ -1973,6 +1977,7 @@ impl Expr<'_> {
19731977
| ExprKind::Block(..)
19741978
| ExprKind::Call(..)
19751979
| ExprKind::ConstBlock(_)
1980+
| ExprKind::Continue(..)
19761981
| ExprKind::Field(..)
19771982
| ExprKind::If(..)
19781983
| ExprKind::Index(..)
@@ -1990,7 +1995,7 @@ impl Expr<'_> {
19901995
| ExprKind::UnsafeBinderCast(..)
19911996
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
19921997

1993-
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
1998+
ExprKind::DropTemps(expr, ..) => expr.precedence(),
19941999
}
19952000
}
19962001

Diff for: tests/ui-fulldeps/pprust-parenthesis-insertion.rs

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ static EXPRS: &[&str] = &[
6868
// These mean different things.
6969
"return - 2",
7070
"(return) - 2",
71+
// Closures and jumps have equal precedence.
72+
"|| return break 2",
73+
"return break || 2",
74+
// Closures with a return type have especially high precedence.
75+
"|| -> T { x } + 1",
76+
"(|| { x }) + 1",
7177
// These mean different things.
7278
"if let _ = true && false {}",
7379
"if let _ = (true && false) {}",

0 commit comments

Comments
 (0)