Skip to content

Commit

Permalink
rustc_typeck: improve diagnostics for -> _ fn return type
Browse files Browse the repository at this point in the history
Closes: #56132
  • Loading branch information
lundibundi committed Jul 17, 2019
1 parent d9ad04a commit f8681f0
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 30 deletions.
41 changes: 24 additions & 17 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,52 +746,53 @@ 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
/// redirecting to the owning function.
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,
}
}
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,
}
}
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,
}
}
Expand Down Expand Up @@ -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());

Expand Down
44 changes: 38 additions & 6 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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, _, _),
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/error-codes/E0121.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 24 additions & 6 deletions src/test/ui/typeck/typeck_type_placeholder_item.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/typeck/typeck_type_placeholder_item_help.rs
Original file line number Diff line number Diff line change
@@ -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();
}
12 changes: 12 additions & 0 deletions src/test/ui/typeck/typeck_type_placeholder_item_help.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit f8681f0

Please sign in to comment.