about summary refs log tree commit diff
path: root/src/librustc
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/librustc
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/librustc')
-rw-r--r--src/librustc/traits/error_reporting.rs68
1 files changed, 61 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(