Suggest call fn ctor passed as arg to fn with type param bounds
This commit is contained in:
parent
444bc3ca66
commit
5384d5584f
5 changed files with 117 additions and 7 deletions
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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`.
|
|
@ -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
|
||||
}
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue