Skip to content

Commit 6f1c20d

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

File tree

5 files changed

+154
-6
lines changed

5 files changed

+154
-6
lines changed

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 36 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,33 @@ 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
833+
.to_option(self)
834+
// We do not want to suggest e.g. `-> _`
835+
.filter(|ty| !ty.has_infer())
836+
.map(|ty| ("Output", ty));
837+
self.err_ctxt().suggest_restricting_param_bound(
838+
&mut err,
839+
ty::Binder::dummy(trait_pred),
840+
associated_ty,
841+
self.body_id,
842+
);
843+
}
809844
err.span_label(call_expr.span, "call expression requires function");
810845
}
811846
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
388388
);
389389

390390
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}>");
391+
if self.tcx.is_fn_trait(trait_pred.skip_binder().trait_ref.def_id) {
392+
constraint.push_str(&format!(" -> {term}"));
395393
} else {
396-
constraint.push_str(&format!("<{name} = {term}>"));
394+
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
395+
// That should be extracted into a helper function.
396+
if let Some(stripped) = constraint.strip_suffix('>') {
397+
constraint = format!("{stripped}, {name} = {term}>");
398+
} else {
399+
constraint.push_str(&format!("<{name} = {term}>"));
400+
}
397401
}
398402
}
399403

tests/ui/issues/issue-21701.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | let y = t();
77
| ^--
88
| |
99
| call expression requires function
10+
|
11+
help: consider restricting type parameter `U` with trait `Fn`
12+
|
13+
LL | fn foo<U: Fn()>(t: U) {
14+
| ++++++
1015

1116
error[E0618]: expected function, found struct `Bar`
1217
--> $DIR/issue-21701.rs:9:13
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
fn return_type<T>(t: T) {
2+
let x: u32 = t(1);
3+
//~^ ERROR: expected function, found `T` [E0618]
4+
}
5+
6+
fn unknown_return_type<T>(t: T) {
7+
let x = t();
8+
//~^ ERROR: expected function, found `T` [E0618]
9+
}
10+
11+
fn nested_return_type<T>(t: Vec<T>) {
12+
t();
13+
//~^ ERROR: expected function, found `Vec<T>` [E0618]
14+
}
15+
16+
fn no_return_type<T>(t: T) {
17+
t(1, 2, true);
18+
//~^ ERROR: expected function, found `T` [E0618]
19+
}
20+
21+
fn existing_bound<T: Copy>(t: T) {
22+
t(false);
23+
//~^ ERROR: expected function, found `T` [E0618]
24+
}
25+
26+
fn main() {}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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:13
18+
|
19+
LL | fn unknown_return_type<T>(t: T) {
20+
| - `t` has type `T`
21+
LL | let x = t();
22+
| ^--
23+
| |
24+
| call expression requires function
25+
|
26+
help: consider restricting type parameter `T` with trait `Fn`
27+
|
28+
LL | fn unknown_return_type<T: Fn()>(t: T) {
29+
| ++++++
30+
31+
error[E0618]: expected function, found `Vec<T>`
32+
--> $DIR/suggest-call-on-generic-param.rs:12:5
33+
|
34+
LL | fn nested_return_type<T>(t: Vec<T>) {
35+
| - `t` has type `Vec<T>`
36+
LL | t();
37+
| ^--
38+
| |
39+
| call expression requires function
40+
|
41+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
42+
|
43+
LL | fn nested_return_type<T>(t: Vec<T>) where Vec<T>: Fn() {
44+
| ++++++++++++++++++
45+
46+
error[E0618]: expected function, found `T`
47+
--> $DIR/suggest-call-on-generic-param.rs:17:5
48+
|
49+
LL | fn no_return_type<T>(t: T) {
50+
| - `t` has type `T`
51+
LL | t(1, 2, true);
52+
| ^------------
53+
| |
54+
| call expression requires function
55+
|
56+
help: consider restricting type parameter `T` with trait `Fn`
57+
|
58+
LL | fn no_return_type<T: Fn(i32, i32, bool)>(t: T) {
59+
| ++++++++++++++++++++
60+
61+
error[E0618]: expected function, found `T`
62+
--> $DIR/suggest-call-on-generic-param.rs:22:5
63+
|
64+
LL | fn existing_bound<T: Copy>(t: T) {
65+
| - `t` has type `T`
66+
LL | t(false);
67+
| ^-------
68+
| |
69+
| call expression requires function
70+
|
71+
help: consider further restricting type parameter `T` with trait `Fn`
72+
|
73+
LL | fn existing_bound<T: Copy + Fn(bool)>(t: T) {
74+
| ++++++++++
75+
76+
error: aborting due to 5 previous errors
77+
78+
For more information about this error, try `rustc --explain E0618`.

0 commit comments

Comments
 (0)