From c95f9f50de555454cbd931c026814e46793a820a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 27 Dec 2024 14:18:39 -0800 Subject: [PATCH 1/2] Add pretty-printer test of tuple field function call --- tests/ui-fulldeps/pprust-parenthesis-insertion.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 94c7964392d1d..fa9b0300743f3 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -77,6 +77,9 @@ static EXPRS: &[&str] = &[ // These mean different things. "if let _ = true && false {}", "if let _ = (true && false) {}", + // Parentheses to call a named field, but not an unnamed field. + "(self.fun)()", + "(self.0)()", // FIXME: no parenthesis needed. // Conditions end at the first curly brace, so struct expressions need to be // parenthesized. Except in a match guard, where conditions end at arrow. "if let _ = (Struct {}) {}", From 26bb4e64645d3f3e0ed0b2921738e56888f96fd1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 27 Dec 2024 14:25:08 -0800 Subject: [PATCH 2/2] Skip parenthesis around tuple struct field calls --- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 4 +++- compiler/rustc_parse/src/parser/diagnostics.rs | 2 +- compiler/rustc_span/src/symbol.rs | 6 ++++++ tests/ui-fulldeps/pprust-parenthesis-insertion.rs | 2 +- 4 files changed, 11 insertions(+), 3 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..f7d8edc4743a6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -213,7 +213,9 @@ impl<'a> State<'a> { fn print_expr_call(&mut self, func: &ast::Expr, args: &[P], fixup: FixupContext) { let needs_paren = match func.kind { - ast::ExprKind::Field(..) => true, + // In order to call a named field, needs parens: `(self.fun)()` + // But not for an unnamed field: `self.0()` + ast::ExprKind::Field(_, name) => !name.is_numeric(), _ => func.precedence() < ExprPrecedence::Unambiguous, }; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index aab4e1b1afc1b..7df7e7329252c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1336,7 +1336,7 @@ impl<'a> Parser<'a> { ) -> bool { if let ExprKind::Binary(op, l1, r1) = &inner_op.kind { if let ExprKind::Field(_, ident) = l1.kind - && ident.as_str().parse::().is_err() + && !ident.is_numeric() && !matches!(r1.kind, ExprKind::Lit(_)) { // The parser has encountered `foo.bar bool { self.name.can_be_raw() && self.is_reserved() } + + /// Whether this would be the identifier for a tuple field like `self.0`, as + /// opposed to a named field like `self.thing`. + pub fn is_numeric(self) -> bool { + !self.name.is_empty() && self.as_str().bytes().all(|b| b.is_ascii_digit()) + } } /// Collect all the keywords in a given edition into a vector. diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index fa9b0300743f3..2697c5f66ce78 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -79,7 +79,7 @@ static EXPRS: &[&str] = &[ "if let _ = (true && false) {}", // Parentheses to call a named field, but not an unnamed field. "(self.fun)()", - "(self.0)()", // FIXME: no parenthesis needed. + "self.0()", // Conditions end at the first curly brace, so struct expressions need to be // parenthesized. Except in a match guard, where conditions end at arrow. "if let _ = (Struct {}) {}",