about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2022-06-24 16:43:46 +0900
committerGitHub <noreply@github.com>2022-06-24 16:43:46 +0900
commit964fc41b8986c3631e85decbadea3f9f40425811 (patch)
treeecb256d5d7b511b0a1953e524f27588449c8bf21
parent33eb3c05c54b306afea341dd233671a9f039156f (diff)
parentd15fed79b84c621b42699442ccf99563a6bc6881 (diff)
downloadrust-964fc41b8986c3631e85decbadea3f9f40425811.tar.gz
rust-964fc41b8986c3631e85decbadea3f9f40425811.zip
Rollup merge of #98280 - compiler-errors:better-call-closure-on-type-err, r=estebank
Improve suggestion for calling fn-like expr on type mismatch

1.) Suggest calling values of with RPIT types (and probably TAIT) when we expect `Ty` and have `impl Fn() -> Ty`
2.) Suggest calling closures even when they're not assigned to a local variable first
3.) Drive-by fix of a pretty-printing bug (`impl Fn()-> Ty` => `impl Fn() -> Ty`)

r? ```@estebank```
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs189
-rw-r--r--src/test/ui/impl-trait/suggest-calling-rpit-closure.rs12
-rw-r--r--src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr21
-rw-r--r--src/test/ui/parser/expr-as-stmt.stderr4
-rw-r--r--src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr6
-rw-r--r--src/test/ui/reify-intrinsic.stderr4
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr8
-rw-r--r--src/test/ui/span/move-closure.stderr4
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr14
-rw-r--r--src/test/ui/type-alias-impl-trait/issue-63279.stderr4
11 files changed, 143 insertions, 125 deletions
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index bfb822fab26..200253d5755 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -856,7 +856,7 @@ pub trait PrettyPrinter<'tcx>:
                     p!(")");
                     if let Term::Ty(ty) = return_ty.skip_binder() {
                         if !ty.is_unit() {
-                            p!("-> ", print(return_ty));
+                            p!(" -> ", print(return_ty));
                         }
                     }
                     p!(write("{}", if paren_needed { ")" } else { "" }));
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 03d91435e77..7195da863db 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -8,15 +8,14 @@ use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
-    Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind,
-    WherePredicate,
+    Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
 };
 use rustc_infer::infer::{self, TyCtxtInferExt};
 use rustc_infer::traits;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
