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
This commit is contained in:
bors 2019-07-19 04:04:17 +00:00
commit f9477a77c5
6 changed files with 125 additions and 42 deletions

View File

@ -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());

View File

@ -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, _, _),

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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`.