about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-08-27 23:08:21 +0000
committerMichael Goulet <michael@errs.io>2022-08-27 23:08:44 +0000
commit2f78dd15a6f29894e5876582cc84e8a5faa539c1 (patch)
tree0118bd5d9890bef3fc6000dc3366f8edc738f33f
parentcef0482d11ec42d50611beca2dbf551094d5025c (diff)
downloadrust-2f78dd15a6f29894e5876582cc84e8a5faa539c1.tar.gz
rust-2f78dd15a6f29894e5876582cc84e8a5faa539c1.zip
Suggest calling trait objects and parameters too, when possible
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs75
-rw-r--r--src/test/ui/associated-types/substs-ppaux.normal.stderr6
-rw-r--r--src/test/ui/associated-types/substs-ppaux.verbose.stderr6
-rw-r--r--src/test/ui/fn/fn-trait-formatting.stderr8
-rw-r--r--src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr2
-rw-r--r--src/test/ui/suggestions/call-on-missing.rs20
-rw-r--r--src/test/ui/suggestions/call-on-missing.stderr51
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr16
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(&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,
             }
@@ -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: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 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>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 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>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 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();
    |                                                        ++
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: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 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>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 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>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 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();
    |                                                        ++
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<dyn FnOnce(isize)>;
    |
    = note: expected unit type `()`
                  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
   --> $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 `()`
                  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
   --> $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<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
 }
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<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.
 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 {<X as T>::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 {<X as T>::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 {<X as T>::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 {<X as T>::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 {<X as T>::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 {<X as T>::bal}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let _: usize = X::bal(_);
    |                          +++