-use rustc_span::symbol::{kw, sym};
+use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 
@@ -78,124 +77,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) -> bool {
-        let hir = self.tcx.hir();
-        let (def_id, sig) = match *found.kind() {
-            ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
-            ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
+        let (def_id, output, inputs) = match *found.kind() {
+            ty::FnDef(def_id, _) => {
+                let fn_sig = found.fn_sig(self.tcx);
+                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
+            }
+            ty::Closure(def_id, substs) => {
+                let fn_sig = substs.as_closure().sig();
+                (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| {
+                    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((
+                            pred.kind().rebind(proj.term.ty().unwrap()),
+                            args.len(),
+                        ))
+                    } else {
+                        None
+                    }
+                });
+                if let Some((output, inputs)) = sig {
+                    (def_id, output, inputs)
+                } else {
+                    return false;
+                }
+            }
             _ => return false,
         };
 
-        let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, sig);
-        let sig = self.normalize_associated_types_in(expr.span, sig);
-        if self.can_coerce(sig.output(), expected) {
-            let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
-                (String::new(), Applicability::MachineApplicable)
-            } else {
-                ("...".to_string(), Applicability::HasPlaceholders)
+        let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
+        let output = self.normalize_associated_types_in(expr.span, output);
+        if !output.is_ty_var() && self.can_coerce(output, expected) {
+            let (sugg_call, mut applicability) = match inputs {
+                0 => ("".to_string(), Applicability::MachineApplicable),
+                1..=4 => (
+                    (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
+                    Applicability::MachineApplicable,
+                ),
+                _ => ("...".to_string(), Applicability::HasPlaceholders),
             };
-            let mut msg = "call this function";
-            match hir.get_if_local(def_id) {
-                Some(
-                    Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
-                    | Node::ImplItem(hir::ImplItem {
-                        kind: hir::ImplItemKind::Fn(_, body_id), ..
-                    })
-                    | Node::TraitItem(hir::TraitItem {
-                        kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
-                        ..
-                    }),
-                ) => {
-                    let body = hir.body(*body_id);
-                    sugg_call = body
-                        .params
-                        .iter()
-                        .map(|param| match &param.pat.kind {
-                            hir::PatKind::Binding(_, _, ident, None)
-                                if ident.name != kw::SelfLower =>
-                            {
-                                ident.to_string()
-                            }
-                            _ => "_".to_string(),
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ");
-                }
-                Some(Node::Expr(hir::Expr {
-                    kind: ExprKind::Closure { body: body_id, .. },
-                    span: full_closure_span,
-                    ..
-                })) => {
-                    if *full_closure_span == expr.span {
-                        return false;
-                    }
-                    msg = "call this closure";
-                    let body = hir.body(*body_id);
-                    sugg_call = body
-                        .params
-                        .iter()
-                        .map(|param| match &param.pat.kind {
-                            hir::PatKind::Binding(_, _, ident, None)
-                                if ident.name != kw::SelfLower =>
-                            {
-                                ident.to_string()
-                            }
-                            _ => "_".to_string(),
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ");
-                }
-                Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
-                    sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
-                    match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) {
-                        Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
-                            msg = "instantiate this tuple variant";
-                        }
-                        Some(DefKind::Ctor(CtorOf::Struct, _)) => {
-                            msg = "instantiate this tuple struct";
-                        }
-                        _ => {}
-                    }
+
+            let msg = match self.tcx.def_kind(def_id) {
+                DefKind::Fn => "call this function",
+                DefKind::Closure | DefKind::OpaqueTy => "call this closure",
+                DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
+                DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
+                _ => "call this function",
+            };
+
+            let sugg = match expr.kind {
+                hir::ExprKind::Call(..)
+                | hir::ExprKind::Path(..)
+                | hir::ExprKind::Index(..)
+                | hir::ExprKind::Lit(..) => {
+                    vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
                 }
-                Some(Node::ForeignItem(hir::ForeignItem {
-                    kind: hir::ForeignItemKind::Fn(_, idents, _),
-                    ..
-                })) => {
-                    sugg_call = idents
-                        .iter()
-                        .map(|ident| {
-                            if ident.name != kw::SelfLower {
-                                ident.to_string()
-                            } else {
-                                "_".to_string()
-                            }
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ")
+                hir::ExprKind::Closure { .. } => {
+                    // Might be `{ expr } || { bool }`
+                    applicability = Applicability::MaybeIncorrect;
+                    vec![
+                        (expr.span.shrink_to_lo(), "(".to_string()),
+                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                    ]
                 }
-                Some(Node::TraitItem(hir::TraitItem {
-                    kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
-                    ..
-                })) => {
-                    sugg_call = idents
-                        .iter()
-                        .map(|ident| {
-                            if ident.name != kw::SelfLower {
-                                ident.to_string()
-                            } else {
-                                "_".to_string()
-                            }
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", ")
+                _ => {
+                    vec![
+                        (expr.span.shrink_to_lo(), "(".to_string()),
+                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                    ]
                 }
-                _ => {}
-            }
-            err.span_suggestion_verbose(
-                expr.span.shrink_to_hi(),
-                &format!("use parentheses to {}", msg),
-                format!("({})", sugg_call),
+            };
+
+            err.multipart_suggestion_verbose(
+                format!("use parentheses to {msg}"),
+                sugg,
                 applicability,
             );
+
             return true;
         }
         false
diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs b/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
new file mode 100644
index 00000000000..640156291a3
--- /dev/null
+++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
@@ -0,0 +1,12 @@
+fn whatever() -> i32 {
+    opaque()
+//~^ ERROR mismatched types
+}
+
+fn opaque() -> impl Fn() -> i32 {
+    || 0
+}
+
+fn main() {
+    let _ = whatever();
+}
diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
new file mode 100644
index 00000000000..2a328a0e6f5
--- /dev/null
+++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+  --> $DIR/suggest-calling-rpit-closure.rs:2:5
+   |
+LL | fn whatever() -> i32 {
+   |                  --- expected `i32` because of return type
+LL |     opaque()
+   |     ^^^^^^^^ expected `i32`, found opaque type
+...
+LL | fn opaque() -> impl Fn() -> i32 {
+   |                ---------------- the found opaque type
+   |
+   = note:     expected type `i32`
+           found opaque type `impl Fn() -> i32`
+help: use parentheses to call this closure
+   |
+LL |     opaque()()
+   |             ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/expr-as-stmt.stderr b/src/test/ui/parser/expr-as-stmt.stderr
index 8eb81301bc3..d4f64a7de5b 100644
--- a/src/test/ui/parser/expr-as-stmt.stderr
+++ b/src/test/ui/parser/expr-as-stmt.stderr
@@ -201,6 +201,10 @@ LL |     { true } || { true }
    |
    = note: expected type `bool`
            found closure `[closure@$DIR/expr-as-stmt.rs:51:14: 51:25]`
+help: use parentheses to call this closure
+   |
+LL |     { true } (|| { true })()
+   |              +           +++
 help: parentheses are required to parse this as an expression
    |
 LL |     ({ true }) || { true }
diff --git a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr
index b948ab2abb5..e71f15ebfd2 100644
--- a/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr
+++ b/src/test/ui/parser/struct-literal-restrictions-in-lamda.stderr
@@ -25,6 +25,12 @@ LL | |     }.hi() {
    |
    = note: expected type `bool`
            found closure `[closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 14:11]`
+help: use parentheses to call this closure
+   |
+LL ~     while (|| Foo {
+LL |         x: 3
+LL ~     }.hi())() {
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr
index 70a64446f6a..360557fb520 100644
--- a/src/test/ui/reify-intrinsic.stderr
+++ b/src/test/ui/reify-intrinsic.stderr
@@ -8,10 +8,6 @@ LL |     let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr
    |
    = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize`
                  found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
-help: use parentheses to call this function
-   |
-LL |     let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute(...);
-   |                                                                                   +++++
 
 error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
   --> $DIR/reify-intrinsic.rs:11:13
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
index dfc7fdc1ed2..f7f39bd0b9a 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -1118,6 +1118,10 @@ LL |     if let Range { start: F, end } = F..|| true {}
    |
    = note: expected type `bool`
            found closure `[closure@$DIR/disallowed-positions.rs:136:41: 136:48]`
+help: use parentheses to call this closure
+   |
+LL |     if let Range { start: F, end } = F..(|| true)() {}
+   |                                         +       +++
 
 error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:136:8
@@ -1314,6 +1318,10 @@ LL |     while let Range { start: F, end } = F..|| true {}
    |
    = note: expected type `bool`
            found closure `[closure@$DIR/disallowed-positions.rs:200:44: 200:51]`
+help: use parentheses to call this closure
+   |
+LL |     while let Range { start: F, end } = F..(|| true)() {}
+   |                                            +       +++
 
 error[E0308]: mismatched types
   --> $DIR/disallowed-positions.rs:200:11
diff --git a/src/test/ui/span/move-closure.stderr b/src/test/ui/span/move-closure.stderr
index ded581dc496..3e7041f02b3 100644
--- a/src/test/ui/span/move-closure.stderr
+++ b/src/test/ui/span/move-closure.stderr
@@ -8,6 +8,10 @@ LL |     let x: () = move || ();
    |
    = note: expected unit type `()`
                 found closure `[closure@$DIR/move-closure.rs:5:17: 5:27]`
+help: use parentheses to call this closure
+   |
+LL |     let x: () = (move || ())();
+   |                 +          +++
 
 error: aborting due to previous error
 
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 1ed784e8f5b..25ce458f6d8 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
@@ -33,7 +33,7 @@ LL |     let _: usize = foo;
            found fn item `fn(usize, usize) -> usize {foo}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = foo(a, b);
+LL |     let _: usize = foo(_, _);
    |                       ++++++
 
 error[E0308]: mismatched types
@@ -105,7 +105,7 @@ LL |     let _: usize = T::baz;
            found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = T::baz(x, y);
+LL |     let _: usize = T::baz(_, _);
    |                          ++++++
 
 error[E0308]: mismatched types
@@ -123,7 +123,7 @@ LL |     let _: usize = T::bat;
            found fn item `fn(usize) -> usize {<_ as T>::bat}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = T::bat(x);
+LL |     let _: usize = T::bat(_);
    |                          +++
 
 error[E0308]: mismatched types
@@ -159,7 +159,7 @@ LL |     let _: usize = X::baz;
            found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = X::baz(x, y);
+LL |     let _: usize = X::baz(_, _);
    |                          ++++++
 
 error[E0308]: mismatched types
@@ -177,7 +177,7 @@ LL |     let _: usize = X::bat;
            found fn item `fn(usize) -> usize {<X as T>::bat}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = X::bat(x);
+LL |     let _: usize = X::bat(_);
    |                          +++
 
 error[E0308]: mismatched types
@@ -195,7 +195,7 @@ LL |     let _: usize = X::bax;
            found fn item `fn(usize) -> usize {<X as T>::bax}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = X::bax(x);
+LL |     let _: usize = X::bax(_);
    |                          +++
 
 error[E0308]: mismatched types
@@ -213,7 +213,7 @@ LL |     let _: usize = X::bach;
            found fn item `fn(usize) -> usize {<X as T>::bach}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = X::bach(x);
+LL |     let _: usize = X::bach(_);
    |                           +++
 
 error[E0308]: mismatched types
diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.stderr
index 33f81a77aaf..ab39ee74be4 100644
--- a/src/test/ui/type-alias-impl-trait/issue-63279.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-63279.stderr
@@ -15,6 +15,10 @@ LL |     || -> Closure { || () }
    |
    = note: expected unit type `()`
                 found closure `[closure@$DIR/issue-63279.rs:8:21: 8:26]`
+help: use parentheses to call this closure
+   |
+LL |     || -> Closure { (|| ())() }
+   |                     +     +++
 
 error[E0308]: mismatched types
   --> $DIR/issue-63279.rs:8:5