Skip to content

Commit 7eb2d4e

Browse files
Generalize call suggestion for unsatisfied predicate
1 parent f5336a9 commit 7eb2d4e

13 files changed

+174
-74
lines changed

compiler/rustc_hir_analysis/src/check/callee.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::method::probe::{IsSuggestion, Mode, ProbeScope};
22
use super::method::MethodCallee;
3-
use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag};
3+
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
44
use crate::type_error_struct;
55

66
use rustc_ast::util::parser::PREC_POSTFIX;
@@ -27,6 +27,7 @@ use rustc_span::Span;
2727
use rustc_target::spec::abi;
2828
use rustc_trait_selection::autoderef::Autoderef;
2929
use rustc_trait_selection::infer::InferCtxtExt as _;
30+
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
3031
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
3132

3233
use std::iter;

compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use super::FnCtxt;
22
use crate::astconv::AstConv;
33
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
44

5-
use hir::def_id::DefId;
65
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
76
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
87
use rustc_hir as hir;
@@ -19,6 +18,7 @@ use rustc_session::errors::ExprParenthesesNeeded;
1918
use rustc_span::symbol::sym;
2019
use rustc_span::Span;
2120
use rustc_trait_selection::infer::InferCtxtExt;
21+
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
2222
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2323

2424
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1209,8 +1209,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12091209
}
12101210
}
12111211
}
1212-
1213-
pub enum DefIdOrName {
1214-
DefId(DefId),
1215-
Name(&'static str),
1216-
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -2796,3 +2796,8 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor {
27962796
}
27972797
}
27982798
}
2799+
2800+
pub enum DefIdOrName {
2801+
DefId(DefId),
2802+
Name(&'static str),
2803+
}

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

+118-46
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use super::{
2-
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
2+
DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
3+
SelectionContext,
34
};
45

56
use crate::autoderef::Autoderef;
67
use crate::infer::InferCtxt;
78
use crate::traits::normalize_to;
89

10+
use hir::def::CtorOf;
911
use hir::HirId;
1012
use rustc_data_structures::fx::FxHashSet;
1113
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -812,28 +814,87 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
812814
err: &mut Diagnostic,
813815
trait_pred: ty::PolyTraitPredicate<'tcx>,
814816
) -> bool {
815-
// Skipping binder here, remapping below
816-
let self_ty = trait_pred.self_ty().skip_binder();
817+
if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder()
818+
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
819+
{
820+
// Don't suggest calling to turn an unsized type into a sized type
821+
return false;
822+
}
817823

818-
let (def_id, inputs, output, kind) = match *self_ty.kind() {
819-
ty::Closure(def_id, substs) => {
820-
let sig = substs.as_closure().sig();
821-
(def_id, sig.inputs().map_bound(|inputs| &inputs[1..]), sig.output(), "closure")
824+
// This is duplicated from `extract_callable_info` in typeck, which
825+
// relies on autoderef, so we can't use it here.
826+
let found = trait_pred.self_ty().skip_binder().peel_refs();
827+
let Some((def_id_or_name, output, inputs)) = (match *found.kind()
828+
{
829+
ty::FnPtr(fn_sig) => {
830+
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs()))
822831
}
823832
ty::FnDef(def_id, _) => {
824-
let sig = self_ty.fn_sig(self.tcx);
825-
(
826-
def_id,
827-
sig.inputs(),
828-
sig.output(),
829-
match self.tcx.def_kind(def_id) {
830-
DefKind::Ctor(..) => "constructor",
831-
_ => "function",
832-
},
833-
)
833+
let fn_sig = found.fn_sig(self.tcx);
834+
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
834835
}
835-
_ => return false,
836-
};
836+
ty::Closure(def_id, substs) => {
837+
let fn_sig = substs.as_closure().sig();
838+
Some((
839+
DefIdOrName::DefId(def_id),
840+
fn_sig.output(),
841+
fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
842+
))
843+
}
844+
ty::Opaque(def_id, substs) => {
845+
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
846+
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
847+
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
848+
// args tuple will always be substs[1]
849+
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
850+
{
851+
Some((
852+
DefIdOrName::DefId(def_id),
853+
pred.kind().rebind(proj.term.ty().unwrap()),
854+
pred.kind().rebind(args.as_slice()),
855+
))
856+
} else {
857+
None
858+
}
859+
})
860+
}
861+
ty::Dynamic(data, _, ty::Dyn) => {
862+
data.iter().find_map(|pred| {
863+
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
864+
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
865+
// for existential projection, substs are shifted over by 1
866+
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
867+
{
868+
Some((
869+
DefIdOrName::Name("trait object"),
870+
pred.rebind(proj.term.ty().unwrap()),
871+
pred.rebind(args.as_slice()),
872+
))
873+
} else {
874+
None
875+
}
876+
})
877+
}
878+
ty::Param(_) => {
879+
obligation.param_env.caller_bounds().iter().find_map(|pred| {
880+
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
881+
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
882+
&& proj.projection_ty.self_ty() == found
883+
// args tuple will always be substs[1]
884+
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
885+
{
886+
Some((
887+
DefIdOrName::Name("type parameter"),
888+
pred.kind().rebind(proj.term.ty().unwrap()),
889+
pred.kind().rebind(args.as_slice()),
890+
))
891+
} else {
892+
None
893+
}
894+
})
895+
}
896+
_ => None,
897+
}) else { return false; };
837898
let output = self.replace_bound_vars_with_fresh_vars(
838899
obligation.cause.span,
839900
LateBoundRegionConversionTime::FnCall,
@@ -859,7 +920,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
859920
// Get the name of the callable and the arguments to be used in the suggestion.
860921
let hir = self.tcx.hir();
861922

862-
let msg = format!("use parentheses to call the {}", kind);
923+
let msg = match def_id_or_name {
924+
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
925+
DefKind::Ctor(CtorOf::Struct, _) => {
926+
"use parentheses to instantiate this tuple struct".to_string()
927+
}
928+
DefKind::Ctor(CtorOf::Variant, _) => {
929+
"use parentheses to instantiate this tuple variant".to_string()
930+
}
931+
kind => format!("use parentheses to call this {}", kind.descr(def_id)),
932+
},
933+
DefIdOrName::Name(name) => format!("use parentheses to call this {name}"),
934+
};
863935

