Rollup merge of #134834 - dtolnay:unnamedcall, r=compiler-errors

Skip parenthesis around tuple struct field calls

The pretty-printer previously did not distinguish between named vs unnamed fields when printing a function call containing a struct field. It would print the call as `(self.fun)()` for a named field which is correct, and `(self.0)()` for an unnamed field which is redundant.

This PR changes function calls of tuple struct fields to print without parens.

**Before:**

```rust
struct Tuple(fn());

fn main() {
    let tuple = Tuple(|| {});
    (tuple.0)();
}
```

**After:**

```rust
struct Tuple(fn());

fn main() {
    let tuple = Tuple(|| {});
    tuple.0();
}
```
This commit is contained in:
David Tolnay 2024-12-27 18:43:05 -08:00 committed by GitHub
commit 0a09252866
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 13 additions and 2 deletions

View file

@ -213,7 +213,9 @@ impl<'a> State<'a> {
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) { fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
let needs_paren = match func.kind { 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, _ => func.precedence() < ExprPrecedence::Unambiguous,
}; };

View file

@ -1336,7 +1336,7 @@ impl<'a> Parser<'a> {
) -> bool { ) -> bool {
if let ExprKind::Binary(op, l1, r1) = &inner_op.kind { if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
if let ExprKind::Field(_, ident) = l1.kind if let ExprKind::Field(_, ident) = l1.kind
&& ident.as_str().parse::<i32>().is_err() && !ident.is_numeric()
&& !matches!(r1.kind, ExprKind::Lit(_)) && !matches!(r1.kind, ExprKind::Lit(_))
{ {
// The parser has encountered `foo.bar<baz`, the likelihood of the turbofish // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish

View file

@ -2708,6 +2708,12 @@ impl Ident {
pub fn is_raw_guess(self) -> bool { pub fn is_raw_guess(self) -> bool {
self.name.can_be_raw() && self.is_reserved() 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. /// Collect all the keywords in a given edition into a vector.

View file

@ -77,6 +77,9 @@ static EXPRS: &[&str] = &[
// These mean different things. // These mean different things.
"if let _ = true && false {}", "if let _ = true && false {}",
"if let _ = (true && false) {}", "if let _ = (true && false) {}",
// Parentheses to call a named field, but not an unnamed field.
"(self.fun)()",
"self.0()",
// Conditions end at the first curly brace, so struct expressions need to be // Conditions end at the first curly brace, so struct expressions need to be
// parenthesized. Except in a match guard, where conditions end at arrow. // parenthesized. Except in a match guard, where conditions end at arrow.
"if let _ = (Struct {}) {}", "if let _ = (Struct {}) {}",