1
Fork 0

Suggest calling trait objects and parameters too, when possible

This commit is contained in:
Michael Goulet 2022-08-27 23:08:21 +00:00
parent cef0482d11
commit 2f78dd15a6
8 changed files with 151 additions and 33 deletions

View file

@ -2,6 +2,7 @@ use super::FnCtxt;
use crate::astconv::AstConv; use crate::astconv::AstConv;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use hir::def_id::DefId;
use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
@ -75,38 +76,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
found: Ty<'tcx>, found: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool { ) -> bool {
enum DefIdOrName {
DefId(DefId),
Name(&'static str),
}
// Autoderef is useful here because sometimes we box callables, etc. // Autoderef is useful here because sometimes we box callables, etc.
let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
match *found.kind() { match *found.kind() {
ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())), ty::FnPtr(fn_sig) =>
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())),
ty::FnDef(def_id, _) => { ty::FnDef(def_id, _) => {
let fn_sig = found.fn_sig(self.tcx); let fn_sig = found.fn_sig(self.tcx);
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len())) Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
} }
ty::Closure(def_id, substs) => { ty::Closure(def_id, substs) => {
let fn_sig = substs.as_closure().sig(); let fn_sig = substs.as_closure().sig();
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)) Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
} }
ty::Opaque(def_id, substs) => { ty::Opaque(def_id, substs) => {
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1] // args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
{ {
Some(( Some((
DefIdOrName::DefId(def_id),
pred.kind().rebind(proj.term.ty().unwrap()), pred.kind().rebind(proj.term.ty().unwrap()),
args.len(), args.len(),
)) ))
} else { } else {
None None
} }
}); })
if let Some((output, inputs)) = sig { }
Some((Some(def_id), output, inputs)) ty::Dynamic(data, _) => {
} else { data.iter().find_map(|pred| {
None if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
} && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
// for existential projection, substs are shifted over by 1
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
{
Some((
DefIdOrName::Name("trait object"),
pred.rebind(proj.term.ty().unwrap()),
args.len(),
))
} else {
None
}
})
}
ty::Param(param) => {
let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
{
Some((
DefIdOrName::DefId(def_id),
pred.kind().rebind(proj.term.ty().unwrap()),
args.len(),
))
} else {
None
}
})
} }
_ => None, _ => None,
} }
@ -128,12 +166,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => ("...".to_string(), Applicability::HasPlaceholders), _ => ("...".to_string(), Applicability::HasPlaceholders),
}; };
let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) { let msg = match def_id_or_name {
Some(DefKind::Fn) => "call this function", DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure", DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct", DefKind::Ctor(CtorOf::Variant, _) => {
Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant", "instantiate this tuple variant".to_string()
_ => "call this function", }
kind => format!("call this {}", kind.descr(def_id)),
},
DefIdOrName::Name(name) => format!("call this {name}"),
}; };
let sugg = match expr.kind { let sugg = match expr.kind {

View file

@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}` found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>(); LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++ | ++
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}` found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>(); LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++ | ++
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}` found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz(); LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++ | ++

View file

@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}` found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>(); LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++ | ++
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}` found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>(); LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++ | ++
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
| |
= note: expected unit type `()` = note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}` found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz(); LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++ | ++

View file

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

View file

@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
| |
= note: expected type `i32` = note: expected type `i32`
found opaque type `impl Fn() -> i32` found opaque type `impl Fn() -> i32`
help: use parentheses to call this closure help: use parentheses to call this opaque type
| |
LL | opaque()() LL | opaque()()
| ++ | ++

View file

@ -16,4 +16,24 @@ fn main() {
foo.i; foo.i;
//~^ ERROR no field `i` //~^ ERROR no field `i`
//~| HELP use parentheses to call this function //~| HELP use parentheses to call this function
let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
callable.bar();
//~^ ERROR no method named `bar`
//~| HELP use parentheses to call this trait object
callable.i;
//~^ ERROR no field `i`
//~| HELP use parentheses to call this trait object
}
fn type_param<T: Fn() -> Foo>(t: T) {
t.bar();
//~^ ERROR no method named `bar`
//~| HELP use parentheses to call this type parameter
t.i;
//~^ ERROR no field `i`
//~| HELP use parentheses to call this type parameter
} }

View file

@ -20,7 +20,56 @@ help: use parentheses to call this function
LL | foo().i; LL | foo().i;
| ++ | ++
error: aborting due to 2 previous errors error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
--> $DIR/call-on-missing.rs:22:14
|
LL | callable.bar();
| ^^^ method not found in `Box<dyn Fn() -> Foo>`
|
help: use parentheses to call this trait object
|
LL | callable().bar();
| ++
error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
--> $DIR/call-on-missing.rs:26:14
|
LL | callable.i;
| ^ unknown field
|
help: use parentheses to call this trait object
|
LL | callable().i;
| ++
error[E0599]: no method named `bar` found for type parameter `T` in the current scope
--> $DIR/call-on-missing.rs:32:7
|
LL | fn type_param<T: Fn() -> Foo>(t: T) {
| - method `bar` not found for this type parameter
LL | t.bar();
| ^^^ method not found in `T`
|
help: use parentheses to call this type parameter
|
LL | t().bar();
| ++
error[E0609]: no field `i` on type `T`
--> $DIR/call-on-missing.rs:36:7
|
LL | fn type_param<T: Fn() -> Foo>(t: T) {
| - type parameter 'T' declared here
...
LL | t.i;
| ^
|
help: use parentheses to call this type parameter
|
LL | t().i;
| ++
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0599, E0609. Some errors have detailed explanations: E0599, E0609.
For more information about an error, try `rustc --explain E0599`. For more information about an error, try `rustc --explain E0599`.

View file

@ -103,7 +103,7 @@ LL | let _: usize = T::baz;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}` found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = T::baz(_, _); LL | let _: usize = T::baz(_, _);
| ++++++ | ++++++
@ -121,7 +121,7 @@ LL | let _: usize = T::bat;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize) -> usize {<_ as T>::bat}` found fn item `fn(usize) -> usize {<_ as T>::bat}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = T::bat(_); LL | let _: usize = T::bat(_);
| +++ | +++
@ -157,7 +157,7 @@ LL | let _: usize = X::baz;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<X as T>::baz}` found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::baz(_, _); LL | let _: usize = X::baz(_, _);
| ++++++ | ++++++
@ -175,7 +175,7 @@ LL | let _: usize = X::bat;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bat}` found fn item `fn(usize) -> usize {<X as T>::bat}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::bat(_); LL | let _: usize = X::bat(_);
| +++ | +++
@ -193,7 +193,7 @@ LL | let _: usize = X::bax;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bax}` found fn item `fn(usize) -> usize {<X as T>::bax}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::bax(_); LL | let _: usize = X::bax(_);
| +++ | +++
@ -211,7 +211,7 @@ LL | let _: usize = X::bach;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bach}` found fn item `fn(usize) -> usize {<X as T>::bach}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::bach(_); LL | let _: usize = X::bach(_);
| +++ | +++
@ -229,7 +229,7 @@ LL | let _: usize = X::ban;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}` found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::ban(_); LL | let _: usize = X::ban(_);
| +++ | +++
@ -247,7 +247,7 @@ LL | let _: usize = X::bal;
| |
= note: expected type `usize` = note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}` found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
help: use parentheses to call this function help: use parentheses to call this associated function
| |
LL | let _: usize = X::bal(_); LL | let _: usize = X::bal(_);
| +++ | +++