From f687d5c43a0c90afba6d3fddab7665772bb0664a Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 11 Jun 2021 13:30:12 +0200 Subject: [PATCH 1/2] Do not suggest to add type annotations for unnameable types --- compiler/rustc_typeck/src/collect/type_of.rs | 54 +++++++++++---- src/test/ui/suggestions/unnamable-types.rs | 33 ++++++++++ .../ui/suggestions/unnamable-types.stderr | 66 +++++++++++++++++++ 3 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/suggestions/unnamable-types.rs create mode 100644 src/test/ui/suggestions/unnamable-types.stderr diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 29a87b18a9eb..38678cc76e0d 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -749,6 +749,17 @@ fn infer_placeholder_type( span: Span, item_ident: Ident, ) -> Ty<'_> { + fn contains_anonymous(ty: Ty<'_>) -> bool { + for gen_arg in ty.walk() { + if let ty::subst::GenericArgKind::Type(inner_ty) = gen_arg.unpack() { + if let ty::FnDef(..) | ty::Closure(..) | ty::Generator(..) = inner_ty.kind() { + return true; + } + } + } + false + } + 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 +771,41 @@ 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. + if !contains_anonymous(ty) { + err.span_suggestion( + span, + "provide a type for the item", + format!("{}: {}", item_ident, 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, - ); + if !contains_anonymous(ty) { + diag.span_suggestion( + span, + "replace with the correct type", + 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 000000000000..ed70bcf5484c --- /dev/null +++ b/src/test/ui/suggestions/unnamable-types.rs @@ -0,0 +1,33 @@ +// 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 + + +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 } +const D = S { t: || -> i32 { 42 } }; +//~^ ERROR: missing type for `const` item +//~| NOTE: however, the inferred type + +fn foo() -> i32 { 42 } +const E = S { t: foo }; +//~^ ERROR: missing type for `const` item +//~| NOTE: however, the inferred type + +const F = || -> 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 000000000000..8082707fd3c2 --- /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:16:10 + | +LL | const C: _ = || 42; + | ^ not allowed in type signatures + | +note: however, the inferred type `[closure@$DIR/unnamable-types.rs:16:14: 16:19]` cannot be named + --> $DIR/unnamable-types.rs:16:14 + | +LL | const C: _ = || 42; + | ^^^^^ + +error: missing type for `const` item + --> $DIR/unnamable-types.rs:22:7 + | +LL | const D = S { t: || -> i32 { 42 } }; + | ^ + | +note: however, the inferred type `S<[closure@$DIR/unnamable-types.rs:22:18: 22:34]>` cannot be named + --> $DIR/unnamable-types.rs:22:11 + | +LL | const D = S { t: || -> i32 { 42 } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing type for `const` item + --> $DIR/unnamable-types.rs:27:7 + | +LL | const E = S { t: foo }; + | ^ + | +note: however, the inferred type `S i32 {foo}>` cannot be named + --> $DIR/unnamable-types.rs:27:11 + | +LL | const E = S { t: foo }; + | ^^^^^^^^^^^^ + +error: missing type for `const` item + --> $DIR/unnamable-types.rs:31:7 + | +LL | const F = || -> i32 { yield 0; return 1; }; + | ^ + | +note: however, the inferred type `[generator@$DIR/unnamable-types.rs:31:11: 31:43 {i32, ()}]` cannot be named + --> $DIR/unnamable-types.rs:31:11 + | +LL | const F = || -> i32 { yield 0; return 1; }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0121`. From 79dc9a76a6272dc2e080237de27406b1456d7c2c Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 11 Jun 2021 20:12:40 +0200 Subject: [PATCH 2/2] Suggest a FnPtr type if a FnDef type is found --- compiler/rustc_typeck/src/collect/type_of.rs | 51 +++++++++++++++---- src/test/ui/suggestions/unnamable-types.rs | 14 +++-- .../ui/suggestions/unnamable-types.stderr | 46 ++++++++--------- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 38678cc76e0d..abe5d69a3b3c 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,15 +749,38 @@ fn infer_placeholder_type( span: Span, item_ident: Ident, ) -> Ty<'_> { - fn contains_anonymous(ty: Ty<'_>) -> bool { - for gen_arg in ty.walk() { - if let ty::subst::GenericArgKind::Type(inner_ty) = gen_arg.unpack() { - if let ty::FnDef(..) | ty::Closure(..) | ty::Generator(..) = inner_ty.kind() { - return true; + // 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), } } - false } let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); @@ -773,11 +796,14 @@ fn infer_placeholder_type( err.suggestions.clear(); // Suggesting unnameable types won't help. - if !contains_anonymous(ty) { + 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, ty), + format!("{}: {}", item_ident, sugg_ty), Applicability::MachineApplicable, ); } else { @@ -793,11 +819,14 @@ fn infer_placeholder_type( let mut diag = bad_placeholder_type(tcx, vec![span]); if !ty.references_error() { - if !contains_anonymous(ty) { + 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", - ty.to_string(), + sugg_ty.to_string(), Applicability::MaybeIncorrect, ); } else { diff --git a/src/test/ui/suggestions/unnamable-types.rs b/src/test/ui/suggestions/unnamable-types.rs index ed70bcf5484c..5d0616443e5a 100644 --- a/src/test/ui/suggestions/unnamable-types.rs +++ b/src/test/ui/suggestions/unnamable-types.rs @@ -13,21 +13,27 @@ static B: _ = "abc"; //~| 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 } -const D = S { t: || -> i32 { 42 } }; +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 = S { t: foo }; +const E = foo; //~^ ERROR: missing type for `const` item -//~| NOTE: however, the inferred type +//~| 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 F = || -> i32 { yield 0; return 1; }; +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 index 8082707fd3c2..2c8166781bfd 100644 --- a/src/test/ui/suggestions/unnamable-types.stderr +++ b/src/test/ui/suggestions/unnamable-types.stderr @@ -14,53 +14,53 @@ LL | static B: _ = "abc"; | help: replace with the correct type: `&str` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/unnamable-types.rs:16:10 + --> $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:16:14: 16:19]` cannot be named - --> $DIR/unnamable-types.rs:16:14 +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:22:7 + --> $DIR/unnamable-types.rs:23:7 | -LL | const D = S { t: || -> i32 { 42 } }; +LL | const D = S { t: { let i = 0; move || -> i32 { i } } }; | ^ | -note: however, the inferred type `S<[closure@$DIR/unnamable-types.rs:22:18: 22:34]>` cannot be named - --> $DIR/unnamable-types.rs:22:11 +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: || -> i32 { 42 } }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const D = S { t: { let i = 0; move || -> i32 { i } } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing type for `const` item - --> $DIR/unnamable-types.rs:27:7 + --> $DIR/unnamable-types.rs:29:7 | -LL | const E = S { t: foo }; - | ^ - | -note: however, the inferred type `S i32 {foo}>` cannot be named - --> $DIR/unnamable-types.rs:27:11 +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 E = S { t: foo }; - | ^^^^^^^^^^^^ +LL | const F = S { t: foo }; + | ^ help: provide a type for the item: `F: S i32>` error: missing type for `const` item - --> $DIR/unnamable-types.rs:31:7 + --> $DIR/unnamable-types.rs:37:7 | -LL | const F = || -> i32 { yield 0; return 1; }; +LL | const G = || -> i32 { yield 0; return 1; }; | ^ | -note: however, the inferred type `[generator@$DIR/unnamable-types.rs:31:11: 31:43 {i32, ()}]` cannot be named - --> $DIR/unnamable-types.rs:31:11 +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 F = || -> i32 { yield 0; return 1; }; +LL | const G = || -> i32 { yield 0; return 1; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0121`.