Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt breaks method call on || ..
**Describe the bug**

Working input code:

```rust
trait Trait {
    fn method(&self);
}

impl<F: Fn() -> T, T> Trait for F {
    fn method(&self) {}
}

fn main() {
    || .. .method();
}
```

Rustfmt diff as of current master (292c0cc6b59863c9769b9aaa3470da4fb3ddf7d4):

```diff
-     || .. .method();
+     || ...method();
```

The transformed code produced by rustfmt fails to compile.

```console
error: unexpected token: `...`
  --> src/main.rs:10:8
   |
10 |     || ...method();
   |        ^^^
   |
help: use `..` for an exclusive range
   |
10 |     || ..method();
   |        ^^
help: or `..=` for an inclusive range
   |
10 |     || ..=method();
   |        ^^^

error[E0425]: cannot find function `method` in this scope
  --> src/main.rs:10:11
   |
10 |     || ...method();
   |           ^^^^^^ not found in this scope
```

**To Reproduce**

Run rustfmt on playground or locally. Try to compile code. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f76a649e0bd1b99cbebc46c711ca9bf5

**Expected behavior**

I would expect compilable output, either unchanged `|| .. .method()` or parenthesized `(|| ..).method()`.

**Meta**

- rustfmt version: rustfmt 2.0.0-rc.2-dev (292c0cc6 2021-04-20)
- From where did you install rustfmt?: built from source
- How do you run rustfmt: `rustfmt src.rs`

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_ASSIGN_START -->

<!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":"QiangHeisenberg"}$$TRIAGEBOT_ASSIGN_DATA_END -->

<!-- TRIAGEBOT_ASSIGN_END -->
<!-- TRIAGEBOT_END -->

Pull request:
fix: Add parenthesis around `..` closure if it's a method call receiver
Fixes #4808

I think this is (perhaps?) okay without a version gate as the formatted code would be invalid before now, might be wrong though ^^

Patch:
diff --git a/src/chains.rs b/src/chains.rs
index bf09d817..d4876389 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -253,11 +253,7 @@ impl ChainItemKind {
                 return (
                     ChainItemKind::Parent {
                         expr: expr.clone(),
-                        parens: is_method_call_receiver
-                            && matches!(
-                                &expr.kind,
-                                ast::ExprKind::Lit(lit) if crate::expr::lit_ends_in_dot(lit)
-                            ),
+                        parens: is_method_call_receiver && should_add_parens(expr),
                     },
                     expr.span,
                 );
@@ -982,3 +978,22 @@ fn trim_tries(s: &str) -> String {
     }
     result
 }
+
+/// Whether a method call's receiver needs parenthesis, like
+/// ```rust,ignore
+/// || .. .method();
+/// || 1.. .method();
+/// 1. .method();
+/// ```
+/// Which all need parenthesis or a space before `.method()`.
+fn should_add_parens(expr: &ast::Expr) -> bool {
+    match expr.kind {
+        ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit),
+        ast::ExprKind::Closure(ref cl) => match cl.body.kind {
+            ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true,
+            ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit),
+            _ => false,
+        },
+        _ => false,
+    }
+}


Test patch:
diff --git a/tests/source/issue-4808.rs b/tests/source/issue-4808.rs
new file mode 100644
index 00000000..93076edc
--- /dev/null
+++ b/tests/source/issue-4808.rs
@@ -0,0 +1,13 @@
+trait Trait {
+    fn method(&self) {}
+}
+
+impl<F: Fn() -> T, T> Trait for F {}
+
+impl Trait for f32 {}
+
+fn main() {
+    || 10. .method();
+    || .. .method();
+    || 1.. .method();
+}
diff --git a/tests/target/issue-4808.rs b/tests/target/issue-4808.rs
new file mode 100644
index 00000000..cdef53a1
--- /dev/null
+++ b/tests/target/issue-4808.rs
@@ -0,0 +1,13 @@
+trait Trait {
+    fn method(&self) {}
+}
+
+impl<F: Fn() -> T, T> Trait for F {}
+
+impl Trait for f32 {}
+
+fn main() {
+    || (10.).method();
+    (|| ..).method();
+    (|| 1..).method();
+}