864936
let args = inputs
865937
.map(|ty| {
@@ -872,31 +944,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
872944
.collect::<Vec<_>>()
873945
.join(", ");
874946

875-
let name = match hir.get_if_local(def_id) {
876-
Some(hir::Node::Expr(hir::Expr {
877-
kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
878-
..
879-
})) => {
880-
err.span_label(*fn_decl_span, "consider calling this closure");
881-
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
882-
return false;
883-
};
884-
name.to_string()
885-
}
886-
Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => {
887-
err.span_label(ident.span, "consider calling this function");
888-
ident.to_string()
889-
}
890-
Some(hir::Node::Ctor(..)) => {
891-
let name = self.tcx.def_path_str(def_id);
892-
err.span_label(
893-
self.tcx.def_span(def_id),
894-
format!("consider calling the constructor for `{}`", name),
895-
);
896-
name
897-
}
898-
_ => return false,
899-
};
900947
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
901948
&& obligation.cause.span.can_be_used_for_suggestions()
902949
{
@@ -910,7 +957,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
910957
format!("({args})"),
911958
Applicability::HasPlaceholders,
912959
);
913-
} else {
960+
} else if let DefIdOrName::DefId(def_id) = def_id_or_name {
961+
let name = match hir.get_if_local(def_id) {
962+
Some(hir::Node::Expr(hir::Expr {
963+
kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
964+
..
965+
})) => {
966+
err.span_label(*fn_decl_span, "consider calling this closure");
967+
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
968+
return false;
969+
};
970+
name.to_string()
971+
}
972+
Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => {
973+
err.span_label(ident.span, "consider calling this function");
974+
ident.to_string()
975+
}
976+
Some(hir::Node::Ctor(..)) => {
977+
let name = self.tcx.def_path_str(def_id);
978+
err.span_label(
979+
self.tcx.def_span(def_id),
980+
format!("consider calling the constructor for `{}`", name),
981+
);
982+
name
983+
}
984+
_ => return false,
985+
};
914986
err.help(&format!("{msg}: `{name}({args})`"));
915987
}
916988
true

src/test/ui/binop/issue-77910-1.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ LL | assert_eq!(foo, y);
1919
| ^^^^^^^^^^^^^^^^^^ `for<'a> fn(&'a i32) -> &'a i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
2020
|
2121
= help: the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}`
22-
= help: use parentheses to call the function: `foo(/* &i32 */)`
22+
= help: use parentheses to call this function: `foo(/* &i32 */)`
2323
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
2424

