Skip to content

Commit 2f78dd1

Browse files
Suggest calling trait objects and parameters too, when possible
1 parent cef0482 commit 2f78dd1

File tree

8 files changed

+151
-33
lines changed

8 files changed

+151
-33
lines changed

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

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::FnCtxt;
22
use crate::astconv::AstConv;
33
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
44

5+
use hir::def_id::DefId;
56
use rustc_ast::util::parser::ExprPrecedence;
67
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
78
use rustc_hir as hir;
@@ -75,38 +76,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7576
found: Ty<'tcx>,
7677
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
7778
) -> bool {
79+
enum DefIdOrName {
80+
DefId(DefId),
81+
Name(&'static str),
82+
}
7883
// Autoderef is useful here because sometimes we box callables, etc.
79-
let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
84+
let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
8085
match *found.kind() {
81-
ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())),
86+
ty::FnPtr(fn_sig) =>
87+
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())),
8288
ty::FnDef(def_id, _) => {
8389
let fn_sig = found.fn_sig(self.tcx);
84-
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
90+
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
8591
}
8692
ty::Closure(def_id, substs) => {
8793
let fn_sig = substs.as_closure().sig();
88-
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
94+
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
8995
}
9096
ty::Opaque(def_id, substs) => {
91-
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
97+
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
9298
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
9399
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
94100
// args tuple will always be substs[1]
95101
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
96102
{
97103
Some((
104+
DefIdOrName::DefId(def_id),
98105
pred.kind().rebind(proj.term.ty().unwrap()),
99106
args.len(),
100107
))
101108
} else {
102109
None
103110
}
104-
});
105-
if let Some((output, inputs)) = sig {
106-
Some((Some(def_id), output, inputs))
107-
} else {
108-
None
109-
}
111+
})
112+
}
113+
ty::Dynamic(data, _) => {
114+
data.iter().find_map(|pred| {
115+
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
116+
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
117+
// for existential projection, substs are shifted over by 1
118+
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
119+
{
120+
Some((
121+
DefIdOrName::Name("trait object"),
122+
pred.rebind(proj.term.ty().unwrap()),
123+
args.len(),
124+
))
125+
} else {
126+
None
127+
}
128+
})
129+
}
130+
ty::Param(param) => {
131+
let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
132+
self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
133+
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
134+
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
135+
&& proj.projection_ty.self_ty() == found
136+
// args tuple will always be substs[1]
137+
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
138+
{
139+
Some((
140+
DefIdOrName::DefId(def_id),
141+
pred.kind().rebind(proj.term.ty().unwrap()),
142+
args.len(),
143+
))
144+
} else {
145+
None
146+
}
147+
})
110148
}
111149
_ => None,
112150
}
@@ -128,12 +166,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
128166
_ => ("...".to_string(), Applicability::HasPlaceholders),
129167
};
130168

131-
let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) {
132-
Some(DefKind::Fn) => "call this function",
133-
Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure",
134-
Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct",
135-
Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant",
136-
_ => "call this function",
169+
let msg = match def_id_or_name {
170+
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
171+
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
172+
DefKind::Ctor(CtorOf::Variant, _) => {
173+
"instantiate this tuple variant".to_string()
174+
}
175+
kind => format!("call this {}", kind.descr(def_id)),
176+
},
177+
DefIdOrName::Name(name) => format!("call this {name}"),
137178
};
138179

