about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-12-28 14:13:08 +0100
committerGitHub <noreply@github.com>2020-12-28 14:13:08 +0100
commit12ac31235151bbce00be0e0324904c3803e5083d (patch)
treea263538509e02e4c18cf2523941f6f954455a7fe
parentd3c43ac244741221393b685423ce771e88e0ac7e (diff)
parent5e6dc927f7a935874a08c2c3913f24295711d8f2 (diff)
Rollup merge of #80284 - ThePuzzlemaker:issue-80179-fix, r=varkor
Suggest fn ptr rather than fn item and suggest to use `Fn` trait bounds rather than the unique closure type in E0121

Previously, using `_` as a return type in a function that returned a function/closure would provide a diagnostic that would cause a papercut. For example:
```rust
fn f() -> i32 { 0 }
fn fn_ptr() -> _ { f }
fn closure() -> _ { || 0 }
```
would result in this diagnostic:
```rust
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> <anon>:2:16
  |
2 | fn fn_ptr() -> _ { f }
  |                ^
  |                |
  |                not allowed in type signatures
  |                help: replace with the correct return type: `fn() -> i32 {f}`

error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> <anon>:3:17
  |
3 | fn closure() -> _ { || 0 }
  |                 ^
  |                 |
  |                 not allowed in type signatures
  |                 help: replace with the correct return type: `[closure@<anon>:3:21: 3:25]`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0121`.
```
As can be seen, it was suggested to use the function definition return type `fn() -> i32 { f }` which is not valid syntax as a return type. Additionally, closures cause a papercut as unique closure types (notated in this case as `[closure@<anon>:3:21: 3:25]`) are not valid syntax either.

Instead, this PR implements this version of the diagnostic (this example is for the same code featured above):
```rust
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> <anon>:2:16
  |
2 | fn fn_ptr() -> _ { f }
  |                ^
  |                |
  |                not allowed in type signatures
  |                help: replace with the correct return type: `fn() -> i32`

error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> <anon>:3:17
  |
3 | fn closure() -> _ { || 0 }
  |                 ^ not allowed in type signatures
  |
  = help: consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
  = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0121`.
```
As can be seen in this diagnostic, the papercut for returning a function item is fixed by suggesting the usage of a function pointer as the return type. As for closures, it's suggested to use an `Fn`, `FnMut`, or `FnOnce` trait bound (with further reading on closures and `Fn` traits in *The Book* for beginners). I did not implement a suggestion to use `impl Fn() -> i32` syntax as that was out-of-scope for my abilities at the moment, therefore someone in the future may want to implement that. Also, it's possible to use either `impl Trait` syntax, generics, or generics with a `where` clause, and some users may not want to use `impl Trait` syntax for their own reasons.

This PR fixes #80179.
-rw-r--r--compiler/rustc_typeck/src/collect.rs27
-rw-r--r--src/test/ui/fn/issue-80179.rs27
-rw-r--r--src/test/ui/fn/issue-80179.stderr21
3 files changed, 69 insertions, 6 deletions
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index dd4bfc7a751..caa03f79ca2 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1544,12 +1544,27 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
                     let mut diag = bad_placeholder_type(tcx, visitor.0);
                     let ret_ty = fn_sig.output();
                     if ret_ty != tcx.ty_error() {
-                        diag.span_suggestion(
-                            ty.span,
-                            "replace with the correct return type",
-                            ret_ty.to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
+                        if !ret_ty.is_closure() {
+                            let ret_ty_str = match ret_ty.kind() {
+                                // Suggest a function pointer return type instead of a unique function definition
+                                // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid
+                                // syntax)
+                                ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(),
+                                _ => ret_ty.to_string(),
+                            };
+                            diag.span_suggestion(
+                                ty.span,
+                                "replace with the correct return type",
+                                ret_ty_str,
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
+                            // to prevent the user from getting a papercut while trying to use the unique closure
+                            // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
+                            diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
+                            diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
+                        }
                     }
                     diag.emit();
                     ty::Binder::bind(fn_sig)
diff --git a/src/test/ui/fn/issue-80179.rs b/src/test/ui/fn/issue-80179.rs
new file mode 100644
index 00000000000..7609b1525cc
--- /dev/null
+++ b/src/test/ui/fn/issue-80179.rs
@@ -0,0 +1,27 @@
+// Functions with a type placeholder `_` as the return type should
+// show a function pointer suggestion when given a function item
+// and suggest how to return closures correctly from a function.
+// This is a regression test of #80179
+
+fn returns_i32() -> i32 {
+    0
+}
+
+fn returns_fn_ptr() -> _ {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121]
+//~| NOTE not allowed in type signatures
+//~| HELP replace with the correct return type
+//~| SUGGESTION fn() -> i32
+    returns_i32
+}
+
+fn returns_closure() -> _ {
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121]
+//~| NOTE not allowed in type signatures
+//~| HELP consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
+//~| NOTE for more information on `Fn` traits and closure types, see
+//        https://doc.rust-lang.org/book/ch13-01-closures.html
+    || 0
+}
+
+fn main() {}
diff --git a/src/test/ui/fn/issue-80179.stderr b/src/test/ui/fn/issue-80179.stderr
new file mode 100644
index 00000000000..63571e71b34
--- /dev/null
+++ b/src/test/ui/fn/issue-80179.stderr
@@ -0,0 +1,21 @@
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/issue-80179.rs:10:24
+   |
+LL | fn returns_fn_ptr() -> _ {
+   |                        ^
+   |                        |
+   |                        not allowed in type signatures
+   |                        help: replace with the correct return type: `fn() -> i32`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/issue-80179.rs:18:25
+   |
+LL | fn returns_closure() -> _ {
+   |                         ^ not allowed in type signatures
+   |
+   = help: consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
+   = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0121`.