2525
error: aborting due to 2 previous errors

src/test/ui/closures/closure-bounds-subtype.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ note: required by a bound in `take_const_owned`
1111
|
1212
LL | fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send {
1313
| ^^^^ required by this bound in `take_const_owned`
14+
help: use parentheses to call this type parameter
15+
|
16+
LL | take_const_owned(f());
17+
| ++
1418
help: consider further restricting this bound
1519
|
1620
LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + std::marker::Sync {

src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ LL | assert_eq!(a, 0);
2929
| ^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
3030
|
3131
= help: the trait `Debug` is not implemented for fn item `fn() -> i32 {a}`
32-
= help: use parentheses to call the function: `a()`
32+
= help: use parentheses to call this function: `a()`
3333
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
3434

3535
error: aborting due to 3 previous errors

src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
error[E0277]: `fn() -> impl Future<Output = ()> {foo}` is not a future
22
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:9
33
|
4-
LL | async fn foo() {}
5-
| --- consider calling this function
6-
...
74
LL | bar(foo);
85
| --- ^^^ `fn() -> impl Future<Output = ()> {foo}` is not a future
96
| |
@@ -16,16 +13,14 @@ note: required by a bound in `bar`
1613
|
1714
LL | fn bar(f: impl Future<Output=()>) {}
1815
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
19-
help: use parentheses to call the function
16+
help: use parentheses to call this function
2017
|
2118
LL | bar(foo());
2219
| ++
2320

2421
error[E0277]: `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future
2522
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:9
2623
|
27-
LL | let async_closure = async || ();
28-
| -------- consider calling this closure
2924
LL | bar(async_closure);
3025
| --- ^^^^^^^^^^^^^ `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future
3126
| |
@@ -38,7 +33,7 @@ note: required by a bound in `bar`
3833
|
3934
LL | fn bar(f: impl Future<Output=()>) {}
4035
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
41-
help: use parentheses to call the closure
36+
help: use parentheses to call this closure
4237
|
4338
LL | bar(async_closure());
4439
| ++

src/test/ui/suggestions/call-on-unimplemented-ctor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ fn main() {
22
insert_resource(Marker);
33
insert_resource(Time);
44
//~^ ERROR the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied
5-
//~| HELP use parentheses to call the constructor
5+
//~| HELP use parentheses to instantiate this tuple struct
66
}
77

88
trait Resource {}

src/test/ui/suggestions/call-on-unimplemented-ctor.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ LL | insert_resource(Time);
55
| --------------- ^^^^ the trait `Resource` is not implemented for fn item `fn(u32) -> Time {Time}`
66
| |
77
| required by a bound introduced by this call
8-
...
9-
LL | struct Time(u32);
10-
| ----------- consider calling the constructor for `Time`
118
|
129
note: required by a bound in `insert_resource`
1310
--> $DIR/call-on-unimplemented-ctor.rs:10:23
1411
|
1512
LL | fn insert_resource<R: Resource>(resource: R) {}
1613
| ^^^^^^^^ required by this bound in `insert_resource`
17-
help: use parentheses to call the constructor
14+
help: use parentheses to instantiate this tuple struct
1815
|
1916
LL | insert_resource(Time(/* u32 */));
2017
| +++++++++++
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct Foo;
2+
3+
trait Bar {}
4+
5+
impl Bar for Foo {}
6+
7+
fn needs_bar<T: Bar>(_: T) {}
8+
9+
fn blah(f: fn() -> Foo) {
10+
needs_bar(f);
11+
//~^ ERROR the trait bound `fn() -> Foo: Bar` is not satisfied
12+
//~| HELP use parentheses to call this function pointer
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0277]: the trait bound `fn() -> Foo: Bar` is not satisfied
2+
--> $DIR/call-on-unimplemented-fn-ptr.rs:10:15
3+
|
4+
LL | needs_bar(f);
5+
| --------- ^ the trait `Bar` is not implemented for `fn() -> Foo`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `needs_bar`
10+
--> $DIR/call-on-unimplemented-fn-ptr.rs:7:17
11+
|
12+
LL | fn needs_bar<T: Bar>(_: T) {}
13+
| ^^^ required by this bound in `needs_bar`
14+
help: use parentheses to call this function pointer
15+
|
16+
LL | needs_bar(f());
17+
| ++
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)