From fef8ec5ad9ccd3249d616520e22e43602fa47883 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 27 Dec 2024 13:41:46 -0800 Subject: [PATCH 1/2] Add test of dot after eager statement boundary expr --- tests/ui-fulldeps/pprust-parenthesis-insertion.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 94c7964392d1d..8cef9fa13f900 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -108,6 +108,10 @@ static EXPRS: &[&str] = &[ "{ (match 2 {})() - 1 }", "{ (match 2 {})[0] - 1 }", "{ (loop {}) - 1 }", + "match 2 { _ => (loop {}) - 1 }", + // No eager statement boundary if followed by `.` or `?`. + "{ (loop {}).to_string() - 1 }", // FIXME: no parenthesis needed. + "match 2 { _ => (loop {}).to_string() - 1 }", // FIXME: no parenthesis needed. // Angle bracket is eagerly parsed as a path's generic argument list. "(2 as T) < U", "(2 as T) < V", // FIXME: no parentheses needed. From e67fe3698bfd0f3957abb84c2b396c50822a5b67 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 27 Dec 2024 13:42:25 -0800 Subject: [PATCH 2/2] Skip parenthesis if `.` makes statement boundary unambiguous --- .../rustc_ast_pretty/src/pprust/state/expr.rs | 28 +++++++++++-------- .../src/pprust/state/fixup.rs | 14 ++++++++++ .../pprust-parenthesis-insertion.rs | 4 +-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index dce76fb1e7707..ab1dd46fe3387 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -245,19 +245,21 @@ impl<'a> State<'a> { base_args: &[P], fixup: FixupContext, ) { - // Unlike in `print_expr_call`, no change to fixup here because + // The fixup here is different than in `print_expr_call` because // statement boundaries never occur in front of a `.` (or `?`) token. // - // match () { _ => f }.method(); + // Needs parens: + // + // (loop { break x; })(); + // + // Does not need parens: + // + // loop { break x; }.method(); // - // Parenthesizing only for precedence and not with regard to statement - // boundaries, `$receiver.method()` can be parsed back as a statement - // containing an expression if and only if `$receiver` can be parsed as - // a statement containing an expression. self.print_expr_cond_paren( receiver, receiver.precedence() < ExprPrecedence::Unambiguous, - fixup, + fixup.leftmost_subexpression_with_dot(), ); self.word("."); @@ -503,7 +505,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren( expr, expr.precedence() < ExprPrecedence::Unambiguous, - fixup, + fixup.leftmost_subexpression_with_dot(), ); self.word_nbsp(".match"); } @@ -567,7 +569,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren( expr, expr.precedence() < ExprPrecedence::Unambiguous, - fixup, + fixup.leftmost_subexpression_with_dot(), ); self.word(".await"); } @@ -606,7 +608,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren( expr, expr.precedence() < ExprPrecedence::Unambiguous, - fixup, + fixup.leftmost_subexpression_with_dot(), ); self.word("."); self.print_ident(*ident); @@ -763,7 +765,11 @@ impl<'a> State<'a> { } } ast::ExprKind::Try(e) => { - self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup); + self.print_expr_cond_paren( + e, + e.precedence() < ExprPrecedence::Unambiguous, + fixup.leftmost_subexpression_with_dot(), + ); self.word("?") } ast::ExprKind::TryBlock(blk) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs index ff466703f7374..3ef21f5cb29eb 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -138,6 +138,20 @@ impl FixupContext { } } + /// Transform this fixup into the one that should apply when printing a + /// leftmost subexpression followed by a `.` or `?` token, which confer + /// different statement boundary rules compared to other leftmost + /// subexpressions. + pub(crate) fn leftmost_subexpression_with_dot(self) -> Self { + FixupContext { + stmt: self.stmt || self.leftmost_subexpression_in_stmt, + leftmost_subexpression_in_stmt: false, + match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, + leftmost_subexpression_in_match_arm: false, + ..self + } + } + /// Transform this fixup into the one that should apply when printing any /// subexpression that is neither a leftmost subexpression nor surrounded in /// delimiters. diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 8cef9fa13f900..dc45731dce76f 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -110,8 +110,8 @@ static EXPRS: &[&str] = &[ "{ (loop {}) - 1 }", "match 2 { _ => (loop {}) - 1 }", // No eager statement boundary if followed by `.` or `?`. - "{ (loop {}).to_string() - 1 }", // FIXME: no parenthesis needed. - "match 2 { _ => (loop {}).to_string() - 1 }", // FIXME: no parenthesis needed. + "{ loop {}.to_string() - 1 }", + "match 2 { _ => loop {}.to_string() - 1 }", // Angle bracket is eagerly parsed as a path's generic argument list. "(2 as T) < U", "(2 as T) < V", // FIXME: no parentheses needed.