Skip to content

Commit c064f6e

Browse files
authored
Rollup merge of rust-lang#132489 - compiler-errors:fn-sugg-tweaks, r=BoxyUwU
Fix closure arg extraction in `extract_callable_info`, generalize it to async closures * Fix argument extraction in `extract_callable_info` * FIx `extract_callable_info` to work for async closures * Remove redundant `is_fn_ty` which is just a less general `extract_callable_info` * More precisely name what is being called (i.e. call it a "closure" not a "function") Review this without whitespace -- I ended up reformatting `extract_callable_info` because some pesky `//` comments were keeping the let-chains from being formatted.
2 parents 379b221 + cbacb6d commit c064f6e

File tree

9 files changed

+148
-139
lines changed

9 files changed

+148
-139
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+8-47
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use rustc_span::symbol::{Ident, kw, sym};
3131
use rustc_span::{
3232
DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance,
3333
};
34+
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
3435
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
3536
use rustc_trait_selection::infer::InferCtxtExt;
3637
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -45,50 +46,6 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
4546
use crate::{Expectation, FnCtxt};
4647

4748
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
48-
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
49-
let tcx = self.tcx;
50-
match ty.kind() {
51-
// Not all of these (e.g., unsafe fns) implement `FnOnce`,
52-
// so we look for these beforehand.
53-
// FIXME(async_closures): These don't impl `FnOnce` by default.
54-
ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(..) => true,
55-
// If it's not a simple function, look for things which implement `FnOnce`.
56-
_ => {
57-
let Some(fn_once) = tcx.lang_items().fn_once_trait() else {
58-
return false;
59-
};
60-
61-
// This conditional prevents us from asking to call errors and unresolved types.
62-
// It might seem that we can use `predicate_must_hold_modulo_regions`,
63-
// but since a Dummy binder is used to fill in the FnOnce trait's arguments,
64-
// type resolution always gives a "maybe" here.
65-
if self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
66-
info!("check deref {:?} error", ty);
67-
matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
68-
}) {
69-
return false;
70-
}
71-
72-
self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
73-
info!("check deref {:?} impl FnOnce", ty);
74-
self.probe(|_| {
75-
let trait_ref =
76-
ty::TraitRef::new(tcx, fn_once, [ty, self.next_ty_var(span)]);
77-
let poly_trait_ref = ty::Binder::dummy(trait_ref);
78-
let obligation = Obligation::misc(
79-
tcx,
80-
span,
81-
self.body_id,
82-
self.param_env,
83-
poly_trait_ref,
84-
);
85-
self.predicate_may_hold(&obligation)
86-
})
87-
})
88-
}
89-
}
90-
}
91-
9249
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
9350
self.autoderef(span, ty)
9451
.silence_errors()
@@ -2367,12 +2324,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23672324
let is_accessible = field.vis.is_accessible_from(scope, tcx);
23682325

