about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-07-19 04:04:17 +0000
committerbors <bors@rust-lang.org>2019-07-19 04:04:17 +0000
commitf9477a77c52af8d3dea361b3f4ac3e60653aa529 (patch)
tree13459d013bcd32ccd59de28a29c6aab8853f6fd4
parentfe499a7b34dcb1fc054dd637ea561a19a268d2de (diff)
parentf8681f0c05c91df4aaf90a0ca60d1f823cf89d9d (diff)
downloadrust-f9477a77c52af8d3dea361b3f4ac3e60653aa529.tar.gz
rust-f9477a77c52af8d3dea361b3f4ac3e60653aa529.zip
Auto merge of #62694 - lundibundi:help-infer-fn-ret, r=eddyb
rustc_typeck: improve diagnostics for -> _ fn return type

This should implement IIUC the mentioned issue.

~~I'm not sure if there is a better way than `get_infer_ret_ty` to get/check the return type without code duplication.~~

~~Also, is this unwrap be okay `ty::Binder::bind(*tables.liberated_fn_sigs().get(hir_id).unwrap())`?~~

r? @eddyb
Closes: https://github.com/rust-lang/rust/issues/56132
-rw-r--r--src/librustc_typeck/check/mod.rs41
-rw-r--r--src/librustc_typeck/collect.rs68
-rw-r--r--src/test/ui/error-codes/E0121.stderr5
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.stderr30
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item_help.rs11
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item_help.stderr12
6 files changed, 125 insertions, 42 deletions
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 41b57decc10..bde6db78aef 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -746,11 +746,12 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
     tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl)
 }
 
-/// If this `DefId` is a "primary tables entry", returns `Some((body_id, decl))`
-/// with information about it's body-id and fn-decl (if any). Otherwise,
+/// If this `DefId` is a "primary tables entry", returns
+/// `Some((body_id, header, decl))` with information about
+/// it's body-id, fn-header and fn-decl (if any). Otherwise,
 /// returns `None`.
 ///
-/// If this function returns "some", then `typeck_tables(def_id)` will
+/// If this function returns `Some`, then `typeck_tables(def_id)` will
 /// succeed; if it returns `None`, then `typeck_tables(def_id)` may or
 /// may not succeed. In some cases where this function returns `None`
 /// (notably closures), `typeck_tables(def_id)` would wind up
@@ -758,15 +759,15 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
 fn primary_body_of(
     tcx: TyCtxt<'_>,
     id: hir::HirId,
-) -> Option<(hir::BodyId, Option<&hir::FnDecl>)> {
+) -> Option<(hir::BodyId, Option<&hir::FnHeader>, Option<&hir::FnDecl>)> {
     match tcx.hir().get(id) {
         Node::Item(item) => {
             match item.node {
                 hir::ItemKind::Const(_, body) |
                 hir::ItemKind::Static(_, _, body) =>
-                    Some((body, None)),
-                hir::ItemKind::Fn(ref decl, .., body) =>
-                    Some((body, Some(decl))),
+                    Some((body, None, None)),
+                hir::ItemKind::Fn(ref decl, ref header, .., body) =>
+                    Some((body, Some(header), Some(decl))),
                 _ =>
                     None,
             }
@@ -774,9 +775,9 @@ fn primary_body_of(
         Node::TraitItem(item) => {
             match item.node {
                 hir::TraitItemKind::Const(_, Some(body)) =>
-                    Some((body, None)),
+                    Some((body, None, None)),
                 hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) =>
-                    Some((body, Some(&sig.decl))),
+                    Some((body, Some(&sig.header), Some(&sig.decl))),
                 _ =>
                     None,
             }
@@ -784,14 +785,14 @@ fn primary_body_of(
         Node::ImplItem(item) => {
             match item.node {
                 hir::ImplItemKind::Const(_, body) =>
-                    Some((body, None)),
+                    Some((body, None, None)),
                 hir::ImplItemKind::Method(ref sig, body) =>
-                    Some((body, Some(&sig.decl))),
+                    Some((body, Some(&sig.header), Some(&sig.decl))),
                 _ =>
                     None,
             }
         }
-        Node::AnonConst(constant) => Some((constant.body, None)),
+        Node::AnonConst(constant) => Some((constant.body, None, None)),
         _ => None,
     }
 }
@@ -824,15 +825,21 @@ fn typeck_tables_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TypeckTables<'_> {
     let span = tcx.hir().span(id);
 
     // Figure out what primary body this item has.
-    let (body_id, fn_decl) = primary_body_of(tcx, id).unwrap_or_else(|| {
-        span_bug!(span, "can't type-check body of {:?}", def_id);
-    });
+    let (body_id, fn_header, fn_decl) = primary_body_of(tcx, id)
+        .unwrap_or_else(|| {
+            span_bug!(span, "can't type-check body of {:?}", def_id);
+        });
     let body = tcx.hir().body(body_id);
 
     let tables = Inherited::build(tcx, def_id).enter(|inh| {
         let param_env = tcx.param_env(def_id);
-        let fcx = if let Some(decl) = fn_decl {
-            let fn_sig = tcx.fn_sig(def_id);
+        let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) {
+            let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
+                let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+                AstConv::ty_of_fn(&fcx, header.unsafety, header.abi, decl)
+            } else {
+                tcx.fn_sig(def_id)
+            };
 
             check_abi(tcx, span, fn_sig.abi());
 
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 5420a2407e6..a5457c45d37 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -160,6 +160,16 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Utility types and common code for the above passes.
 
+fn bad_placeholder_type(tcx: TyCtxt<'tcx>, span: Span) -> errors::DiagnosticBuilder<'tcx> {
+    let mut diag = tcx.sess.struct_span_err_with_code(
+        span,
+        "the type placeholder `_` is not allowed within types on item signatures",
+        DiagnosticId::Error("E0121".into()),
+    );
+    diag.span_label(span, "not allowed in type signatures");
+    diag
+}
+
 impl ItemCtxt<'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> {
         ItemCtxt { tcx, item_def_id }
@@ -191,12 +201,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
     }
 
     fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
-        self.tcx().sess.struct_span_err_with_code(
-            span,
-            "the type placeholder `_` is not allowed within types on item signatures",
-            DiagnosticId::Error("E0121".into()),
-        ).span_label(span, "not allowed in type signatures")
-         .emit();
+        bad_placeholder_type(self.tcx(), span).emit();
 
         self.tcx().types.err
     }
@@ -207,12 +212,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
         _: Option<&ty::GenericParamDef>,
         span: Span,
     ) -> &'tcx Const<'tcx> {
-        self.tcx().sess.struct_span_err_with_code(
-            span,
-            "the const placeholder `_` is not allowed within types on item signatures",
-            DiagnosticId::Error("E0121".into()),
-        ).span_label(span, "not allowed in type signatures")
-         .emit();
+        bad_placeholder_type(self.tcx(), span).emit();
 
         self.tcx().consts.err
     }
@@ -1682,6 +1682,15 @@ fn find_existential_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
     }
 }
 
+pub fn get_infer_ret_ty(output: &'_ hir::FunctionRetTy) -> Option<&hir::Ty> {
+    if let hir::FunctionRetTy::Return(ref ty) = output {
+        if let hir::TyKind::Infer = ty.node {
+            return Some(&**ty)
+        }
+    }
+    None
+}
+
 fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
     use rustc::hir::*;
     use rustc::hir::Node::*;
@@ -1692,18 +1701,41 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
 
     match tcx.hir().get(hir_id) {
         TraitItem(hir::TraitItem {
-            node: TraitItemKind::Method(sig, _),
+            node: TraitItemKind::Method(MethodSig { header, decl }, TraitMethod::Provided(_)),
             ..
         })
         | ImplItem(hir::ImplItem {
-            node: ImplItemKind::Method(sig, _),
+            node: ImplItemKind::Method(MethodSig { header, decl }, _),
             ..
-        }) => AstConv::ty_of_fn(&icx, sig.header.unsafety, sig.header.abi, &sig.decl),
-
-        Item(hir::Item {
+        })
+        | Item(hir::Item {
             node: ItemKind::Fn(decl, header, _, _),
             ..
-        }) => AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl),
+        }) => match get_infer_ret_ty(&decl.output) {
+            Some(ty) => {
+                let fn_sig = tcx.typeck_tables_of(def_id).liberated_fn_sigs()[hir_id];
+                let mut diag = bad_placeholder_type(tcx, ty.span);
+                let ret_ty = fn_sig.output();
+                if ret_ty != tcx.types.err  {
+                    diag.span_suggestion(
+                        ty.span,
+                        "replace `_` with the correct return type",
+                        ret_ty.to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                diag.emit();
+                ty::Binder::bind(fn_sig)
+            },
+            None => AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl)
+        },
+
+        TraitItem(hir::TraitItem {
+            node: TraitItemKind::Method(MethodSig { header, decl }, _),
+            ..
+        }) => {
+            AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl)
+        },
 
         ForeignItem(&hir::ForeignItem {
             node: ForeignItemKind::Fn(ref fn_decl, _, _),
diff --git a/src/test/ui/error-codes/E0121.stderr b/src/test/ui/error-codes/E0121.stderr
index b7f4ce4d230..1a16aab6a41 100644
--- a/src/test/ui/error-codes/E0121.stderr
+++ b/src/test/ui/error-codes/E0121.stderr
@@ -2,7 +2,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/E0121.rs:1:13
    |
 LL | fn foo() -> _ { 5 }
-   |             ^ not allowed in type signatures
+   |             ^
+   |             |
+   |             not allowed in type signatures
+   |             help: replace `_` with the correct return type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/E0121.rs:3:13
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
index 9ae104c9463..ddaa5de4d3e 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
@@ -2,7 +2,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:4:14
    |
 LL | fn test() -> _ { 5 }
-   |              ^ not allowed in type signatures
+   |              ^
+   |              |
+   |              not allowed in type signatures
+   |              help: replace `_` with the correct return type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:7:16
@@ -98,7 +101,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:57:21
    |
 LL |     fn fn_test() -> _ { 5 }
-   |                     ^ not allowed in type signatures
+   |                     ^
+   |                     |
+   |                     not allowed in type signatures
+   |                     help: replace `_` with the correct return type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:60:23
@@ -158,7 +164,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:33:24
    |
 LL |     fn test9(&self) -> _ { () }
-   |                        ^ not allowed in type signatures
+   |                        ^
+   |                        |
+   |                        not allowed in type signatures
+   |                        help: replace `_` with the correct return type: `()`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:36:27
@@ -170,7 +179,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:41:24
    |
 LL |     fn clone(&self) -> _ { Test9 }
-   |                        ^ not allowed in type signatures
+   |                        ^
+   |                        |
+   |                        not allowed in type signatures
+   |                        help: replace `_` with the correct return type: `Test9`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:44:37
@@ -182,7 +194,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:86:31
    |
 LL |         fn fn_test9(&self) -> _ { () }
-   |                               ^ not allowed in type signatures
+   |                               ^
+   |                               |
+   |                               not allowed in type signatures
+   |                               help: replace `_` with the correct return type: `()`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:89:34
@@ -194,7 +209,10 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
   --> $DIR/typeck_type_placeholder_item.rs:94:28
    |
 LL |         fn clone(&self) -> _ { FnTest9 }
-   |                            ^ not allowed in type signatures
+   |                            ^
+   |                            |
+   |                            not allowed in type signatures
+   |                            help: replace `_` with the correct return type: `main::FnTest9`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:97:41
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item_help.rs b/src/test/ui/typeck/typeck_type_placeholder_item_help.rs
new file mode 100644
index 00000000000..5f4cb4c1316
--- /dev/null
+++ b/src/test/ui/typeck/typeck_type_placeholder_item_help.rs
@@ -0,0 +1,11 @@
+// This test checks that it proper item type will be suggested when
+// using the `_` type placeholder.
+
+fn test1() -> _ { Some(42) }
+//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+
+pub fn main() {
+    let _: Option<usize> = test1();
+    let _: f64 = test1();
+    let _: Option<i32> = test1();
+}
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr b/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr
new file mode 100644
index 00000000000..7fb5549825c
--- /dev/null
+++ b/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr
@@ -0,0 +1,12 @@
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item_help.rs:4:15
+   |
+LL | fn test1() -> _ { Some(42) }
+   |               ^
+   |               |
+   |               not allowed in type signatures
+   |               help: replace `_` with the correct return type: `std::option::Option<i32>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.