From b8708e2c9a264f1db74700c7471b8038e2d7f8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Aug 2019 16:50:46 -0700 Subject: [PATCH 1/7] When needing type annotations in local bindings, account for impl Trait and closures Do not suggest nonsensical types when the type inference is failing on `impl Trait` or anonymous closures. --- .../infer/error_reporting/need_type_info.rs | 18 ++++++++++++++++-- ...infer-async-enabled-impl-trait-bindings.rs | 18 ++++++++++++++++++ ...r-async-enabled-impl-trait-bindings.stderr | 19 +++++++++++++++++++ src/test/ui/inference/cannot-infer-async.rs | 16 ++++++++++++++++ .../ui/inference/cannot-infer-async.stderr | 11 +++++++++++ src/test/ui/inference/cannot-infer-closure.rs | 6 ++++++ .../ui/inference/cannot-infer-closure.stderr | 11 +++++++++++ 7 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs create mode 100644 src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr create mode 100644 src/test/ui/inference/cannot-infer-async.rs create mode 100644 src/test/ui/inference/cannot-infer-async.stderr create mode 100644 src/test/ui/inference/cannot-infer-closure.rs create mode 100644 src/test/ui/inference/cannot-infer-closure.stderr diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 770d5155777bb..263b052e71f0f 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -151,12 +151,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // | the type parameter `E` is specified // ``` let (ty_msg, suffix) = match &local_visitor.found_ty { - Some(ty) if &ty.to_string() != "_" && name == "_" => { + Some(ty) if &ty.to_string() != "_" && + name == "_" && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && + !ty.is_closure() => // The suggestion doesn't make sense for closures. + { let ty = ty_to_string(ty); (format!(" for `{}`", ty), format!("the explicit type `{}`, with the type parameters specified", ty)) } - Some(ty) if &ty.to_string() != "_" && ty.to_string() != name => { + Some(ty) if &ty.to_string() != "_" && + ty.to_string() != name && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && + !ty.is_closure() => // The suggestion doesn't make sense for closures. + { let ty = ty_to_string(ty); (format!(" for `{}`", ty), format!( @@ -165,6 +175,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { name, )) } + Some(ty) if ty.is_closure() => ( + " for the closure".to_string(), + "a boxed closure type like `Box _>`".to_string(), + ), _ => (String::new(), "a type".to_owned()), }; let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))]; diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs new file mode 100644 index 0000000000000..fb6fd60c22f04 --- /dev/null +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs @@ -0,0 +1,18 @@ +// edition:2018 +#![feature(async_await)] +#![feature(impl_trait_in_bindings)] +//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash + +use std::io::Error; + +fn make_unit() -> Result<(), Error> { + Ok(()) +} + +fn main() { + let fut = async { + make_unit()?; //~ ERROR type annotations needed + + Ok(()) + }; +} diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr new file mode 100644 index 0000000000000..67a834a2e957a --- /dev/null +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr @@ -0,0 +1,19 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash + --> $DIR/cannot-infer-async-enabled-impl-trait-bindings.rs:3:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0282]: type annotations needed for `impl std::future::Future` + --> $DIR/cannot-infer-async-enabled-impl-trait-bindings.rs:14:9 + | +LL | let fut = async { + | --- consider giving `fut` the explicit type `impl std::future::Future`, with the type parameters specified +LL | make_unit()?; + | ^^^^^^^^^^^^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/inference/cannot-infer-async.rs b/src/test/ui/inference/cannot-infer-async.rs new file mode 100644 index 0000000000000..354942121b0a5 --- /dev/null +++ b/src/test/ui/inference/cannot-infer-async.rs @@ -0,0 +1,16 @@ +// edition:2018 +#![feature(async_await)] + +use std::io::Error; + +fn make_unit() -> Result<(), Error> { + Ok(()) +} + +fn main() { + let fut = async { + make_unit()?; //~ ERROR type annotations needed + + Ok(()) + }; +} diff --git a/src/test/ui/inference/cannot-infer-async.stderr b/src/test/ui/inference/cannot-infer-async.stderr new file mode 100644 index 0000000000000..36608a11bb730 --- /dev/null +++ b/src/test/ui/inference/cannot-infer-async.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/cannot-infer-async.rs:12:9 + | +LL | let fut = async { + | --- consider giving `fut` a type +LL | make_unit()?; + | ^^^^^^^^^^^^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/inference/cannot-infer-closure.rs b/src/test/ui/inference/cannot-infer-closure.rs new file mode 100644 index 0000000000000..9c1e609ce6431 --- /dev/null +++ b/src/test/ui/inference/cannot-infer-closure.rs @@ -0,0 +1,6 @@ +fn main() { + let x = || { + Err(())?; //~ ERROR type annotations needed for the closure + Ok(()) + }; +} diff --git a/src/test/ui/inference/cannot-infer-closure.stderr b/src/test/ui/inference/cannot-infer-closure.stderr new file mode 100644 index 0000000000000..b8ca099aec13d --- /dev/null +++ b/src/test/ui/inference/cannot-infer-closure.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed for the closure + --> $DIR/cannot-infer-closure.rs:3:9 + | +LL | let x = || { + | - consider giving `x` a boxed closure type like `Box _>` +LL | Err(())?; + | ^^^^^^^^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From 3b6d46c6404cbb076ac07c568c6c4eb6d370994e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Aug 2019 18:12:19 -0700 Subject: [PATCH 2/7] Resolve types when suggesting boxed closure --- .../infer/error_reporting/need_type_info.rs | 24 +++++++++++++++---- src/librustc/ty/sty.rs | 3 +++ src/test/ui/inference/cannot-infer-closure.rs | 6 ++--- .../ui/inference/cannot-infer-closure.stderr | 8 +++---- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 263b052e71f0f..cff2b41e9eca1 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -175,10 +175,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { name, )) } - Some(ty) if ty.is_closure() => ( - " for the closure".to_string(), - "a boxed closure type like `Box _>`".to_string(), - ), + Some(ty::TyS { sty: ty::TyKind::Closure(def_id, substs), .. }) => { + let msg = " for the closure".to_string(); + let fn_sig = substs.closure_sig(*def_id, self.tcx); + let args = fn_sig.inputs() + .skip_binder() + .iter() + .next() + .map(|args| args.tuple_fields() + .map(|arg| arg.to_string()) + .collect::>().join(", ")) + .unwrap_or_else(String::new); + // This suggestion is incomplete, as the user will get further type inference + // errors due to the `_` placeholders and the introduction of `Box`, but it does + // nudge them in the right direction. + (msg, format!( + "a boxed closure type like `Box {}>`", + args, + fn_sig.output().skip_binder().to_string(), + )) + } _ => (String::new(), "a type".to_owned()), }; let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))]; diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 129ea9b5b674a..02593d4d609fd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2068,6 +2068,9 @@ impl<'tcx> TyS<'tcx> { Error => { // ignore errors (#54954) ty::Binder::dummy(FnSig::fake()) } + Closure(..) => bug!( + "to get the signature of a closure, use `closure_sig()` not `fn_sig()`", + ), _ => bug!("Ty::fn_sig() called on non-fn type: {:?}", self) } } diff --git a/src/test/ui/inference/cannot-infer-closure.rs b/src/test/ui/inference/cannot-infer-closure.rs index 9c1e609ce6431..8f48483c25421 100644 --- a/src/test/ui/inference/cannot-infer-closure.rs +++ b/src/test/ui/inference/cannot-infer-closure.rs @@ -1,6 +1,6 @@ fn main() { - let x = || { - Err(())?; //~ ERROR type annotations needed for the closure - Ok(()) + let x = |a: (), b: ()| { + Err(a)?; //~ ERROR type annotations needed for the closure + Ok(b) }; } diff --git a/src/test/ui/inference/cannot-infer-closure.stderr b/src/test/ui/inference/cannot-infer-closure.stderr index b8ca099aec13d..20b2784a45d84 100644 --- a/src/test/ui/inference/cannot-infer-closure.stderr +++ b/src/test/ui/inference/cannot-infer-closure.stderr @@ -1,10 +1,10 @@ error[E0282]: type annotations needed for the closure --> $DIR/cannot-infer-closure.rs:3:9 | -LL | let x = || { - | - consider giving `x` a boxed closure type like `Box _>` -LL | Err(())?; - | ^^^^^^^^ cannot infer type +LL | let x = |a: (), b: ()| { + | - consider giving `x` a boxed closure type like `Box std::result::Result<(), _>>` +LL | Err(a)?; + | ^^^^^^^ cannot infer type error: aborting due to previous error From 1e6f7537629b668eca7a5c2e94205552b7f8f4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Aug 2019 18:42:19 -0700 Subject: [PATCH 3/7] Fix tidy --- src/librustc/infer/error_reporting/need_type_info.rs | 2 +- .../cannot-infer-async-enabled-impl-trait-bindings.rs | 4 ++-- src/test/ui/inference/cannot-infer-async.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index cff2b41e9eca1..6d16580384ed7 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -175,7 +175,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { name, )) } - Some(ty::TyS { sty: ty::TyKind::Closure(def_id, substs), .. }) => { + Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { let msg = " for the closure".to_string(); let fn_sig = substs.closure_sig(*def_id, self.tcx); let args = fn_sig.inputs() diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs index fb6fd60c22f04..30ed5050433a6 100644 --- a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs @@ -5,14 +5,14 @@ use std::io::Error; -fn make_unit() -> Result<(), Error> { +fn make_unit() -> Result<(), Error> { Ok(()) } fn main() { let fut = async { make_unit()?; //~ ERROR type annotations needed - + Ok(()) }; } diff --git a/src/test/ui/inference/cannot-infer-async.rs b/src/test/ui/inference/cannot-infer-async.rs index 354942121b0a5..edc64276e7ce4 100644 --- a/src/test/ui/inference/cannot-infer-async.rs +++ b/src/test/ui/inference/cannot-infer-async.rs @@ -3,14 +3,14 @@ use std::io::Error; -fn make_unit() -> Result<(), Error> { +fn make_unit() -> Result<(), Error> { Ok(()) } fn main() { let fut = async { make_unit()?; //~ ERROR type annotations needed - + Ok(()) }; } From fb2511c3c581d811d61e6022f863a5c0e7006ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Aug 2019 20:22:58 -0700 Subject: [PATCH 4/7] Suggest `Box::new` when appropriate When encountering a boxed value as expected and a stack allocated value that could be boxed to fulfill the expectation, like in the following snippet, suggest `Box::new` wrapping. --- src/librustc/hir/map/mod.rs | 28 ++++++++++++++--- src/librustc_typeck/check/demand.rs | 1 + src/librustc_typeck/check/mod.rs | 35 ++++++++++++++++++++++ src/test/ui/suggestions/suggest-box.fixed | 8 +++++ src/test/ui/suggestions/suggest-box.rs | 8 +++++ src/test/ui/suggestions/suggest-box.stderr | 24 +++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-box.fixed create mode 100644 src/test/ui/suggestions/suggest-box.rs create mode 100644 src/test/ui/suggestions/suggest-box.stderr diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index b85738dd29a6d..b68cef2b6ea92 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -650,11 +650,31 @@ impl<'hir> Map<'hir> { } pub fn is_const_scope(&self, hir_id: HirId) -> bool { - self.walk_parent_nodes(hir_id, |node| match *node { - Node::Item(Item { node: ItemKind::Const(_, _), .. }) => true, - Node::Item(Item { node: ItemKind::Fn(_, header, _, _), .. }) => header.is_const(), + let parent_id = self.get_parent_item(hir_id); + match self.get(parent_id) { + Node::Item(&Item { + node: ItemKind::Const(..), + .. + }) + | Node::TraitItem(&TraitItem { + node: TraitItemKind::Const(..), + .. + }) + | Node::ImplItem(&ImplItem { + node: ImplItemKind::Const(..), + .. + }) + | Node::AnonConst(_) + | Node::Item(&Item { + node: ItemKind::Static(..), + .. + }) => true, + Node::Item(&Item { + node: ItemKind::Fn(_, header, ..), + .. + }) => header.constness == Constness::Const, _ => false, - }, |_| false).map(|id| id != CRATE_HIR_ID).unwrap_or(false) + } } /// If there is some error when walking the parents (e.g., a node does not diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 3229d49841e79..bb499a97a2d2d 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_compatible_variants(&mut err, expr, expected, expr_ty); self.suggest_ref_or_into(&mut err, expr, expected, expr_ty); + self.suggest_boxing_when_appropriate(&mut err, expr, expected, expr_ty); self.suggest_missing_await(&mut err, expr, expected, expr_ty); (expected, Some(err)) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4fb28db6e94fa..5b708b69e2d7e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3820,6 +3820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err, &fn_decl, expected, found, can_suggest); } self.suggest_ref_or_into(err, expression, expected, found); + self.suggest_boxing_when_appropriate(err, expression, expected, found); pointing_at_return_type } @@ -3980,6 +3981,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// When encountering the expected boxed value allocated in the stack, suggest allocating it + /// in the heap by calling `Box::new()`. + fn suggest_boxing_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'tcx>, + expr: &hir::Expr, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if self.tcx.hir().is_const_scope(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return; + } + if expected.is_box() && !found.is_box() { + let boxed_found = self.tcx.mk_box(found); + if let (true, Ok(snippet)) = ( + self.can_coerce(boxed_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + err.span_suggestion( + expr.span, + "you can store this in the heap calling `Box::new`", + format!("Box::new({})", snippet), + Applicability::MachineApplicable, + ); + err.note("for more information about the distinction between the stack and the \ + heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html and \ + https://doc.rust-lang.org/std/boxed/index.html"); + } + } + } + + /// A common error is to forget to add a semicolon at the end of a block, e.g., /// /// ``` diff --git a/src/test/ui/suggestions/suggest-box.fixed b/src/test/ui/suggestions/suggest-box.fixed new file mode 100644 index 0000000000000..3de02cd0bd481 --- /dev/null +++ b/src/test/ui/suggestions/suggest-box.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + let _x: Box Result<(), ()>> = Box::new(|| { //~ ERROR mismatched types + Err(())?; + Ok(()) + }); +} diff --git a/src/test/ui/suggestions/suggest-box.rs b/src/test/ui/suggestions/suggest-box.rs new file mode 100644 index 0000000000000..e680a61db3b17 --- /dev/null +++ b/src/test/ui/suggestions/suggest-box.rs @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + let _x: Box Result<(), ()>> = || { //~ ERROR mismatched types + Err(())?; + Ok(()) + }; +} diff --git a/src/test/ui/suggestions/suggest-box.stderr b/src/test/ui/suggestions/suggest-box.stderr new file mode 100644 index 0000000000000..aacee2cda1341 --- /dev/null +++ b/src/test/ui/suggestions/suggest-box.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/suggest-box.rs:4:47 + | +LL | let _x: Box Result<(), ()>> = || { + | _______________________________________________^ +LL | | Err(())?; +LL | | Ok(()) +LL | | }; + | |_____^ expected struct `std::boxed::Box`, found closure + | + = note: expected type `std::boxed::Box std::result::Result<(), ()>>` + found type `[closure@$DIR/suggest-box.rs:4:47: 7:6]` + = note: for more information about the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html and https://doc.rust-lang.org/std/boxed/index.html +help: you can store this in the heap calling `Box::new` + | +LL | let _x: Box Result<(), ()>> = Box::new(|| { +LL | Err(())?; +LL | Ok(()) +LL | }); + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 939c1cb349f81a3ce488f5c17f195a5fcd84691c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Aug 2019 11:24:08 -0700 Subject: [PATCH 5/7] review comments --- src/librustc/hir/map/mod.rs | 4 +- .../infer/error_reporting/need_type_info.rs | 8 ++-- src/librustc_typeck/check/demand.rs | 2 +- src/librustc_typeck/check/mod.rs | 37 ++++++++++--------- .../ui/inference/cannot-infer-closure.stderr | 2 +- src/test/ui/suggestions/suggest-box.stderr | 4 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index b68cef2b6ea92..571ee393782f9 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -649,7 +649,9 @@ impl<'hir> Map<'hir> { } } - pub fn is_const_scope(&self, hir_id: HirId) -> bool { + /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. + /// Used exclusively for diagnostics, to avoid suggestion function calls. + pub fn is_const_context(&self, hir_id: HirId) -> bool { let parent_id = self.get_parent_item(hir_id); match self.get(parent_id) { Node::Item(&Item { diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 6d16580384ed7..b5d78a80bf674 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -153,7 +153,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let (ty_msg, suffix) = match &local_visitor.found_ty { Some(ty) if &ty.to_string() != "_" && name == "_" && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && !ty.is_closure() => // The suggestion doesn't make sense for closures. { @@ -163,7 +163,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } Some(ty) if &ty.to_string() != "_" && ty.to_string() != name && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && !ty.is_closure() => // The suggestion doesn't make sense for closures. { @@ -185,12 +185,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .map(|args| args.tuple_fields() .map(|arg| arg.to_string()) .collect::>().join(", ")) - .unwrap_or_else(String::new); + .unwrap_or_default(); // This suggestion is incomplete, as the user will get further type inference // errors due to the `_` placeholders and the introduction of `Box`, but it does // nudge them in the right direction. (msg, format!( - "a boxed closure type like `Box {}>`", + "a boxed closure type like `Box {}>`", args, fn_sig.output().skip_binder().to_string(), )) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index bb499a97a2d2d..ed25601208ad1 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -549,7 +549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool { - if self.tcx.hir().is_const_scope(expr.hir_id) { + if self.tcx.hir().is_const_context(expr.hir_id) { // Shouldn't suggest `.into()` on `const`s. // FIXME(estebank): modify once we decide to suggest `as` casts return false; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5b708b69e2d7e..8565cbd3708c9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3990,27 +3990,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) { - if self.tcx.hir().is_const_scope(expr.hir_id) { + if self.tcx.hir().is_const_context(expr.hir_id) { // Do not suggest `Box::new` in const context. return; } - if expected.is_box() && !found.is_box() { - let boxed_found = self.tcx.mk_box(found); - if let (true, Ok(snippet)) = ( - self.can_coerce(boxed_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - err.span_suggestion( - expr.span, - "you can store this in the heap calling `Box::new`", - format!("Box::new({})", snippet), - Applicability::MachineApplicable, - ); - err.note("for more information about the distinction between the stack and the \ - heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html and \ - https://doc.rust-lang.org/std/boxed/index.html"); - } + if !expected.is_box() || found.is_box() { + return; + } + let boxed_found = self.tcx.mk_box(found); + if let (true, Ok(snippet)) = ( + self.can_coerce(boxed_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + err.span_suggestion( + expr.span, + "store this in the heap by calling `Box::new`", + format!("Box::new({})", snippet), + Applicability::MachineApplicable, + ); + err.note("for more on the distinction between the stack and the \ + heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html, and \ + https://doc.rust-lang.org/std/boxed/index.html"); } } diff --git a/src/test/ui/inference/cannot-infer-closure.stderr b/src/test/ui/inference/cannot-infer-closure.stderr index 20b2784a45d84..fb78dedc887ff 100644 --- a/src/test/ui/inference/cannot-infer-closure.stderr +++ b/src/test/ui/inference/cannot-infer-closure.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for the closure --> $DIR/cannot-infer-closure.rs:3:9 | LL | let x = |a: (), b: ()| { - | - consider giving `x` a boxed closure type like `Box std::result::Result<(), _>>` + | - consider giving `x` a boxed closure type like `Box std::result::Result<(), _>>` LL | Err(a)?; | ^^^^^^^ cannot infer type diff --git a/src/test/ui/suggestions/suggest-box.stderr b/src/test/ui/suggestions/suggest-box.stderr index aacee2cda1341..50c106d63a02b 100644 --- a/src/test/ui/suggestions/suggest-box.stderr +++ b/src/test/ui/suggestions/suggest-box.stderr @@ -10,8 +10,8 @@ LL | | }; | = note: expected type `std::boxed::Box std::result::Result<(), ()>>` found type `[closure@$DIR/suggest-box.rs:4:47: 7:6]` - = note: for more information about the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html and https://doc.rust-lang.org/std/boxed/index.html -help: you can store this in the heap calling `Box::new` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` | LL | let _x: Box Result<(), ()>> = Box::new(|| { LL | Err(())?; From 806476c840a19ee2da4113ec3e68f9681018325e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Aug 2019 16:41:43 -0700 Subject: [PATCH 6/7] Suggest giving return type to closures on E0282 --- src/librustc/hir/mod.rs | 2 +- .../infer/error_reporting/need_type_info.rs | 211 ++++++++++++------ src/test/ui/error-codes/E0282.stderr | 5 +- ...for-loop-unconstrained-element-type.stderr | 5 +- .../ui/inference/cannot-infer-closure.stderr | 8 +- src/test/ui/issues/issue-18159.stderr | 5 +- .../ui/match/match-unresolved-one-arm.stderr | 5 +- .../suggest-closure-return-type-1.rs | 3 + .../suggest-closure-return-type-1.stderr | 13 ++ .../suggest-closure-return-type-2.rs | 3 + .../suggest-closure-return-type-2.stderr | 13 ++ .../suggest-closure-return-type-3.rs | 3 + .../suggest-closure-return-type-3.stderr | 13 ++ 13 files changed, 199 insertions(+), 90 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-1.rs create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-1.stderr create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-2.rs create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-2.stderr create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-3.rs create mode 100644 src/test/ui/suggestions/suggest-closure-return-type-3.stderr diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 3d049fe4ccdad..e8d44ee1310e7 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1541,7 +1541,7 @@ pub enum ExprKind { Match(P, HirVec, MatchSource), /// A closure (e.g., `move |a, b, c| {a + b + c}`). /// - /// The final span is the span of the argument block `|...|`. + /// The `Span` is the argument block `|...|`. /// /// This may also be a generator literal or an `async block` as indicated by the /// `Option`. diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index b5d78a80bf674..16fed3d42d679 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -1,5 +1,5 @@ use crate::hir::def::Namespace; -use crate::hir::{self, Local, Pat, Body, HirId}; +use crate::hir::{self, Body, FunctionRetTy, Expr, ExprKind, HirId, Local, Pat}; use crate::hir::intravisit::{self, Visitor, NestedVisitorMap}; use crate::infer::InferCtxt; use crate::infer::type_variable::TypeVariableOriginKind; @@ -7,7 +7,7 @@ use crate::ty::{self, Ty, Infer, TyVar}; use crate::ty::print::Print; use syntax::source_map::DesugaringKind; use syntax_pos::Span; -use errors::DiagnosticBuilder; +use errors::{Applicability, DiagnosticBuilder}; struct FindLocalByTypeVisitor<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, @@ -16,9 +16,26 @@ struct FindLocalByTypeVisitor<'a, 'tcx> { found_local_pattern: Option<&'tcx Pat>, found_arg_pattern: Option<&'tcx Pat>, found_ty: Option>, + found_closure: Option<&'tcx ExprKind>, } impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> { + fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + target_ty: Ty<'tcx>, + hir_map: &'a hir::map::Map<'tcx>, + ) -> FindLocalByTypeVisitor<'a, 'tcx> { + FindLocalByTypeVisitor { + infcx, + target_ty, + hir_map, + found_local_pattern: None, + found_arg_pattern: None, + found_ty: None, + found_closure: None, + } + } + fn node_matches_type(&mut self, hir_id: HirId) -> Option> { let ty_opt = self.infcx.in_progress_tables.and_then(|tables| { tables.borrow().node_type_opt(hir_id) @@ -72,6 +89,16 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> { } intravisit::walk_body(self, body); } + + fn visit_expr(&mut self, expr: &'tcx Expr) { + if let (ExprKind::Closure(_, _fn_decl, _id, _sp, _), Some(_)) = ( + &expr.node, + self.node_matches_type(expr.hir_id), + ) { + self.found_closure = Some(&expr.node); + } + intravisit::walk_expr(self, expr); + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -106,16 +133,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(&ty); let name = self.extract_type_name(&ty, None); - let mut err_span = span; - - let mut local_visitor = FindLocalByTypeVisitor { - infcx: &self, - target_ty: ty, - hir_map: &self.tcx.hir(), - found_local_pattern: None, - found_arg_pattern: None, - found_ty: None, - }; + let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, &self.tcx.hir()); let ty_to_string = |ty: Ty<'tcx>| -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); @@ -136,6 +154,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let expr = self.tcx.hir().expect_expr(body_id.hir_id); local_visitor.visit_expr(expr); } + let err_span = if let Some(pattern) = local_visitor.found_arg_pattern { + pattern.span + } else { + span + }; + + let ty_msg = match local_visitor.found_ty { + Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { + let fn_sig = substs.closure_sig(*def_id, self.tcx); + let args = fn_sig.inputs() + .skip_binder() + .iter() + .next() + .map(|args| args.tuple_fields() + .map(|arg| arg.to_string()) + .collect::>().join(", ")) + .unwrap_or_default(); + let ret = fn_sig.output().skip_binder().to_string(); + format!(" for the closure `fn({}) -> {}`", args, ret) + } + Some(ty) if &ty.to_string() != "_" && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => + { + let ty = ty_to_string(ty); + format!(" for `{}`", ty) + } + _ => String::new(), + }; // When `name` corresponds to a type argument, show the path of the full type we're // trying to infer. In the following example, `ty_msg` contains @@ -150,34 +197,47 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // | consider giving `b` the explicit type `std::result::Result`, where // | the type parameter `E` is specified // ``` - let (ty_msg, suffix) = match &local_visitor.found_ty { - Some(ty) if &ty.to_string() != "_" && - name == "_" && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 - (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && - !ty.is_closure() => // The suggestion doesn't make sense for closures. - { - let ty = ty_to_string(ty); - (format!(" for `{}`", ty), - format!("the explicit type `{}`, with the type parameters specified", ty)) - } - Some(ty) if &ty.to_string() != "_" && - ty.to_string() != name && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 - (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) && - !ty.is_closure() => // The suggestion doesn't make sense for closures. - { - let ty = ty_to_string(ty); - (format!(" for `{}`", ty), - format!( - "the explicit type `{}`, where the type parameter `{}` is specified", - ty, - name, - )) - } + let mut err = struct_span_err!( + self.tcx.sess, + err_span, + E0282, + "type annotations needed{}", + ty_msg, + ); + + let suffix = match local_visitor.found_ty { Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { - let msg = " for the closure".to_string(); let fn_sig = substs.closure_sig(*def_id, self.tcx); + let ret = fn_sig.output().skip_binder().to_string(); + + if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure { + let (arrow, post) = match decl.output { + FunctionRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) { + let suggestion = match body.value.node { + ExprKind::Block(..) => { + vec![(decl.output.span(), format!("{}{}{}", arrow, ret, post))] + } + _ => { + vec![ + (decl.output.span(), format!("{}{}{}{{ ", arrow, ret, post)), + (body.value.span.shrink_to_hi(), " }".to_string()), + ] + } + }; + err.multipart_suggestion( + "give this closure an explicit return type without `_` placeholders", + suggestion, + Applicability::HasPlaceholders, + ); + err.span_label(span, InferCtxt::missing_type_msg(&name)); + return err; + } + } + + // This shouldn't be reachable, but just in case we leave a reasonable fallback. let args = fn_sig.inputs() .skip_binder() .iter() @@ -189,18 +249,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // This suggestion is incomplete, as the user will get further type inference // errors due to the `_` placeholders and the introduction of `Box`, but it does // nudge them in the right direction. - (msg, format!( - "a boxed closure type like `Box {}>`", - args, - fn_sig.output().skip_binder().to_string(), - )) + format!("a boxed closure type like `Box {}>`", args, ret) + } + Some(ty) if &ty.to_string() != "_" && + name == "_" && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => + { + let ty = ty_to_string(ty); + format!("the explicit type `{}`, with the type parameters specified", ty) } - _ => (String::new(), "a type".to_owned()), + Some(ty) if &ty.to_string() != "_" && + ty.to_string() != name && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => + { + let ty = ty_to_string(ty); + format!( + "the explicit type `{}`, where the type parameter `{}` is specified", + ty, + name, + ) + } + _ => "a type".to_string(), }; - let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))]; if let Some(pattern) = local_visitor.found_arg_pattern { - err_span = pattern.span; // We don't want to show the default label for closures. // // So, before clearing, the output would look something like this: @@ -217,39 +291,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // ^ consider giving this closure parameter the type `[_; 0]` // with the type parameter `_` specified // ``` - labels.clear(); - labels.push(( + err.span_label( pattern.span, format!("consider giving this closure parameter {}", suffix), - )); + ); } else if let Some(pattern) = local_visitor.found_local_pattern { if let Some(simple_ident) = pattern.simple_ident() { match pattern.span.desugaring_kind() { - None => labels.push(( - pattern.span, - format!("consider giving `{}` {}", simple_ident, suffix), - )), - Some(DesugaringKind::ForLoop) => labels.push(( - pattern.span, - "the element type for this iterator is not specified".to_owned(), - )), + None => { + err.span_label( + pattern.span, + format!("consider giving `{}` {}", simple_ident, suffix), + ); + } + Some(DesugaringKind::ForLoop) => { + err.span_label( + pattern.span, + "the element type for this iterator is not specified".to_string(), + ); + } _ => {} } } else { - labels.push((pattern.span, format!("consider giving this pattern {}", suffix))); + err.span_label(pattern.span, format!("consider giving this pattern {}", suffix)); } - }; - - let mut err = struct_span_err!( - self.tcx.sess, - err_span, - E0282, - "type annotations needed{}", - ty_msg, - ); - - for (target_span, label_message) in labels { - err.span_label(target_span, label_message); + } + if !err.span.span_labels().iter().any(|span_label| { + span_label.label.is_some() && span_label.span == span + }) && local_visitor.found_arg_pattern.is_none() + { // Avoid multiple labels pointing at `span`. + err.span_label(span, InferCtxt::missing_type_msg(&name)); } err diff --git a/src/test/ui/error-codes/E0282.stderr b/src/test/ui/error-codes/E0282.stderr index 3a5040eb6daa6..0f610a5e42f65 100644 --- a/src/test/ui/error-codes/E0282.stderr +++ b/src/test/ui/error-codes/E0282.stderr @@ -2,10 +2,7 @@ error[E0282]: type annotations needed --> $DIR/E0282.rs:2:9 | LL | let x = "hello".chars().rev().collect(); - | ^ - | | - | cannot infer type - | consider giving `x` a type + | ^ consider giving `x` a type error: aborting due to previous error diff --git a/src/test/ui/for/for-loop-unconstrained-element-type.stderr b/src/test/ui/for/for-loop-unconstrained-element-type.stderr index 02fdb808da449..0672014a92929 100644 --- a/src/test/ui/for/for-loop-unconstrained-element-type.stderr +++ b/src/test/ui/for/for-loop-unconstrained-element-type.stderr @@ -2,10 +2,7 @@ error[E0282]: type annotations needed --> $DIR/for-loop-unconstrained-element-type.rs:8:14 | LL | for i in Vec::new() { } - | ^^^^^^^^^^ - | | - | cannot infer type - | the element type for this iterator is not specified + | ^^^^^^^^^^ the element type for this iterator is not specified error: aborting due to previous error diff --git a/src/test/ui/inference/cannot-infer-closure.stderr b/src/test/ui/inference/cannot-infer-closure.stderr index fb78dedc887ff..5f30b5d993c1e 100644 --- a/src/test/ui/inference/cannot-infer-closure.stderr +++ b/src/test/ui/inference/cannot-infer-closure.stderr @@ -1,10 +1,12 @@ -error[E0282]: type annotations needed for the closure +error[E0282]: type annotations needed for the closure `fn((), ()) -> std::result::Result<(), _>` --> $DIR/cannot-infer-closure.rs:3:9 | -LL | let x = |a: (), b: ()| { - | - consider giving `x` a boxed closure type like `Box std::result::Result<(), _>>` LL | Err(a)?; | ^^^^^^^ cannot infer type +help: give this closure an explicit return type without `_` placeholders + | +LL | let x = |a: (), b: ()| -> std::result::Result<(), _> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-18159.stderr b/src/test/ui/issues/issue-18159.stderr index 6048344125c5f..9b890be3c789b 100644 --- a/src/test/ui/issues/issue-18159.stderr +++ b/src/test/ui/issues/issue-18159.stderr @@ -2,10 +2,7 @@ error[E0282]: type annotations needed --> $DIR/issue-18159.rs:2:9 | LL | let x; - | ^ - | | - | cannot infer type - | consider giving `x` a type + | ^ consider giving `x` a type error: aborting due to previous error diff --git a/src/test/ui/match/match-unresolved-one-arm.stderr b/src/test/ui/match/match-unresolved-one-arm.stderr index ad8569b4802fb..77df9921b754a 100644 --- a/src/test/ui/match/match-unresolved-one-arm.stderr +++ b/src/test/ui/match/match-unresolved-one-arm.stderr @@ -2,10 +2,7 @@ error[E0282]: type annotations needed --> $DIR/match-unresolved-one-arm.rs:4:9 | LL | let x = match () { - | ^ - | | - | cannot infer type - | consider giving `x` a type + | ^ consider giving `x` a type error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-closure-return-type-1.rs b/src/test/ui/suggestions/suggest-closure-return-type-1.rs new file mode 100644 index 0000000000000..910f273b9722d --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-1.rs @@ -0,0 +1,3 @@ +fn main() { + let _v = || -> _ { [] }; //~ ERROR type annotations needed for the closure +} diff --git a/src/test/ui/suggestions/suggest-closure-return-type-1.stderr b/src/test/ui/suggestions/suggest-closure-return-type-1.stderr new file mode 100644 index 0000000000000..de2d29f1270c6 --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-1.stderr @@ -0,0 +1,13 @@ +error[E0282]: type annotations needed for the closure `fn() -> [_; 0]` + --> $DIR/suggest-closure-return-type-1.rs:2:24 + | +LL | let _v = || -> _ { [] }; + | ^^ cannot infer type +help: give this closure an explicit return type without `_` placeholders + | +LL | let _v = || -> [_; 0] { [] }; + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/suggestions/suggest-closure-return-type-2.rs b/src/test/ui/suggestions/suggest-closure-return-type-2.rs new file mode 100644 index 0000000000000..6955b37ad97e4 --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-2.rs @@ -0,0 +1,3 @@ +fn main() { + let _v = || { [] }; //~ ERROR type annotations needed for the closure +} diff --git a/src/test/ui/suggestions/suggest-closure-return-type-2.stderr b/src/test/ui/suggestions/suggest-closure-return-type-2.stderr new file mode 100644 index 0000000000000..9dbd822fbb5de --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-2.stderr @@ -0,0 +1,13 @@ +error[E0282]: type annotations needed for the closure `fn() -> [_; 0]` + --> $DIR/suggest-closure-return-type-2.rs:2:19 + | +LL | let _v = || { [] }; + | ^^ cannot infer type +help: give this closure an explicit return type without `_` placeholders + | +LL | let _v = || -> [_; 0] { [] }; + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/suggestions/suggest-closure-return-type-3.rs b/src/test/ui/suggestions/suggest-closure-return-type-3.rs new file mode 100644 index 0000000000000..ec6c094027e1e --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-3.rs @@ -0,0 +1,3 @@ +fn main() { + let _v = || []; //~ ERROR type annotations needed for the closure +} diff --git a/src/test/ui/suggestions/suggest-closure-return-type-3.stderr b/src/test/ui/suggestions/suggest-closure-return-type-3.stderr new file mode 100644 index 0000000000000..ad0d4e41f7874 --- /dev/null +++ b/src/test/ui/suggestions/suggest-closure-return-type-3.stderr @@ -0,0 +1,13 @@ +error[E0282]: type annotations needed for the closure `fn() -> [_; 0]` + --> $DIR/suggest-closure-return-type-3.rs:2:17 + | +LL | let _v = || []; + | ^^ cannot infer type +help: give this closure an explicit return type without `_` placeholders + | +LL | let _v = || -> [_; 0] { [] }; + | ^^^^^^^^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From 6c3a98e029048ff316d6c8ae2f69b3db08cae90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Aug 2019 12:14:25 -0700 Subject: [PATCH 7/7] review comments --- .../infer/error_reporting/need_type_info.rs | 137 ++++++++++-------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 16fed3d42d679..3267505708b81 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -24,8 +24,8 @@ impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, target_ty: Ty<'tcx>, hir_map: &'a hir::map::Map<'tcx>, - ) -> FindLocalByTypeVisitor<'a, 'tcx> { - FindLocalByTypeVisitor { + ) -> Self { + Self { infcx, target_ty, hir_map, @@ -101,6 +101,50 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> { } } +/// Suggest giving an appropriate return type to a closure expression. +fn closure_return_type_suggestion( + span: Span, + err: &mut DiagnosticBuilder<'_>, + output: &FunctionRetTy, + body: &Body, + name: &str, + ret: &str, +) { + let (arrow, post) = match output { + FunctionRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + let suggestion = match body.value.node { + ExprKind::Block(..) => { + vec![(output.span(), format!("{}{}{}", arrow, ret, post))] + } + _ => { + vec![ + (output.span(), format!("{}{}{}{{ ", arrow, ret, post)), + (body.value.span.shrink_to_hi(), " }".to_string()), + ] + } + }; + err.multipart_suggestion( + "give this closure an explicit return type without `_` placeholders", + suggestion, + Applicability::HasPlaceholders, + ); + err.span_label(span, InferCtxt::missing_type_msg(&name)); +} + +/// Given a closure signature, return a `String` containing a list of all its argument types. +fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String { + fn_sig.inputs() + .skip_binder() + .iter() + .next() + .map(|args| args.tuple_fields() + .map(|arg| arg.to_string()) + .collect::>().join(", ")) + .unwrap_or_default() +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn extract_type_name( &self, @@ -160,24 +204,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span }; + let is_named_and_not_impl_trait = |ty: Ty<'_>| { + &ty.to_string() != "_" && + // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 + (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) + }; + let ty_msg = match local_visitor.found_ty { Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { let fn_sig = substs.closure_sig(*def_id, self.tcx); - let args = fn_sig.inputs() - .skip_binder() - .iter() - .next() - .map(|args| args.tuple_fields() - .map(|arg| arg.to_string()) - .collect::>().join(", ")) - .unwrap_or_default(); + let args = closure_args(&fn_sig); let ret = fn_sig.output().skip_binder().to_string(); format!(" for the closure `fn({}) -> {}`", args, ret) } - Some(ty) if &ty.to_string() != "_" && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 - (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => - { + Some(ty) if is_named_and_not_impl_trait(ty) => { let ty = ty_to_string(ty); format!(" for `{}`", ty) } @@ -211,59 +251,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ret = fn_sig.output().skip_binder().to_string(); if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure { - let (arrow, post) = match decl.output { - FunctionRetTy::DefaultReturn(_) => ("-> ", " "), - _ => ("", ""), - }; if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) { - let suggestion = match body.value.node { - ExprKind::Block(..) => { - vec![(decl.output.span(), format!("{}{}{}", arrow, ret, post))] - } - _ => { - vec![ - (decl.output.span(), format!("{}{}{}{{ ", arrow, ret, post)), - (body.value.span.shrink_to_hi(), " }".to_string()), - ] - } - }; - err.multipart_suggestion( - "give this closure an explicit return type without `_` placeholders", - suggestion, - Applicability::HasPlaceholders, + closure_return_type_suggestion( + span, + &mut err, + &decl.output, + &body, + &name, + &ret, ); - err.span_label(span, InferCtxt::missing_type_msg(&name)); + // We don't want to give the other suggestions when the problem is the + // closure return type. return err; } } // This shouldn't be reachable, but just in case we leave a reasonable fallback. - let args = fn_sig.inputs() - .skip_binder() - .iter() - .next() - .map(|args| args.tuple_fields() - .map(|arg| arg.to_string()) - .collect::>().join(", ")) - .unwrap_or_default(); + let args = closure_args(&fn_sig); // This suggestion is incomplete, as the user will get further type inference // errors due to the `_` placeholders and the introduction of `Box`, but it does // nudge them in the right direction. format!("a boxed closure type like `Box {}>`", args, ret) } - Some(ty) if &ty.to_string() != "_" && - name == "_" && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 - (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => - { + Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => { let ty = ty_to_string(ty); format!("the explicit type `{}`, with the type parameters specified", ty) } - Some(ty) if &ty.to_string() != "_" && - ty.to_string() != name && - // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527 - (!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) => - { + Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => { let ty = ty_to_string(ty); format!( "the explicit type `{}`, where the type parameter `{}` is specified", @@ -296,25 +310,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { format!("consider giving this closure parameter {}", suffix), ); } else if let Some(pattern) = local_visitor.found_local_pattern { - if let Some(simple_ident) = pattern.simple_ident() { + let msg = if let Some(simple_ident) = pattern.simple_ident() { match pattern.span.desugaring_kind() { None => { - err.span_label( - pattern.span, - format!("consider giving `{}` {}", simple_ident, suffix), - ); + format!("consider giving `{}` {}", simple_ident, suffix) } Some(DesugaringKind::ForLoop) => { - err.span_label( - pattern.span, - "the element type for this iterator is not specified".to_string(), - ); + "the element type for this iterator is not specified".to_string() } - _ => {} + _ => format!("this needs {}", suffix), } } else { - err.span_label(pattern.span, format!("consider giving this pattern {}", suffix)); - } + format!("consider giving this pattern {}", suffix) + }; + err.span_label(pattern.span, msg); } if !err.span.span_labels().iter().any(|span_label| { span_label.label.is_some() && span_label.span == span