23692326
if is_accessible {
2370-
if self.is_fn_ty(field_ty, span) {
2327+
if let Some((what, _, _)) = self.extract_callable_info(field_ty) {
2328+
let what = match what {
2329+
DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id),
2330+
DefIdOrName::Name(what) => what,
2331+
};
23712332
let expr_span = expr.span.to(item_name.span);
23722333
err.multipart_suggestion(
23732334
format!(
2374-
"to call the function stored in `{item_name}`, \
2375-
surround the field access with parentheses",
2335+
"to call the {what} stored in `{item_name}`, \
2336+
surround the field access with parentheses",
23762337
),
23772338
vec![
23782339
(expr_span.shrink_to_lo(), '('.to_string()),

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

+97-80
Original file line numberDiff line numberDiff line change
@@ -1075,93 +1075,110 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
10751075
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
10761076
// Autoderef is useful here because sometimes we box callables, etc.
10771077
let Some((def_id_or_name, output, inputs)) =
1078-
(self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
1079-
match *found.kind() {
1080-
ty::FnPtr(sig_tys, _) => Some((
1081-
DefIdOrName::Name("function pointer"),
1082-
sig_tys.output(),
1083-
sig_tys.inputs(),
1084-
)),
1085-
ty::FnDef(def_id, _) => {
1086-
let fn_sig = found.fn_sig(self.tcx);
1087-
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
1088-
}
1089-
ty::Closure(def_id, args) => {
1090-
let fn_sig = args.as_closure().sig();
1091-
Some((
1092-
DefIdOrName::DefId(def_id),
1093-
fn_sig.output(),
1094-
fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
1095-
))
1096-
}
1097-
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
1098-
self.tcx
1099-
.item_super_predicates(def_id)
1100-
.instantiate(self.tcx, args)
1101-
.iter()
1102-
.find_map(|pred| {
1103-
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
1104-
&& self.tcx.is_lang_item(proj.projection_term.def_id,LangItem::FnOnceOutput)
1105-
// args tuple will always be args[1]
1106-
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
1107-
{
1108-
Some((
1109-
DefIdOrName::DefId(def_id),
1110-
pred.kind().rebind(proj.term.expect_type()),
1111-
pred.kind().rebind(args.as_slice()),
1112-
))
1113-
} else {
1114-
None
1115-
}
1116-
})
1117-
}
1118-
ty::Dynamic(data, _, ty::Dyn) => {
1119-
data.iter().find_map(|pred| {
1120-
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
1078+
(self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {
1079+
ty::FnPtr(sig_tys, _) => Some((
1080+
DefIdOrName::Name("function pointer"),
1081+
sig_tys.output(),
1082+
sig_tys.inputs(),
1083+
)),
1084+
ty::FnDef(def_id, _) => {
1085+
let fn_sig = found.fn_sig(self.tcx);
1086+
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
1087+
}
1088+
ty::Closure(def_id, args) => {
1089+
let fn_sig = args.as_closure().sig();
1090+
Some((
1091+
DefIdOrName::DefId(def_id),
1092+
fn_sig.output(),
1093+
fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
1094+
))
1095+
}
1096+
ty::CoroutineClosure(def_id, args) => {
1097+
let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();
1098+
Some((
1099+
DefIdOrName::DefId(def_id),
1100+
sig_parts.map_bound(|sig| {
1101+
sig.to_coroutine(
1102+
self.tcx,
1103+
args.as_coroutine_closure().parent_args(),
1104+
// Just use infer vars here, since we don't really care
1105+
// what these types are, just that we're returning a coroutine.
1106+
self.next_ty_var(DUMMY_SP),
1107+
self.tcx.coroutine_for_closure(def_id),
1108+
self.next_ty_var(DUMMY_SP),
1109+
)
1110+
}),
1111+
sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),
1112+
))
1113+
}
1114+
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
1115+
.tcx
1116+
.item_super_predicates(def_id)
1117+
.instantiate(self.tcx, args)
1118+
.iter()
1119+
.find_map(|pred| {
1120+
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
1121+
&& self
1122+
.tcx
1123+
.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
1124+
// args tuple will always be args[1]
1125+
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
1126+
{
1127+
Some((
1128+
DefIdOrName::DefId(def_id),
1129+
pred.kind().rebind(proj.term.expect_type()),
1130+
pred.kind().rebind(args.as_slice()),
1131+
))
1132+
} else {
1133+
None
1134+
}
1135+
}),
1136+
ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| {
1137+
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
11211138
&& self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)
11221139
// for existential projection, args are shifted over by 1
11231140
&& let ty::Tuple(args) = proj.args.type_at(0).kind()
1124-
{
1125-
Some((
1126-
DefIdOrName::Name("trait object"),
1127-
pred.rebind(proj.term.expect_type()),
1128-
pred.rebind(args.as_slice()),
1129-
))
1130-
} else {
1131-
None
1132-
}
1133-
})
1141+
{
1142+
Some((
1143+
DefIdOrName::Name("trait object"),
1144+
pred.rebind(proj.term.expect_type()),
1145+
pred.rebind(args.as_slice()),
1146+
))
1147+
} else {
1148+
None
11341149
}
1135-
ty::Param(param) => {
1136-
let generics = self.tcx.generics_of(body_id);
1137-
let name = if generics.count() > param.index as usize
1138-
&& let def = generics.param_at(param.index as usize, self.tcx)
1139-
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
1140-
&& def.name == param.name
1150+
}),
1151+
ty::Param(param) => {
1152+
let generics = self.tcx.generics_of(body_id);
1153+
let name = if generics.count() > param.index as usize
1154+
&& let def = generics.param_at(param.index as usize, self.tcx)
1155+
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
1156+
&& def.name == param.name
1157+
{
1158+
DefIdOrName::DefId(def.def_id)
1159+
} else {
1160+
DefIdOrName::Name("type parameter")
1161+
};
1162+
param_env.caller_bounds().iter().find_map(|pred| {
1163+
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
1164+
&& self
1165+
.tcx
1166+
.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
1167+
&& proj.projection_term.self_ty() == found
1168+
// args tuple will always be args[1]
1169+
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
11411170
{
1142-
DefIdOrName::DefId(def.def_id)
1171+
Some((
1172+
name,
1173+
pred.kind().rebind(proj.term.expect_type()),
1174+
pred.kind().rebind(args.as_slice()),
1175+
))
11431176
} else {
1144-
DefIdOrName::Name("type parameter")
1145-
};
1146-
param_env.caller_bounds().iter().find_map(|pred| {
1147-
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
1148-
&& self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
1149-
&& proj.projection_term.self_ty() == found
1150-
// args tuple will always be args[1]
1151-
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
1152-
{
1153-
Some((
1154-
name,
1155-
pred.kind().rebind(proj.term.expect_type()),
1156-
pred.kind().rebind(args.as_slice()),
1157-
))
1158-
} else {
1159-
None
1160-
}
1161-
})
1162-
}
1163-
_ => None,
1177+
None
1178+
}
1179+
})
11641180
}
1181+
_ => None,
11651182
})
11661183
else {
11671184
return None;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Ensure we give the right args when we suggest calling a closure.
2+
3+
fn main() {
4+
let x = |a: i32, b: i32| a + b;
5+
let y: i32 = x;
6+
//~^ ERROR mismatched types
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/correct-args-on-call-suggestion.rs:5:18
3+
|
4+
LL | let x = |a: i32, b: i32| a + b;
5+
| ---------------- the found closure
6+
LL | let y: i32 = x;
7+
| --- ^ expected `i32`, found closure
8+
| |
9+
| expected due to this
10+
|
11+
= note: expected type `i32`
12+
found closure `{closure@$DIR/correct-args-on-call-suggestion.rs:4:13: 4:29}`
13+
help: use parentheses to call this closure
14+
|
15+
LL | let y: i32 = x(/* i32 */, /* i32 */);
16+
| ++++++++++++++++++++++
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.

tests/ui/confuse-field-and-method/issue-18343.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnMut() -> u32 {
77
LL | o.closure();
88
| ^^^^^^^ field, not a method
99
|
10-
help: to call the function stored in `closure`, surround the field access with parentheses
10+
help: to call the closure stored in `closure`, surround the field access with parentheses
1111
|
1212
LL | (o.closure)();
1313
| + +

tests/ui/confuse-field-and-method/issue-2392.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
77
LL | o_closure.closure();
88
| ^^^^^^^ field, not a method
99
|
10-
help: to call the function stored in `closure`, surround the field access with parentheses
10+
help: to call the closure stored in `closure`, surround the field access with parentheses
1111
|
1212
LL | (o_closure.closure)();
1313
| + +
@@ -46,7 +46,7 @@ LL | struct BoxedObj {
4646
LL | boxed_fn.boxed_closure();
4747
| ^^^^^^^^^^^^^ field, not a method
4848
|
49-
help: to call the function stored in `boxed_closure`, surround the field access with parentheses
49+
help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
5050
|
5151
LL | (boxed_fn.boxed_closure)();
5252
| + +
@@ -60,7 +60,7 @@ LL | struct BoxedObj {
6060
LL | boxed_closure.boxed_closure();
6161
| ^^^^^^^^^^^^^ field, not a method
6262
|
63-
help: to call the function stored in `boxed_closure`, surround the field access with parentheses
63+
help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
6464
|
6565
LL | (boxed_closure.boxed_closure)();
6666
| + +
@@ -99,7 +99,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
9999
LL | check_expression().closure();
100100
| ^^^^^^^ field, not a method
101101
|
102-
help: to call the function stored in `closure`, surround the field access with parentheses
102+
help: to call the trait object stored in `closure`, surround the field access with parentheses
103103
|
104104
LL | (check_expression().closure)();
105105
| + +
@@ -113,7 +113,7 @@ LL | struct FuncContainer {
113113
LL | (*self.container).f1(1);
114114
| ^^ field, not a method
115115
|
116-
help: to call the function stored in `f1`, surround the field access with parentheses
116+
help: to call the function pointer stored in `f1`, surround the field access with parentheses
117117
|
118118
LL | ((*self.container).f1)(1);
119119
| + +
@@ -127,7 +127,7 @@ LL | struct FuncContainer {
127127
LL | (*self.container).f2(1);
128128
| ^^ field, not a method
129129
|
130-
help: to call the function stored in `f2`, surround the field access with parentheses
130+
help: to call the function pointer stored in `f2`, surround the field access with parentheses
131131
|
132132
LL | ((*self.container).f2)(1);
133133
| + +
@@ -141,7 +141,7 @@ LL | struct FuncContainer {
141141
LL | (*self.container).f3(1);
142142
| ^^ field, not a method
143143
|
144-
help: to call the function stored in `f3`, surround the field access with parentheses
144+
help: to call the function pointer stored in `f3`, surround the field access with parentheses
145145
|
146146
LL | ((*self.container).f3)(1);
147147
| + +

tests/ui/confuse-field-and-method/issue-32128.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | struct Example {
77
LL | demo.example(1);
88
| ^^^^^^^ field, not a method
99
|
10-
help: to call the function stored in `example`, surround the field access with parentheses
10+
help: to call the trait object stored in `example`, surround the field access with parentheses
1111
|
1212
LL | (demo.example)(1);
1313
| + +

0 commit comments

Comments
 (0)