about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-08-24 14:45:03 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-08-31 00:14:23 -0700
commit5384d5584f6fd596df3a86e2cd4e0281b27d10db (patch)
treee1cdd6075003de703683c8afa69cb5454cd9371a /src
parent444bc3ca6607f7bdeb088b34db23c01e056900b1 (diff)
downloadrust-5384d5584f6fd596df3a86e2cd4e0281b27d10db.tar.gz
rust-5384d5584f6fd596df3a86e2cd4e0281b27d10db.zip
Suggest call fn ctor passed as arg to fn with type param bounds
Diffstat (limited to 'src')
-rw-r--r--src/librustc/traits/error_reporting.rs68
-rw-r--r--src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs10
-rw-r--r--src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr14
-rw-r--r--src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs18
-rw-r--r--src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr14
5 files changed, 117 insertions, 7 deletions
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index aa0fcafbb0e..07083f155d6 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -1,20 +1,21 @@
 use super::{
+    ConstEvalFailure,
+    EvaluationResult,
     FulfillmentError,
     FulfillmentErrorCode,
     MismatchedProjectionTypes,
+    ObjectSafetyViolation,
     Obligation,
     ObligationCause,
     ObligationCauseCode,
     OnUnimplementedDirective,
     OnUnimplementedNote,
     OutputTypeParameterMismatch,
-    TraitNotObjectSafe,
-    ConstEvalFailure,
+    Overflow,
     PredicateObligation,
     SelectionContext,
     SelectionError,
-    ObjectSafetyViolation,
-    Overflow,
+    TraitNotObjectSafe,
 };
 
 use crate::hir;
@@ -35,7 +36,7 @@ use crate::util::nodemap::{FxHashMap, FxHashSet};
 use errors::{Applicability, DiagnosticBuilder};
 use std::fmt;
 use syntax::ast;
-use syntax::symbol::sym;
+use syntax::symbol::{sym, kw};
 use syntax_pos::{DUMMY_SP, Span, ExpnKind};
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -669,8 +670,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             } else {
                                 format!(
                                     "{}the trait `{}` is not implemented for `{}`",
-                                        pre_message,
-                                        trait_ref,
+                                    pre_message,
+                                    trait_ref,
                                     trait_ref.self_ty(),
                                 )
                             };
@@ -689,6 +690,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         }
 
                         self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
+                        self.suggest_fn_call(&obligation, &mut err, &trait_ref);
                         self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
                         self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
 
@@ -956,6 +958,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_fn_call(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut DiagnosticBuilder<'tcx>,
+        trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
+    ) {
+        let self_ty = trait_ref.self_ty();
+        match self_ty.sty {
+            ty::FnDef(def_id, _) => {
+                // We tried to apply the bound to an `fn`. Check wether 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* to 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,
+                            node: hir::ItemKind::Fn(.., body_id),
+                            ..
+                        })) = self.tcx.hir().get_if_local(def_id) {
+                            let body = self.tcx.hir().body(*body_id);
+                            err.help(&format!(
+                                "it looks like you forgot to use parentheses to \
+                                 call the function: `{}({})`",
+                                ident,
+                                body.arguments.iter()
+                                    .map(|arg| match &arg.pat.node {
+                                        hir::PatKind::Binding(_, _, ident, None)
+                                        if ident.name != kw::SelfLower => ident.to_string(),
+                                        _ => "_".to_string(),
+                                    }).collect::<Vec<_>>().join(", ")));
+                        }
+                    }
+                    _ => {}
+                }
+            }
+            _ => {}
+        }
+    }
+
     /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
     /// suggest removing these references until we reach a type that implements the trait.
     fn suggest_remove_reference(
diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs
new file mode 100644
index 00000000000..a2d2ba145bc
--- /dev/null
+++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs
@@ -0,0 +1,10 @@
+// edition:2018
+use std::future::Future;
+
+async fn foo() {}
+
+fn bar(f: impl Future<Output=()>) {}
+
+fn main() {
+    bar(foo); //~ERROR E0277
+}
diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
new file mode 100644
index 00000000000..5735f725dc3
--- /dev/null
+++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
@@ -0,0 +1,14 @@
+error[E0277]: the trait bound `fn() -> impl std::future::Future {foo}: std::future::Future` is not satisfied
+  --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:9:5
+   |
+LL | fn bar(f: impl Future<Output=()>) {}
+   | --------------------------------- required by `bar`
+...
+LL |     bar(foo);
+   |     ^^^ the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}`
+   |
+   = help: it looks like you forgot to use parentheses to call the function: `foo()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs
new file mode 100644
index 00000000000..acd149c5854
--- /dev/null
+++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs
@@ -0,0 +1,18 @@
+// edition:2018
+trait T {
+    type O;
+}
+
+struct S;
+
+impl T for S {
+    type O = ();
+}
+
+fn foo() -> impl T<O=()> { S }
+
+fn bar(f: impl T<O=()>) {}
+
+fn main() {
+    bar(foo); //~ERROR E0277
+}
diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
new file mode 100644
index 00000000000..2e4505c7405
--- /dev/null
+++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr
@@ -0,0 +1,14 @@
+error[E0277]: the trait bound `fn() -> impl T {foo}: T` is not satisfied
+  --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:17:5
+   |
+LL | fn bar(f: impl T<O=()>) {}
+   | ----------------------- required by `bar`
+...
+LL |     bar(foo);
+   |     ^^^ the trait `T` is not implemented for `fn() -> impl T {foo}`
+   |
+   = help: it looks like you forgot to use parentheses to call the function: `foo()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.