139180
let sugg = match expr.kind {

src/test/ui/associated-types/substs-ppaux.normal.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
1111
|
1212
= note: expected unit type `()`
1313
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
14-
help: use parentheses to call this function
14+
help: use parentheses to call this associated function
1515
|
1616
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
1717
| ++
@@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
2929
|
3030
= note: expected unit type `()`
3131
found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
32-
help: use parentheses to call this function
32+
help: use parentheses to call this associated function
3333
|
3434
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
3535
| ++
@@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
4747
|
4848
= note: expected unit type `()`
4949
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
50-
help: use parentheses to call this function
50+
help: use parentheses to call this associated function
5151
|
5252
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
5353
| ++

src/test/ui/associated-types/substs-ppaux.verbose.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
1111
|
1212
= note: expected unit type `()`
1313
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
14-
help: use parentheses to call this function
14+
help: use parentheses to call this associated function
1515
|
1616
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
1717
| ++
@@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
2929
|
3030
= note: expected unit type `()`
3131
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
32-
help: use parentheses to call this function
32+
help: use parentheses to call this associated function
3333
|
3434
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
3535
| ++
@@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
4747
|
4848
= note: expected unit type `()`
4949
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
50-
help: use parentheses to call this function
50+
help: use parentheses to call this associated function
5151
|
5252
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
5353
| ++

src/test/ui/fn/fn-trait-formatting.stderr

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
88
|
99
= note: expected unit type `()`
1010
found struct `Box<dyn FnOnce(isize)>`
11+
help: use parentheses to call this trait object
12+
|
13+
LL | let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(_);
14+
| + ++++
1115

1216
error[E0308]: mismatched types
1317
--> $DIR/fn-trait-formatting.rs:10:17
@@ -19,6 +23,10 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
1923
|
2024
= note: expected unit type `()`
2125
found struct `Box<dyn Fn(isize, isize)>`
26+
help: use parentheses to call this trait object
27+
|
28+
LL | let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(_, _);
29+
| + +++++++
2230

2331
error[E0308]: mismatched types
2432
--> $DIR/fn-trait-formatting.rs:14:17

src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
1111
|
1212
= note: expected type `i32`
1313
found opaque type `impl Fn() -> i32`
14-
help: use parentheses to call this closure
14+
help: use parentheses to call this opaque type
1515
|
1616
LL | opaque()()
1717
| ++

src/test/ui/suggestions/call-on-missing.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,24 @@ fn main() {
1616
foo.i;
1717
//~^ ERROR no field `i`
1818
//~| HELP use parentheses to call this function
19+
20+
let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
21+
22+
callable.bar();
23+
//~^ ERROR no method named `bar`
24+
//~| HELP use parentheses to call this trait object
25+
26+
callable.i;
27+
//~^ ERROR no field `i`
28+
//~| HELP use parentheses to call this trait object
29+
}
30+
31+
fn type_param<T: Fn() -> Foo>(t: T) {
32+
t.bar();
33+
//~^ ERROR no method named `bar`
34+
//~| HELP use parentheses to call this type parameter
35+
36+
t.i;
37+
//~^ ERROR no field `i`
38+
//~| HELP use parentheses to call this type parameter
1939
}

src/test/ui/suggestions/call-on-missing.stderr

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,56 @@ help: use parentheses to call this function
2020
LL | foo().i;
2121
| ++
2222

23-
error: aborting due to 2 previous errors
23+
error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
24+
--> $DIR/call-on-missing.rs:22:14
25+
|
26+
LL | callable.bar();
27+
| ^^^ method not found in `Box<dyn Fn() -> Foo>`
28+
|
29+
help: use parentheses to call this trait object
30+
|
31+
LL | callable().bar();
32+
| ++
33+
34+
error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
35+
--> $DIR/call-on-missing.rs:26:14
36+
|
37+
LL | callable.i;
38+
| ^ unknown field
39+
|
40+
help: use parentheses to call this trait object
41+
|
42+
LL | callable().i;
43+
| ++
44+
45+
error[E0599]: no method named `bar` found for type parameter `T` in the current scope
46+
--> $DIR/call-on-missing.rs:32:7
47+
|
48+
LL | fn type_param<T: Fn() -> Foo>(t: T) {
49+
| - method `bar` not found for this type parameter
50+
LL | t.bar();
51+
| ^^^ method not found in `T`
52+
|
53+
help: use parentheses to call this type parameter
54+
|
55+
LL | t().bar();
56+
| ++
57+
58+
error[E0609]: no field `i` on type `T`
59+
--> $DIR/call-on-missing.rs:36:7
60+
|
61+
LL | fn type_param<T: Fn() -> Foo>(t: T) {
62+
| - type parameter 'T' declared here
63+
...
64+
LL | t.i;
65+
| ^
66+
|
67+
help: use parentheses to call this type parameter
68+
|
69+
LL | t().i;
70+
| ++
71+
72+
error: aborting due to 6 previous errors
2473

2574
Some errors have detailed explanations: E0599, E0609.
2675
For more information about an error, try `rustc --explain E0599`.

src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ LL | let _: usize = T::baz;
103103
|
104104
= note: expected type `usize`
105105
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
106-
help: use parentheses to call this function
106+
help: use parentheses to call this associated function
107107
|
108108
LL | let _: usize = T::baz(_, _);
109109
| ++++++
@@ -121,7 +121,7 @@ LL | let _: usize = T::bat;
121121
|
122122
= note: expected type `usize`
123123
found fn item `fn(usize) -> usize {<_ as T>::bat}`
124-
help: use parentheses to call this function
124+
help: use parentheses to call this associated function
125125
|
126126
LL | let _: usize = T::bat(_);
127127
| +++
@@ -157,7 +157,7 @@ LL | let _: usize = X::baz;
157157
|
158158
= note: expected type `usize`
159159
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
160-
help: use parentheses to call this function
160+
help: use parentheses to call this associated function
161161
|
162162
LL | let _: usize = X::baz(_, _);
163163
| ++++++
@@ -175,7 +175,7 @@ LL | let _: usize = X::bat;
175175
|
176176
= note: expected type `usize`
177177
found fn item `fn(usize) -> usize {<X as T>::bat}`
178-
help: use parentheses to call this function
178+
help: use parentheses to call this associated function
179179
|
180180
LL | let _: usize = X::bat(_);
181181
| +++
@@ -193,7 +193,7 @@ LL | let _: usize = X::bax;
193193
|
194194
= note: expected type `usize`
195195
found fn item `fn(usize) -> usize {<X as T>::bax}`
196-
help: use parentheses to call this function
196+
help: use parentheses to call this associated function
197197
|
198198
LL | let _: usize = X::bax(_);
199199
| +++
@@ -211,7 +211,7 @@ LL | let _: usize = X::bach;
211211
|
212212
= note: expected type `usize`
213213
found fn item `fn(usize) -> usize {<X as T>::bach}`
214-
help: use parentheses to call this function
214+
help: use parentheses to call this associated function
215215
|
216216
LL | let _: usize = X::bach(_);
217217
| +++
@@ -229,7 +229,7 @@ LL | let _: usize = X::ban;
229229
|
230230
= note: expected type `usize`
231231
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
232-
help: use parentheses to call this function
232+
help: use parentheses to call this associated function
233233
|
234234
LL | let _: usize = X::ban(_);
235235
| +++
@@ -247,7 +247,7 @@ LL | let _: usize = X::bal;
247247
|
248248
= note: expected type `usize`
249249
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
250-
help: use parentheses to call this function
250+
help: use parentheses to call this associated function
251251
|
252252
LL | let _: usize = X::bal(_);
253253
| +++

0 commit comments

Comments
 (0)