Skip to content

Commit 9ff1376

Browse files
committed
Suggest adding Fn bound when calling a generic parameter
1 parent f63685d commit 9ff1376

File tree

4 files changed

+109
-6
lines changed

4 files changed

+109
-6
lines changed

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use tracing::{debug, instrument};
2525
use super::method::MethodCallee;
2626
use super::method::probe::ProbeScope;
2727
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
28+
use crate::expr_use_visitor::TypeInformationCtxt;
2829
use crate::{errors, fluent_generated};
2930

3031
/// Checks that it is legal to call methods of the trait corresponding
@@ -122,7 +123,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
122123
);
123124
}
124125

125-
let guar = self.report_invalid_callee(call_expr, callee_expr, expr_ty, arg_exprs);
126+
let guar = self.report_invalid_callee(
127+
call_expr,
128+
callee_expr,
129+
expr_ty,
130+
arg_exprs,
131+
expected,
132+
);
126133
Ty::new_error(self.tcx, guar)
127134
}
128135

@@ -677,6 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
677684
callee_expr: &'tcx hir::Expr<'tcx>,
678685
callee_ty: Ty<'tcx>,
679686
arg_exprs: &'tcx [hir::Expr<'tcx>],
687+
expected: Expectation<'tcx>,
680688
) -> ErrorGuaranteed {
681689
// Callee probe fails when APIT references errors, so suppress those
682690
// errors here.
@@ -806,6 +814,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
806814
err.span_label(def_span, "the callable type is defined here");
807815
}
808816
} else {
817+
// Suggest adding <Param>: Fn(...) [-> RetType]
818+
if callee_ty.has_non_region_param() {
819+
let arg_types: Vec<Ty<'tcx>> = arg_exprs
820+
.iter()
821+
.map(|arg| self.typeck_results.borrow().expr_ty(arg))
822+
.collect();
823+
let args_tuple = Ty::new_tup(self.tcx(), &arg_types);
824+
825+
let fn_def_id = self.tcx().require_lang_item(LangItem::Fn, callee_expr.span);
826+
let trait_ref =
827+
ty::TraitRef::new(self.tcx(), fn_def_id, [callee_ty, args_tuple]);
828+
829+
let trait_pred =
830+
ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive };
831+
832+
let associated_ty = expected.to_option(self).map(|ty| ("Output", ty));
833+
self.err_ctxt().suggest_restricting_param_bound(
834+
&mut err,
835+
ty::Binder::dummy(trait_pred),
836+
associated_ty,
837+
self.body_id,
838+
);
839+
}
809840
err.span_label(call_expr.span, "call expression requires function");
810841
}
811842
}

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_hir::{
2323
};
2424
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};
2525
use rustc_middle::middle::privacy::Level;
26+
use rustc_middle::query::Key;
2627
use rustc_middle::traits::IsConstable;
2728
use rustc_middle::ty::error::TypeError;
2829
use rustc_middle::ty::print::{
@@ -388,12 +389,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
388389
);
389390

390391
if let Some((name, term)) = associated_ty {
391-
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
392-
// That should be extracted into a helper function.
393-
if let Some(stripped) = constraint.strip_suffix('>') {
394-
constraint = format!("{stripped}, {name} = {term}>");
392+
let fn_def_id = self
393+
.tcx
394+
.require_lang_item(LangItem::Fn, body_id.default_span(self.tcx));
395+
if fn_def_id == trait_pred.skip_binder().trait_ref.def_id {
396+
constraint.push_str(&format!(" -> {term}"));
395397
} else {
396-
constraint.push_str(&format!("<{name} = {term}>"));
398+
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
399+
// That should be extracted into a helper function.
400+
if let Some(stripped) = constraint.strip_suffix('>') {
401+
constraint = format!("{stripped}, {name} = {term}>");
402+
} else {
403+
constraint.push_str(&format!("<{name} = {term}>"));
404+
}
397405
}
398406
}
399407

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn return_type<T>(t: T) {
2+
let x: u32 = t(1);
3+
//~^ ERROR: expected function, found `T` [E0618]
4+
}
5+
6+
fn no_return_type<T>(t: T) {
7+
t(1, 2, true);
8+
//~^ ERROR: expected function, found `T` [E0618]
9+
}
10+
11+
fn existing_bound<T: Copy>(t: T) {
12+
t(false);
13+
//~^ ERROR: expected function, found `T` [E0618]
14+
}
15+
16+
fn main() {}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
error[E0618]: expected function, found `T`
2+
--> $DIR/suggest-call-on-generic-param.rs:2:18
3+
|
4+
LL | fn return_type<T>(t: T) {
5+
| - `t` has type `T`
6+
LL | let x: u32 = t(1);
7+
| ^---
8+
| |
9+
| call expression requires function
10+
|
11+
help: consider restricting type parameter `T` with trait `Fn`
12+
|
13+
LL | fn return_type<T: Fn(i32) -> u32>(t: T) {
14+
| ++++++++++++++++
15+
16+
error[E0618]: expected function, found `T`
17+
--> $DIR/suggest-call-on-generic-param.rs:7:5
18+
|
19+
LL | fn no_return_type<T>(t: T) {
20+
| - `t` has type `T`
21+
LL | t(1, 2, true);
22+
| ^------------
23+
| |
24+
| call expression requires function
25+
|
26+
help: consider restricting type parameter `T` with trait `Fn`
27+
|
28+
LL | fn no_return_type<T: Fn(i32, i32, bool)>(t: T) {
29+
| ++++++++++++++++++++
30+
31+
error[E0618]: expected function, found `T`
32+
--> $DIR/suggest-call-on-generic-param.rs:12:5
33+
|
34+
LL | fn existing_bound<T: Copy>(t: T) {
35+
| - `t` has type `T`
36+
LL | t(false);
37+
| ^-------
38+
| |
39+
| call expression requires function
40+
|
41+
help: consider further restricting type parameter `T` with trait `Fn`
42+
|
43+
LL | fn existing_bound<T: Copy + Fn(bool)>(t: T) {
44+
| ++++++++++
45+
46+
error: aborting due to 3 previous errors
47+
48+
For more information about this error, try `rustc --explain E0618`.

0 commit comments

Comments
 (0)