about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-12 11:12:16 +0000
committerbors <bors@rust-lang.org>2021-06-12 11:12:16 +0000
commit60f1a2fc4b535ead9c85ce085fdce49b1b097531 (patch)
tree759d0461aad1a810641068a70fd73c18c17cce4d
parentd59b80d588368cdcfcc1d54e119374a3d78169ff (diff)
parent79dc9a76a6272dc2e080237de27406b1456d7c2c (diff)
downloadrust-60f1a2fc4b535ead9c85ce085fdce49b1b097531.tar.gz
rust-60f1a2fc4b535ead9c85ce085fdce49b1b097531.zip
Auto merge of #86215 - FabianWolff:unnameable-types, r=jackh726
Do not suggest to add type annotations for unnameable types

Consider this example:
```rust
const A = || 42;

struct S<T> { t: T }
const B: _ = S { t: || 42 };
```
This currently produces the following output:
```
error: missing type for `const` item
 --> src/lib.rs:1:7
  |
1 | const A = || 42;
  |       ^ help: provide a type for the item: `A: [closure@src/lib.rs:1:11: 1:16]`

error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> src/lib.rs:4:10
  |
4 | const B: _ = S { t: || 42 };
  |          ^
  |          |
  |          not allowed in type signatures
  |          help: replace `_` with the correct type: `S<[closure@src/lib.rs:4:21: 4:26]>`

error: aborting due to 2 previous errors
```
However, these suggestions are obviously useless, because the suggested types cannot be written down. With my changes, the suggestion is replaced with a note, because there is no simple fix:
```
error: missing type for `const` item
 --> test.rs:1:7
  |
1 | const A = || 42;
  |       ^
  |
note: however, the inferred type `[closure@test.rs:1:11: 1:16]` cannot be named
 --> test.rs:1:11
  |
1 | const A = || 42;
  |           ^^^^^

error[E0121]: the type placeholder `_` is not allowed within types on item signatures
 --> test.rs:4:10
  |
4 | const B: _ = S { t: || 42 };
  |          ^ not allowed in type signatures
  |
note: however, the inferred type `S<[closure@test.rs:4:21: 4:26]>` cannot be named
 --> test.rs:4:14
  |
4 | const B: _ = S { t: || 42 };
  |              ^^^^^^^^^^^^^^

error: aborting due to 2 previous errors
```
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs85
-rw-r--r--src/test/ui/suggestions/unnamable-types.rs39
-rw-r--r--src/test/ui/suggestions/unnamable-types.stderr66
3 files changed, 176 insertions, 14 deletions
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 29a87b18a9e..abe5d69a3b3 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -9,7 +9,7 @@ use rustc_hir::{HirId, Node};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
 use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable, TypeFolder};
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, DUMMY_SP};
 
@@ -749,6 +749,40 @@ fn infer_placeholder_type(
     span: Span,
     item_ident: Ident,
 ) -> Ty<'_> {
+    // Attempts to make the type nameable by turning FnDefs into FnPtrs.
+    struct MakeNameable<'tcx> {
+        success: bool,
+        tcx: TyCtxt<'tcx>,
+    }
+
+    impl<'tcx> MakeNameable<'tcx> {
+        fn new(tcx: TyCtxt<'tcx>) -> Self {
+            MakeNameable { success: true, tcx }
+        }
+    }
+
+    impl TypeFolder<'tcx> for MakeNameable<'tcx> {
+        fn tcx(&self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+            if !self.success {
+                return ty;
+            }
+
+            match ty.kind() {
+                ty::FnDef(def_id, _) => self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id)),
+                // FIXME: non-capturing closures should also suggest a function pointer
+                ty::Closure(..) | ty::Generator(..) => {
+                    self.success = false;
+                    ty
+                }
+                _ => ty.super_fold_with(self),
+            }
+        }
+    }
+
     let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id);
 
     // If this came from a free `const` or `static mut?` item,
