diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 77d45cfa63c10..1028c4b468063 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 { 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 { 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 5420a2407e66f..a5457c45d378c 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 b7f4ce4d230ef..1a16aab6a41d1 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 9ae104c9463dc..ddaa5de4d3e27 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 0000000000000..5f4cb4c1316d5 --- /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 = test1(); + let _: f64 = test1(); + let _: Option = 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 0000000000000..7fb5549825cc5 --- /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` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0121`.