about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-08-14 22:56:22 +0200
committerGitHub <noreply@github.com>2019-08-14 22:56:22 +0200
commitd2d49d238f66ad3e9aea12d2cdac8f4bc2af4ce9 (patch)
tree971766d824661216b8465284eb565e49621220da
parentaaeff01446fed6fb3bd15d638022a85174350772 (diff)
parent6c3a98e029048ff316d6c8ae2f69b3db08cae90e (diff)
downloadrust-d2d49d238f66ad3e9aea12d2cdac8f4bc2af4ce9.tar.gz
rust-d2d49d238f66ad3e9aea12d2cdac8f4bc2af4ce9.zip
Rollup merge of #63507 - estebank:type-inference-error, r=Centril
When needing type annotations in local bindings, account for impl Trait and closures

Fix https://github.com/rust-lang/rust/issues/46680, fix https://github.com/rust-lang/rust/issues/63504, fix https://github.com/rust-lang/rust/issues/63506, fix #40014, cc https://github.com/rust-lang/rust/issues/63502.
-rw-r--r--src/librustc/hir/map/mod.rs32
-rw-r--r--src/librustc/hir/mod.rs2
-rw-r--r--src/librustc/infer/error_reporting/need_type_info.rs212
-rw-r--r--src/librustc/ty/sty.rs3
-rw-r--r--src/librustc_typeck/check/demand.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs36
-rw-r--r--src/test/ui/error-codes/E0282.stderr5
-rw-r--r--src/test/ui/for/for-loop-unconstrained-element-type.stderr5
-rw-r--r--src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs18
-rw-r--r--src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr19
-rw-r--r--src/test/ui/inference/cannot-infer-async.rs16
-rw-r--r--src/test/ui/inference/cannot-infer-async.stderr11
-rw-r--r--src/test/ui/inference/cannot-infer-closure.rs6
-rw-r--r--src/test/ui/inference/cannot-infer-closure.stderr13
-rw-r--r--src/test/ui/issues/issue-18159.stderr5
-rw-r--r--src/test/ui/match/match-unresolved-one-arm.stderr5
-rw-r--r--src/test/ui/suggestions/suggest-box.fixed8
-rw-r--r--src/test/ui/suggestions/suggest-box.rs8
-rw-r--r--src/test/ui/suggestions/suggest-box.stderr24
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-1.rs3
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-1.stderr13
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-2.rs3
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-2.stderr13
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-3.rs3
-rw-r--r--src/test/ui/suggestions/suggest-closure-return-type-3.stderr13
25 files changed, 405 insertions, 74 deletions
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index b85738dd29a..571ee393782 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -649,12 +649,34 @@ 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(),
+    /// 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 {
+                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/hir/mod.rs b/src/librustc/hir/mod.rs
index 3d049fe4ccd..e8d44ee1310 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1541,7 +1541,7 @@ pub enum ExprKind {
     Match(P<Expr>, HirVec<Arm>, 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<GeneratorMovability>`.
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs
index 770d5155777..3267505708b 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<Ty<'tcx>>,
+    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>,
+    ) -> Self {
+        Self {
+            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<Ty<'tcx>> {
         let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
             tables.borrow().node_type_opt(hir_id)
@@ -72,6 +89,60 @@ 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);
+    }
+}
+
+/// 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::<Vec<_>>().join(", "))
+        .unwrap_or_default()
 }
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -106,16 +177,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 +198,31 @@ 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 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 = closure_args(&fn_sig);
+                let ret = fn_sig.output().skip_binder().to_string();
+                format!(" for the closure `fn({}) -> {}`", args, ret)
+            }
+            Some(ty) if is_named_and_not_impl_trait(ty) => {
+                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,27 +237,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         //   |         consider giving `b` the explicit type `std::result::Result<i32, E>`, where
         //   |         the type parameter `E` is specified
         // ```
-        let (ty_msg, suffix) = match &local_visitor.found_ty {
-            Some(ty) if &ty.to_string() != "_" && 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 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 {
+                    if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) {
+                        closure_return_type_suggestion(
+                            span,
+                            &mut err,
+                            &decl.output,
+                            &body,
+                            &name,
+                            &ret,
+                        );
+                        // 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 = 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<dyn Fn({}) -> {}>`", args, ret)
+            }
+            Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => {
                 let ty = ty_to_string(ty);
-                (format!(" for `{}`", ty),
-                 format!("the explicit type `{}`, with the type parameters specified", ty))
+                format!("the explicit type `{}`, with the type parameters specified", ty)
             }
-            Some(ty) if &ty.to_string() != "_" && ty.to_string() != name => {
+            Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => {
                 let ty = ty_to_string(ty);
-                (format!(" for `{}`", ty),
-                 format!(
-                     "the explicit type `{}`, where the type parameter `{}` is specified",
+                format!(
+                    "the explicit type `{}`, where the type parameter `{}` is specified",
                     ty,
                     name,
-                 ))
+                )
             }
-            _ => (String::new(), "a type".to_owned()),
+            _ => "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:
@@ -187,39 +305,31 @@ 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() {
+            let msg = 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 => {
+                        format!("consider giving `{}` {}", simple_ident, suffix)
+                    }
+                    Some(DesugaringKind::ForLoop) => {
+                        "the element type for this iterator is not specified".to_string()
+                    }
+                    _ => format!("this needs {}", suffix),
                 }
             } else {
-                labels.push((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);
+                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
+            }) && 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/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 129ea9b5b67..02593d4d609 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/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 3229d49841e..ed25601208a 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))
@@ -548,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 14fc0d6347e..74d3a12765b 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3902,6 +3902,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
     }
 
@@ -4062,6 +4063,41 @@ 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_context(expr.hir_id) {
+            // Do not suggest `Box::new` in const context.
+            return;
+        }
+        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");
+        }
+    }
+
+
     /// A common error is to forget to add a semicolon at the end of a block, e.g.,
     ///
     /// ```
diff --git a/src/test/ui/error-codes/E0282.stderr b/src/test/ui/error-codes/E0282.stderr
index 3a5040eb6da..0f610a5e42f 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 02fdb808da4..0672014a929 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-async-enabled-impl-trait-bindings.rs b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.rs
new file mode 100644
index 00000000000..30ed5050433
--- /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 00000000000..67a834a2e95
--- /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 00000000000..edc64276e7c
--- /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 00000000000..36608a11bb7
--- /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 00000000000..8f48483c254
--- /dev/null
+++ b/src/test/ui/inference/cannot-infer-closure.rs
@@ -0,0 +1,6 @@
+fn main() {
+    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
new file mode 100644
index 00000000000..5f30b5d993c
--- /dev/null
+++ b/src/test/ui/inference/cannot-infer-closure.stderr
@@ -0,0 +1,13 @@
+error[E0282]: type annotations needed for the closure `fn((), ()) -> std::result::Result<(), _>`
+  --> $DIR/cannot-infer-closure.rs:3:9
+   |
+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
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/issues/issue-18159.stderr b/src/test/ui/issues/issue-18159.stderr
index 6048344125c..9b890be3c78 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 ad8569b4802..77df9921b75 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-box.fixed b/src/test/ui/suggestions/suggest-box.fixed
new file mode 100644
index 00000000000..3de02cd0bd4
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-box.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+
+fn main() {
+    let _x: Box<dyn Fn() -> 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 00000000000..e680a61db3b
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-box.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+
+fn main() {
+    let _x: Box<dyn Fn() -> 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 00000000000..50c106d63a0
--- /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<dyn Fn() -> Result<(), ()>> = || {
+   |  _______________________________________________^
+LL | |         Err(())?;
+LL | |         Ok(())
+LL | |     };
+   | |_____^ expected struct `std::boxed::Box`, found closure
+   |
+   = note: expected type `std::boxed::Box<dyn std::ops::Fn() -> std::result::Result<(), ()>>`
+              found type `[closure@$DIR/suggest-box.rs:4:47: 7:6]`
+   = 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<dyn Fn() -> Result<(), ()>> = Box::new(|| {
+LL |         Err(())?;
+LL |         Ok(())
+LL |     });
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
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 00000000000..910f273b972
--- /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 00000000000..de2d29f1270
--- /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 00000000000..6955b37ad97
--- /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 00000000000..9dbd822fbb5
--- /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 00000000000..ec6c094027e
--- /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 00000000000..ad0d4e41f78
--- /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`.