@@ -760,24 +794,47 @@ fn infer_placeholder_type(
             // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
             // We are typeck and have the real type, so remove that and suggest the actual type.
             err.suggestions.clear();
-            err.span_suggestion(
-                span,
-                "provide a type for the item",
-                format!("{}: {}", item_ident, ty),
-                Applicability::MachineApplicable,
-            )
-            .emit_unless(ty.references_error());
+
+            // Suggesting unnameable types won't help.
+            let mut mk_nameable = MakeNameable::new(tcx);
+            let ty = mk_nameable.fold_ty(ty);
+            let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
+            if let Some(sugg_ty) = sugg_ty {
+                err.span_suggestion(
+                    span,
+                    "provide a type for the item",
+                    format!("{}: {}", item_ident, sugg_ty),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                err.span_note(
+                    tcx.hir().body(body_id).value.span,
+                    &format!("however, the inferred type `{}` cannot be named", ty.to_string()),
+                );
+            }
+
+            err.emit_unless(ty.references_error());
         }
         None => {
             let mut diag = bad_placeholder_type(tcx, vec![span]);
 
             if !ty.references_error() {
-                diag.span_suggestion(
-                    span,
-                    "replace with the correct type",
-                    ty.to_string(),
-                    Applicability::MaybeIncorrect,
-                );
+                let mut mk_nameable = MakeNameable::new(tcx);
+                let ty = mk_nameable.fold_ty(ty);
+                let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
+                if let Some(sugg_ty) = sugg_ty {
+                    diag.span_suggestion(
+                        span,
+                        "replace with the correct type",
+                        sugg_ty.to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                } else {
+                    diag.span_note(
+                        tcx.hir().body(body_id).value.span,
+                        &format!("however, the inferred type `{}` cannot be named", ty.to_string()),
+                    );
+                }
             }
 
             diag.emit();
diff --git a/src/test/ui/suggestions/unnamable-types.rs b/src/test/ui/suggestions/unnamable-types.rs
new file mode 100644
index 00000000000..5d0616443e5
--- /dev/null
+++ b/src/test/ui/suggestions/unnamable-types.rs
@@ -0,0 +1,39 @@
+// Test that we do not suggest to add type annotations for unnamable types.
+
+#![crate_type="lib"]
+#![feature(generators)]
+
+const A = 5;
+//~^ ERROR: missing type for `const` item
+//~| HELP: provide a type for the item
+
+static B: _ = "abc";
+//~^ ERROR: the type placeholder `_` is not allowed within types on item signatures
+//~| NOTE: not allowed in type signatures
+//~| HELP: replace with the correct type
+
+
+// FIXME: this should also suggest a function pointer, as the closure is non-capturing
+const C: _ = || 42;
+//~^ ERROR: the type placeholder `_` is not allowed within types on item signatures
+//~| NOTE: not allowed in type signatures
+//~| NOTE: however, the inferred type
+
+struct S<T> { t: T }
+const D = S { t: { let i = 0; move || -> i32 { i } } };
+//~^ ERROR: missing type for `const` item
+//~| NOTE: however, the inferred type
+
+
+fn foo() -> i32 { 42 }
+const E = foo;
+//~^ ERROR: missing type for `const` item
+//~| HELP: provide a type for the item
+const F = S { t: foo };
+//~^ ERROR: missing type for `const` item
+//~| HELP: provide a type for the item
+
+
+const G = || -> i32 { yield 0; return 1; };
+//~^ ERROR: missing type for `const` item
+//~| NOTE: however, the inferred type
diff --git a/src/test/ui/suggestions/unnamable-types.stderr b/src/test/ui/suggestions/unnamable-types.stderr
new file mode 100644
index 00000000000..2c8166781bf
--- /dev/null
+++ b/src/test/ui/suggestions/unnamable-types.stderr
@@ -0,0 +1,66 @@
+error: missing type for `const` item
+  --> $DIR/unnamable-types.rs:6:7
+   |
+LL | const A = 5;
+   |       ^ help: provide a type for the item: `A: i32`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/unnamable-types.rs:10:11
+   |
+LL | static B: _ = "abc";
+   |           ^
+   |           |
+   |           not allowed in type signatures
+   |           help: replace with the correct type: `&str`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/unnamable-types.rs:17:10
+   |
+LL | const C: _ = || 42;
+   |          ^ not allowed in type signatures
+   |
+note: however, the inferred type `[closure@$DIR/unnamable-types.rs:17:14: 17:19]` cannot be named
+  --> $DIR/unnamable-types.rs:17:14
+   |
+LL | const C: _ = || 42;
+   |              ^^^^^
+
+error: missing type for `const` item
+  --> $DIR/unnamable-types.rs:23:7
+   |
+LL | const D = S { t: { let i = 0; move || -> i32 { i } } };
+   |       ^
+   |
+note: however, the inferred type `S<[closure@$DIR/unnamable-types.rs:23:31: 23:51]>` cannot be named
+  --> $DIR/unnamable-types.rs:23:11
+   |
+LL | const D = S { t: { let i = 0; move || -> i32 { i } } };
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing type for `const` item
+  --> $DIR/unnamable-types.rs:29:7
+   |
+LL | const E = foo;
+   |       ^ help: provide a type for the item: `E: fn() -> i32`
+
+error: missing type for `const` item
+  --> $DIR/unnamable-types.rs:32:7
+   |
+LL | const F = S { t: foo };
+   |       ^ help: provide a type for the item: `F: S<fn() -> i32>`
+
+error: missing type for `const` item
+  --> $DIR/unnamable-types.rs:37:7
+   |
+LL | const G = || -> i32 { yield 0; return 1; };
+   |       ^
+   |
+note: however, the inferred type `[generator@$DIR/unnamable-types.rs:37:11: 37:43 {i32, ()}]` cannot be named
+  --> $DIR/unnamable-types.rs:37:11
+   |
+LL | const G = || -> i32 { yield 0; return 1; };
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0121`.