From 2f78dd15a6f29894e5876582cc84e8a5faa539c1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 23:08:21 +0000 Subject: [PATCH] Suggest calling trait objects and parameters too, when possible --- .../src/check/fn_ctxt/suggestions.rs | 75 ++++++++++++++----- .../substs-ppaux.normal.stderr | 6 +- .../substs-ppaux.verbose.stderr | 6 +- src/test/ui/fn/fn-trait-formatting.stderr | 8 ++ .../suggest-calling-rpit-closure.stderr | 2 +- src/test/ui/suggestions/call-on-missing.rs | 20 +++++ .../ui/suggestions/call-on-missing.stderr | 51 ++++++++++++- .../fn-or-tuple-struct-without-args.stderr | 16 ++-- 8 files changed, 151 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 45872a0b07a..4eb0f045d77 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -2,6 +2,7 @@ use super::FnCtxt; use crate::astconv::AstConv; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use hir::def_id::DefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; @@ -75,38 +76,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { + enum DefIdOrName { + DefId(DefId), + Name(&'static str), + } // 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() { - 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, _) => { 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) => { 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) => { - 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() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() // 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 } - }); - if let Some((output, inputs)) = sig { - Some((Some(def_id), output, inputs)) - } else { - None - } + }) + } + ty::Dynamic(data, _) => { + data.iter().find_map(|pred| { + 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(¶m, 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, } @@ -128,12 +166,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => ("...".to_string(), Applicability::HasPlaceholders), }; - let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) { - Some(DefKind::Fn) => "call this function", - Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure", - Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct", - Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant", - _ => "call this function", + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => { + "instantiate this tuple variant".to_string() + } + kind => format!("call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("call this {name}"), }; let sugg = match expr.kind { diff --git a/src/test/ui/associated-types/substs-ppaux.normal.stderr b/src/test/ui/associated-types/substs-ppaux.normal.stderr index 501d2cfaa26..3f180cf4f1f 100644 --- a/src/test/ui/associated-types/substs-ppaux.normal.stderr +++ b/src/test/ui/associated-types/substs-ppaux.normal.stderr @@ -11,7 +11,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -29,7 +29,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -47,7 +47,7 @@ LL | let x: () = >::baz; | = note: expected unit type `()` found fn item `fn() {>::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::baz(); | ++ diff --git a/src/test/ui/associated-types/substs-ppaux.verbose.stderr b/src/test/ui/associated-types/substs-ppaux.verbose.stderr index ae3e862dddd..16dd29de2c5 100644 --- a/src/test/ui/associated-types/substs-ppaux.verbose.stderr +++ b/src/test/ui/associated-types/substs-ppaux.verbose.stderr @@ -11,7 +11,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -29,7 +29,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -47,7 +47,7 @@ LL | let x: () = >::baz; | = note: expected unit type `()` found fn item `fn() {>::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::baz(); | ++ diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr index ea88e401bed..1d5e0a859a6 100644 --- a/src/test/ui/fn/fn-trait-formatting.stderr +++ b/src/test/ui/fn/fn-trait-formatting.stderr @@ -8,6 +8,10 @@ LL | let _: () = Box::new(|_: isize| {}) as Box; | = note: expected unit type `()` found struct `Box` +help: use parentheses to call this trait object + | +LL | let _: () = (Box::new(|_: isize| {}) as Box)(_); + | + ++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:10:17 @@ -19,6 +23,10 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box | = note: expected unit type `()` found struct `Box` +help: use parentheses to call this trait object + | +LL | let _: () = (Box::new(|_: isize, isize| {}) as Box)(_, _); + | + +++++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:14:17 diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr index 2a328a0e6f5..c10a856d83b 100644 --- a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr +++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr @@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 { | = note: expected type `i32` found opaque type `impl Fn() -> i32` -help: use parentheses to call this closure +help: use parentheses to call this opaque type | LL | opaque()() | ++ diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs index 611b9d40f0f..25ced84dd37 100644 --- a/src/test/ui/suggestions/call-on-missing.rs +++ b/src/test/ui/suggestions/call-on-missing.rs @@ -16,4 +16,24 @@ fn main() { foo.i; //~^ ERROR no field `i` //~| HELP use parentheses to call this function + + let callable = Box::new(|| Foo { i: 1 }) as Box 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 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 } diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr index d8070321e14..ca9abc7e906 100644 --- a/src/test/ui/suggestions/call-on-missing.stderr +++ b/src/test/ui/suggestions/call-on-missing.stderr @@ -20,7 +20,56 @@ help: use parentheses to call this function LL | foo().i; | ++ -error: aborting due to 2 previous errors +error[E0599]: no method named `bar` found for struct `Box Foo>` in the current scope + --> $DIR/call-on-missing.rs:22:14 + | +LL | callable.bar(); + | ^^^ method not found in `Box Foo>` + | +help: use parentheses to call this trait object + | +LL | callable().bar(); + | ++ + +error[E0609]: no field `i` on type `Box 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 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 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. For more information about an error, try `rustc --explain E0599`. diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index e75ce0da82e..ba710bfa746 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -103,7 +103,7 @@ LL | let _: usize = T::baz; | = note: expected type `usize` 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(_, _); | ++++++ @@ -121,7 +121,7 @@ LL | let _: usize = T::bat; | = note: expected type `usize` 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(_); | +++ @@ -157,7 +157,7 @@ LL | let _: usize = X::baz; | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::baz(_, _); | ++++++ @@ -175,7 +175,7 @@ LL | let _: usize = X::bat; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bat}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bat(_); | +++ @@ -193,7 +193,7 @@ LL | let _: usize = X::bax; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bax}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bax(_); | +++ @@ -211,7 +211,7 @@ LL | let _: usize = X::bach; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bach}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bach(_); | +++ @@ -229,7 +229,7 @@ LL | let _: usize = X::ban; | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::ban}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::ban(_); | +++ @@ -247,7 +247,7 @@ LL | let _: usize = X::bal; | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::bal}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bal(_); | +++