Skip to content

Commit dfd43f0

Browse files
committed
Auto merge of #63870 - estebank:async-fn-call, r=oli-obk
Suggest call fn ctor passed as arg to fn with type param bounds _Reviewer note: the relevant changes are in the second commit, the first is simple and mechanical, but verbose._ When forgetting to call a fn in an argument position to an fn that has a generic bound: ```rust async fn foo() {} fn bar(f: impl Future<Output=()>) {} fn main() { bar(foo); // <- should be `bar(foo());` } ``` suggest calling it: ``` 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()` ``` Fix #63100. Follow up to #63833 and #63337.
2 parents e29faf0 + e553051 commit dfd43f0

File tree

193 files changed

+1317
-2102
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+1317
-2102
lines changed

src/librustc/traits/error_reporting.rs

+83-21
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> {
@@ -657,19 +658,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
657658
span,
658659
E0277,
659660
"{}",
660-
message.unwrap_or_else(||
661-
format!("the trait bound `{}` is not satisfied{}",
662-
trait_ref.to_predicate(), post_message)
663-
));
661+
message.unwrap_or_else(|| format!(
662+
"the trait bound `{}` is not satisfied{}",
663+
trait_ref.to_predicate(),
664+
post_message,
665+
)));
664666

665667
let explanation =
666668
if obligation.cause.code == ObligationCauseCode::MainFunctionType {
667669
"consider using `()`, or a `Result`".to_owned()
668670
} else {
669-
format!("{}the trait `{}` is not implemented for `{}`",
670-
pre_message,
671-
trait_ref,
672-
trait_ref.self_ty())
671+
format!(
672+
"{}the trait `{}` is not implemented for `{}`",
673+
pre_message,
674+
trait_ref,
675+
trait_ref.self_ty(),
676+
)
673677
};
674678

675679
if let Some(ref s) = label {
@@ -686,6 +690,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
686690
}
687691

688692
self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
693+
self.suggest_fn_call(&obligation, &mut err, &trait_ref);
689694
self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
690695
self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
691696

@@ -953,6 +958,57 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
953958
}
954959
}
955960

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 whether calling it would evaluate
971+
// to a type that *would* satisfy the trait binding. If it would, suggest calling
972+
// it: `bar(foo)` -> `bar(foo)`. This case is *very* likely to be hit if `foo` is
973+
// `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+
"use parentheses to call the function: `{}({})`",
996+
ident,
997+
body.params.iter()
998+
.map(|arg| match &arg.pat.node {
999+
hir::PatKind::Binding(_, _, ident, None)
1000+
if ident.name != kw::SelfLower => ident.to_string(),
1001+
_ => "_".to_string(),
1002+
}).collect::<Vec<_>>().join(", ")));
1003+
}
1004+
}
1005+
_ => {}
1006+
}
1007+
}
1008+
_ => {}
1009+
}
1010+
}
1011+
9561012
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
9571013
/// suggest removing these references until we reach a type that implements the trait.
9581014
fn suggest_remove_reference(
@@ -1535,25 +1591,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15351591
err.note("only the last element of a tuple may have a dynamically sized type");
15361592
}
15371593
ObligationCauseCode::ProjectionWf(data) => {
1538-
err.note(&format!("required so that the projection `{}` is well-formed",
1539-
data));
1594+
err.note(&format!(
1595+
"required so that the projection `{}` is well-formed",
1596+
data,
1597+
));
15401598
}
15411599
ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
1542-
err.note(&format!("required so that reference `{}` does not outlive its referent",
1543-
ref_ty));
1600+
err.note(&format!(
1601+
"required so that reference `{}` does not outlive its referent",
1602+
ref_ty,
1603+
));
15441604
}
15451605
ObligationCauseCode::ObjectTypeBound(object_ty, region) => {
1546-
err.note(&format!("required so that the lifetime bound of `{}` for `{}` \
1547-
is satisfied",
1548-
region, object_ty));
1606+
err.note(&format!(
1607+
"required so that the lifetime bound of `{}` for `{}` is satisfied",
1608+
region,
1609+
object_ty,
1610+
));
15491611
}
15501612
ObligationCauseCode::ItemObligation(item_def_id) => {
15511613
let item_name = tcx.def_path_str(item_def_id);
15521614
let msg = format!("required by `{}`", item_name);
15531615

15541616
if let Some(sp) = tcx.hir().span_if_local(item_def_id) {
15551617
let sp = tcx.sess.source_map().def_span(sp);
1556-
err.span_note(sp, &msg);
1618+
err.span_label(sp, &msg);
15571619
} else {
15581620
err.note(&msg);
15591621
}

0 commit comments

Comments
 (0)