]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/traits/error_reporting.rs
Suggest calling async closure when needed
[rust.git] / src / librustc / traits / error_reporting.rs
index f77db9621351ef6341399361d944bcd70b85f1fd..4a51ce2b780df94f253c3108016e7fa85172ca19 100644 (file)
@@ -39,6 +39,8 @@
 use syntax::symbol::{sym, kw};
 use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan};
 
+use rustc_error_codes::*;
+
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn report_fulfillment_errors(
         &self,
@@ -1236,60 +1238,109 @@ fn suggest_fn_call(
         points_at_arg: bool,
     ) {
         let self_ty = trait_ref.self_ty();
-        match self_ty.kind {
+        let (def_id, output_ty, callable) = match self_ty.kind {
+            ty::Closure(def_id, substs) => {
+                (def_id, self.closure_sig(def_id, substs).output(), "closure")
+            }
             ty::FnDef(def_id, _) => {
-                // We tried to apply the bound to an `fn`. Check whether calling it would evaluate
-                // to a type that *would* satisfy the trait binding. If it would, suggest calling
-                // it: `bar(foo)` -> `bar(foo)`. This case is *very* likely to be hit if `foo` is
-                // `async`.
-                let output_ty = self_ty.fn_sig(self.tcx).output();
-                let new_trait_ref = ty::TraitRef {
-                    def_id: trait_ref.def_id(),
-                    substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]),
-                };
-                let obligation = Obligation::new(
-                    obligation.cause.clone(),
-                    obligation.param_env,
-                    new_trait_ref.to_predicate(),
-                );
-                match self.evaluate_obligation(&obligation) {
-                    Ok(EvaluationResult::EvaluatedToOk) |
-                    Ok(EvaluationResult::EvaluatedToOkModuloRegions) |
-                    Ok(EvaluationResult::EvaluatedToAmbig) => {
-                        if let Some(hir::Node::Item(hir::Item {
-                            ident,
-                            kind: hir::ItemKind::Fn(.., body_id),
-                            ..
-                        })) = self.tcx.hir().get_if_local(def_id) {
-                            let body = self.tcx.hir().body(*body_id);
-                            let msg = "use parentheses to call the function";
-                            let snippet = format!(
-                                "{}({})",
-                                ident,
-                                body.params.iter()
-                                    .map(|arg| match &arg.pat.kind {
-                                        hir::PatKind::Binding(_, _, ident, None)
-                                        if ident.name != kw::SelfLower => ident.to_string(),
-                                        _ => "_".to_string(),
-                                    }).collect::<Vec<_>>().join(", "),
-                            );
-                            // When the obligation error has been ensured to have been caused by
-                            // an argument, the `obligation.cause.span` points at the expression
-                            // of the argument, so we can provide a suggestion. This is signaled
-                            // by `points_at_arg`. Otherwise, we give a more general note.
-                            if points_at_arg {
-                                err.span_suggestion(
-                                    obligation.cause.span,
-                                    msg,
-                                    snippet,
-                                    Applicability::HasPlaceholders,
-                                );
-                            } else {
-                                err.help(&format!("{}: `{}`", msg, snippet));
-                            }
-                        }
+                (def_id, self_ty.fn_sig(self.tcx).output(), "function")
+            }
+            _ => return,
+        };
+        let msg = format!("use parentheses to call the {}", callable);
+        // We tried to apply the bound to an `fn` or closure. Check whether calling it would
+        // evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling
+        // it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.
+
+        let new_trait_ref = ty::TraitRef {
+            def_id: trait_ref.def_id(),
+            substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]),
+        };
+        let obligation = Obligation::new(
+            obligation.cause.clone(),
+            obligation.param_env,
+            new_trait_ref.to_predicate(),
+        );
+        let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option<String> {
+            // Get the local name of this closure. This can be inaccurate because
+            // of the possibility of reassignment, but this should be good enough.
+            match &kind {
+                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
+                    Some(format!("{}", name))
+                }
+                _ => {
+                    err.note(&msg);
+                    None
+                }
+            }
+        };
+        match self.evaluate_obligation(&obligation) {
+            Ok(EvaluationResult::EvaluatedToOk) |
+            Ok(EvaluationResult::EvaluatedToOkModuloRegions) |
+            Ok(EvaluationResult::EvaluatedToAmbig) => {
+                let hir = self.tcx.hir();
+                // Get the name of the callable and the arguments to be used in the suggestion.
+                let snippet = match hir.get_if_local(def_id) {
+                    Some(hir::Node::Expr(hir::Expr {
+                        kind: hir::ExprKind::Closure(_, decl, _, span, ..),
+                        ..
+                    })) => {
+                        err.span_label(*span, "consider calling this closure");
+                        let hir_id = match hir.as_local_hir_id(def_id) {
+                            Some(hir_id) => hir_id,
+                            None => return,
+                        };
+                        let parent_node = hir.get_parent_node(hir_id);
+                        let name = match hir.find(parent_node) {
+                            Some(hir::Node::Stmt(hir::Stmt {
+                                kind: hir::StmtKind::Local(local), ..
+                            })) => match get_name(err, &local.pat.kind) {
+                                Some(name) => name,
+                                None => return,
+                            },
+                            // Different to previous arm because one is `&hir::Local` and the other
+                            // is `P<hir::Local>`.
+                            Some(hir::Node::Local(local)) => match get_name(err, &local.pat.kind) {
+                                Some(name) => name,
+                                None => return,
+                            },
+                            _ => return,
+                        };
+                        let args = decl.inputs.iter()
+                            .map(|_| "_")
+                            .collect::<Vec<_>>().join(", ");
+                        format!("{}({})", name, args)
+                    }
+                    Some(hir::Node::Item(hir::Item {
+                        ident,
+                        kind: hir::ItemKind::Fn(.., body_id),
+                        ..
+                    })) => {
+                        err.span_label(ident.span, "consider calling this function");
+                        let body = hir.body(*body_id);
+                        let args = body.params.iter()
+                            .map(|arg| match &arg.pat.kind {
+                                hir::PatKind::Binding(_, _, ident, None)
+                                if ident.name != kw::SelfLower => ident.to_string(),
+                                _ => "_".to_string(),
+                            }).collect::<Vec<_>>().join(", ");
+                        format!("{}({})", ident, args)
                     }
-                    _ => {}
+                    _ => return,
+                };
+                if points_at_arg {
+                    // When the obligation error has been ensured to have been caused by
+                    // an argument, the `obligation.cause.span` points at the expression
+                    // of the argument, so we can provide a suggestion. This is signaled
+                    // by `points_at_arg`. Otherwise, we give a more general note.
+                    err.span_suggestion(
+                        obligation.cause.span,
+                        &msg,
+                        snippet,
+                        Applicability::HasPlaceholders,
+                    );
+                } else {
+                    err.help(&format!("{}: `{}`", msg, snippet));
                 }
             }
             _ => {}
@@ -2174,15 +2225,15 @@ fn note_obligation_cause_code<T>(&self,
                 err.note(&format!("required by cast to type `{}`",
                                   self.ty_to_string(target)));
             }
-            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expression) => {
+            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => {
                 err.note("the `Copy` trait is required because the \
                           repeated element will be copied");
-                if suggest_const_in_array_repeat_expression {
+                if suggest_const_in_array_repeat_expressions {
                     err.note("this array initializer can be evaluated at compile-time, for more \
                               information, see issue \
                               https://github.com/rust-lang/rust/issues/49147");
                     if tcx.sess.opts.unstable_features.is_nightly_build() {
-                        err.help("add `#![feature(const_in_array_repeat_expression)]` to the \
+                        err.help("add `#![feature(const_in_array_repeat_expressions)]` to the \
                                   crate attributes to enable");
                     }
                 }