From 5384d5584f6fd596df3a86e2cd4e0281b27d10db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Aug 2019 14:45:03 -0700 Subject: [PATCH] Suggest call fn ctor passed as arg to fn with type param bounds --- src/librustc/traits/error_reporting.rs | 68 +++++++++++++++++-- ...as-arg-where-it-should-have-been-called.rs | 10 +++ ...rg-where-it-should-have-been-called.stderr | 14 ++++ ...as-arg-where-it-should-have-been-called.rs | 18 +++++ ...rg-where-it-should-have-been-called.stderr | 14 ++++ 5 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs create mode 100644 src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr create mode 100644 src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs create mode 100644 src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index aa0fcafbb0e6..07083f155d62 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>, + ) { + 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::>().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 000000000000..a2d2ba145bc5 --- /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) {} + +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 000000000000..5735f725dc38 --- /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) {} + | --------------------------------- 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 000000000000..acd149c5854e --- /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 { S } + +fn bar(f: impl T) {} + +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 000000000000..2e4505c74058 --- /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) {} + | ----------------------- 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`.