diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 48bb45de53eb4..3e9e734149e5a 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -25,6 +25,7 @@ use tracing::{debug, instrument};
use super::method::MethodCallee;
use super::method::probe::ProbeScope;
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
+use crate::expr_use_visitor::TypeInformationCtxt;
use crate::{errors, fluent_generated};
/// Checks that it is legal to call methods of the trait corresponding
@@ -122,7 +123,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- let guar = self.report_invalid_callee(call_expr, callee_expr, expr_ty, arg_exprs);
+ let guar = self.report_invalid_callee(
+ call_expr,
+ callee_expr,
+ expr_ty,
+ arg_exprs,
+ expected,
+ );
Ty::new_error(self.tcx, guar)
}
@@ -677,6 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_expr: &'tcx hir::Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
+ expected: Expectation<'tcx>,
) -> ErrorGuaranteed {
// Callee probe fails when APIT references errors, so suppress those
// errors here.
@@ -806,6 +814,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(def_span, "the callable type is defined here");
}
} else {
+ // Suggest adding : Fn(...) [-> RetType]
+ if callee_ty.has_non_region_param() {
+ let arg_types: Vec> = arg_exprs
+ .iter()
+ .map(|arg| self.typeck_results.borrow().expr_ty(arg))
+ .collect();
+ let args_tuple = Ty::new_tup(self.tcx(), &arg_types);
+
+ let fn_def_id = self.tcx().require_lang_item(LangItem::Fn, callee_expr.span);
+ let trait_ref =
+ ty::TraitRef::new(self.tcx(), fn_def_id, [callee_ty, args_tuple]);
+
+ let trait_pred =
+ ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive };
+
+ let associated_ty = expected
+ .to_option(self)
+ // We do not want to suggest e.g. `-> _`
+ .filter(|ty| !ty.has_infer())
+ .map(|ty| ("Output", ty));
+ self.err_ctxt().suggest_restricting_param_bound(
+ &mut err,
+ ty::Binder::dummy(trait_pred),
+ associated_ty,
+ self.body_id,
+ );
+ }
err.span_label(call_expr.span, "call expression requires function");
}
}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index bf7d4257b6269..9923a8179b2f0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -388,12 +388,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
if let Some((name, term)) = associated_ty {
- // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
- // That should be extracted into a helper function.
- if let Some(stripped) = constraint.strip_suffix('>') {
- constraint = format!("{stripped}, {name} = {term}>");
+ if self.tcx.is_fn_trait(trait_pred.skip_binder().trait_ref.def_id) {
+ constraint.push_str(&format!(" -> {term}"));
} else {
- constraint.push_str(&format!("<{name} = {term}>"));
+ // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
+ // That should be extracted into a helper function.
+ if let Some(stripped) = constraint.strip_suffix('>') {
+ constraint = format!("{stripped}, {name} = {term}>");
+ } else {
+ constraint.push_str(&format!("<{name} = {term}>"));
+ }
}
}
diff --git a/tests/ui/issues/issue-21701.stderr b/tests/ui/issues/issue-21701.stderr
index 9f1fe7dde735a..ee491da2e1ced 100644
--- a/tests/ui/issues/issue-21701.stderr
+++ b/tests/ui/issues/issue-21701.stderr
@@ -7,6 +7,11 @@ LL | let y = t();
| ^--
| |
| call expression requires function
+ |
+help: consider restricting type parameter `U` with trait `Fn`
+ |
+LL | fn foo(t: U) {
+ | ++++++
error[E0618]: expected function, found struct `Bar`
--> $DIR/issue-21701.rs:9:13
diff --git a/tests/ui/suggestions/suggest-call-on-generic-param.rs b/tests/ui/suggestions/suggest-call-on-generic-param.rs
new file mode 100644
index 0000000000000..5f4939c604547
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-generic-param.rs
@@ -0,0 +1,26 @@
+fn return_type(t: T) {
+ let x: u32 = t(1);
+ //~^ ERROR: expected function, found `T` [E0618]
+}
+
+fn unknown_return_type(t: T) {
+ let x = t();
+ //~^ ERROR: expected function, found `T` [E0618]
+}
+
+fn nested_return_type(t: Vec) {
+ t();
+ //~^ ERROR: expected function, found `Vec` [E0618]
+}
+
+fn no_return_type(t: T) {
+ t(1, 2, true);
+ //~^ ERROR: expected function, found `T` [E0618]
+}
+
+fn existing_bound(t: T) {
+ t(false);
+ //~^ ERROR: expected function, found `T` [E0618]
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suggest-call-on-generic-param.stderr b/tests/ui/suggestions/suggest-call-on-generic-param.stderr
new file mode 100644
index 0000000000000..63578845d5ea7
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-generic-param.stderr
@@ -0,0 +1,78 @@
+error[E0618]: expected function, found `T`
+ --> $DIR/suggest-call-on-generic-param.rs:2:18
+ |
+LL | fn return_type(t: T) {
+ | - `t` has type `T`
+LL | let x: u32 = t(1);
+ | ^---
+ | |
+ | call expression requires function
+ |
+help: consider restricting type parameter `T` with trait `Fn`
+ |
+LL | fn return_type u32>(t: T) {
+ | ++++++++++++++++
+
+error[E0618]: expected function, found `T`
+ --> $DIR/suggest-call-on-generic-param.rs:7:13
+ |
+LL | fn unknown_return_type(t: T) {
+ | - `t` has type `T`
+LL | let x = t();
+ | ^--
+ | |
+ | call expression requires function
+ |
+help: consider restricting type parameter `T` with trait `Fn`
+ |
+LL | fn unknown_return_type(t: T) {
+ | ++++++
+
+error[E0618]: expected function, found `Vec`
+ --> $DIR/suggest-call-on-generic-param.rs:12:5
+ |
+LL | fn nested_return_type(t: Vec) {
+ | - `t` has type `Vec`
+LL | t();
+ | ^--
+ | |
+ | call expression requires function
+ |
+help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
+ |
+LL | fn nested_return_type(t: Vec) where Vec: Fn() {
+ | ++++++++++++++++++
+
+error[E0618]: expected function, found `T`
+ --> $DIR/suggest-call-on-generic-param.rs:17:5
+ |
+LL | fn no_return_type(t: T) {
+ | - `t` has type `T`
+LL | t(1, 2, true);
+ | ^------------
+ | |
+ | call expression requires function
+ |
+help: consider restricting type parameter `T` with trait `Fn`
+ |
+LL | fn no_return_type(t: T) {
+ | ++++++++++++++++++++
+
+error[E0618]: expected function, found `T`
+ --> $DIR/suggest-call-on-generic-param.rs:22:5
+ |
+LL | fn existing_bound(t: T) {
+ | - `t` has type `T`
+LL | t(false);
+ | ^-------
+ | |
+ | call expression requires function
+ |
+help: consider further restricting type parameter `T` with trait `Fn`
+ |
+LL | fn existing_bound(t: T) {
+ | ++++++++++
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0618`.