Skip to content

Commit 5384d55

Browse files
committed
Suggest call fn ctor passed as arg to fn with type param bounds
1 parent 444bc3c commit 5384d55

5 files changed

+117
-7
lines changed

src/librustc/traits/error_reporting.rs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
use super::{
2+
ConstEvalFailure,
3+
EvaluationResult,
24
FulfillmentError,
35
FulfillmentErrorCode,
46
MismatchedProjectionTypes,
7+
ObjectSafetyViolation,
58
Obligation,
69
ObligationCause,
710
ObligationCauseCode,
811
OnUnimplementedDirective,
912
OnUnimplementedNote,
1013
OutputTypeParameterMismatch,
11-
TraitNotObjectSafe,
12-
ConstEvalFailure,
14+
Overflow,
1315
PredicateObligation,
1416
SelectionContext,
1517
SelectionError,
16-
ObjectSafetyViolation,
17-
Overflow,
18+
TraitNotObjectSafe,
1819
};
1920

2021
use crate::hir;
@@ -35,7 +36,7 @@ use crate::util::nodemap::{FxHashMap, FxHashSet};
3536
use errors::{Applicability, DiagnosticBuilder};
3637
use std::fmt;
3738
use syntax::ast;
38-
use syntax::symbol::sym;
39+
use syntax::symbol::{sym, kw};
3940
use syntax_pos::{DUMMY_SP, Span, ExpnKind};
4041

4142
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -669,8 +670,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
669670
} else {
670671
format!(
671672
"{}the trait `{}` is not implemented for `{}`",
672-
pre_message,
673-
trait_ref,
673+
pre_message,
674+
trait_ref,
674675
trait_ref.self_ty(),
675676
)
676677
};
@@ -689,6 +690,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
689690
}
690691

691692
self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
693+
self.suggest_fn_call(&obligation, &mut err, &trait_ref);
692694
self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
693695
self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
694696

@@ -956,6 +958,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
956958
}
957959
}
958960

961+
fn suggest_fn_call(
962+
&self,
963+
obligation: &PredicateObligation<'tcx>,
964+
err: &mut DiagnosticBuilder<'tcx>,
965+
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
966+
) {
967+
let self_ty = trait_ref.self_ty();
968+
match self_ty.sty {
969+
ty::FnDef(def_id, _) => {
970+
// We tried to apply the bound to an `fn`. Check wether calling it
971+
// would evaluate to a type that *would* satisfy the trait binding.
972+
// If it would, suggest calling it: `bar(foo)` -> `bar(foo)`. This
973+
// case is *very* to hit if `foo` is `async`.
974+
let output_ty = self_ty.fn_sig(self.tcx).output();
975+
let new_trait_ref = ty::TraitRef {
976+
def_id: trait_ref.def_id(),
977+
substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]),
978+
};
979+
let obligation = Obligation::new(
980+
obligation.cause.clone(),
981+
obligation.param_env,
982+
new_trait_ref.to_predicate(),
983+
);
984+
match self.evaluate_obligation(&obligation) {
985+
Ok(EvaluationResult::EvaluatedToOk) |
986+
Ok(EvaluationResult::EvaluatedToOkModuloRegions) |
987+
Ok(EvaluationResult::EvaluatedToAmbig) => {
988+
if let Some(hir::Node::Item(hir::Item {
989+
ident,
990+
node: hir::ItemKind::Fn(.., body_id),
991+
..
992+
})) = self.tcx.hir().get_if_local(def_id) {
993+
let body = self.tcx.hir().body(*body_id);
994+
err.help(&format!(
995+
"it looks like you forgot to use parentheses to \
996+
call the function: `{}({})`",
997+
ident,
998+
body.arguments.iter()
999+
.map(|arg| match &arg.pat.node {
1000+
hir::PatKind::Binding(_, _, ident, None)
1001+
if ident.name != kw::SelfLower => ident.to_string(),
1002+
_ => "_".to_string(),
1003+
}).collect::<Vec<_>>().join(", ")));
1004+
}
1005+
}
1006+
_ => {}
1007+
}
1008+
}
1009+
_ => {}
1010+
}
1011+
}
1012+
9591013
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
9601014
/// suggest removing these references until we reach a type that implements the trait.
9611015
fn suggest_remove_reference(
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// edition:2018
2+
use std::future::Future;
3+
4+
async fn foo() {}
5+
6+
fn bar(f: impl Future<Output=()>) {}
7+
8+
fn main() {
9+
bar(foo); //~ERROR E0277
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0277]: the trait bound `fn() -> impl std::future::Future {foo}: std::future::Future` is not satisfied
2+
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:9:5
3+
|
4+
LL | fn bar(f: impl Future<Output=()>) {}
5+
| --------------------------------- required by `bar`
6+
...
7+
LL | bar(foo);
8+
| ^^^ the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}`
9+
|
10+
= help: it looks like you forgot to use parentheses to call the function: `foo()`
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// edition:2018
2+
trait T {
3+
type O;
4+
}
5+
6+
struct S;
7+
8+
impl T for S {
9+
type O = ();
10+
}
11+
12+
fn foo() -> impl T<O=()> { S }
13+
14+
fn bar(f: impl T<O=()>) {}
15+
16+
fn main() {
17+
bar(foo); //~ERROR E0277
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0277]: the trait bound `fn() -> impl T {foo}: T` is not satisfied
2+
--> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:17:5
3+
|
4+
LL | fn bar(f: impl T<O=()>) {}
5+
| ----------------------- required by `bar`
6+
...
7+
LL | bar(foo);
8+
| ^^^ the trait `T` is not implemented for `fn() -> impl T {foo}`
9+
|
10+
= help: it looks like you forgot to use parentheses to call the function: `foo()`
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)