Skip to content

Commit 319fbe3

Browse files
Fix suggestion for _ on return type for fn in impl for Trait
1 parent 42dbbab commit 319fbe3

7 files changed

+129
-73
lines changed

compiler/rustc_typeck/src/astconv/mod.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2574,7 +2574,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25742574
.map(|(i, a)| {
25752575
if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
25762576
if let Some(suggested_ty) =
2577-
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, i) {
2577+
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
2578+
{
25782579
infer_replacements.push((a.span, suggested_ty.to_string()));
25792580
return suggested_ty;
25802581
}
@@ -2588,8 +2589,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25882589

25892590
let output_ty = match decl.output {
25902591
hir::FnRetTy::Return(output) => {
2591-
visitor.visit_ty(output);
2592-
self.ast_ty_to_ty(output)
2592+
if let hir::TyKind::Infer = output.kind
2593+
&& !self.allow_ty_infer()
2594+
&& let Some(suggested_ty) =
2595+
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
2596+
{
2597+
infer_replacements.push((output.span, suggested_ty.to_string()));
2598+
suggested_ty
2599+
} else {
2600+
visitor.visit_ty(output);
2601+
self.ast_ty_to_ty(output)
2602+
}
25932603
}
25942604
hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
25952605
};
@@ -2619,7 +2629,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26192629
if !infer_replacements.is_empty() {
26202630
diag.multipart_suggestion(&format!(
26212631
"try replacing `_` with the type{} in the corresponding trait method signature",
2622-
if infer_replacements.len() > 1 { "s" } else { "" }
2632+
rustc_errors::pluralize!(infer_replacements.len()),
26232633
), infer_replacements, Applicability::MachineApplicable);
26242634
}
26252635

@@ -2653,18 +2663,20 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26532663

26542664
/// Given a fn_hir_id for a impl function, suggest the type that is found on the
26552665
/// corresponding function in the trait that the impl implements, if it exists.
2666+
/// If arg_idx is Some, then it corresponds to an input type index, otherwise it
2667+
/// corresponds to the return type.
26562668
fn suggest_trait_fn_ty_for_impl_fn_infer(
26572669
&self,
26582670
fn_hir_id: hir::HirId,
2659-
arg_idx: usize,
2671+
arg_idx: Option<usize>,
26602672
) -> Option<Ty<'tcx>> {
26612673
let tcx = self.tcx();
26622674
let hir = tcx.hir();
26632675

26642676
let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
26652677
hir.get(fn_hir_id) else { return None };
26662678
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
2667-
hir.get(hir.get_parent_node(fn_hir_id)) else { return None };
2679+
hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
26682680

26692681
let trait_ref =
26702682
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
@@ -2681,7 +2693,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26812693
trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)),
26822694
);
26832695

2684-
Some(tcx.erase_late_bound_regions(fn_sig.input(arg_idx)))
2696+
let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
2697+
2698+
Some(tcx.erase_late_bound_regions(ty))
26852699
}
26862700

26872701
fn validate_late_bound_regions(

compiler/rustc_typeck/src/collect.rs

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,50 +1897,17 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
18971897
generics,
18981898
..
18991899
})
1900-
| ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. })
19011900
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => {
1902-
match get_infer_ret_ty(&sig.decl.output) {
1903-
Some(ty) => {
1904-
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
1905-
// Typeck doesn't expect erased regions to be returned from `type_of`.
1906-
let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
1907-
ty::ReErased => tcx.lifetimes.re_static,
1908-
_ => r,
1909-
});
1910-
let fn_sig = ty::Binder::dummy(fn_sig);
1911-
1912-
let mut visitor = HirPlaceholderCollector::default();
1913-
visitor.visit_ty(ty);
1914-
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
1915-
let ret_ty = fn_sig.skip_binder().output();
1916-
if !ret_ty.references_error() {
1917-
if !ret_ty.is_closure() {
1918-
let ret_ty_str = match ret_ty.kind() {
1919-
// Suggest a function pointer return type instead of a unique function definition
1920-
// (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid
1921-
// syntax)
1922-
ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(),
1923-
_ => ret_ty.to_string(),
1924-
};
1925-
diag.span_suggestion(
1926-
ty.span,
1927-
"replace with the correct return type",
1928-
ret_ty_str,
1929-
Applicability::MaybeIncorrect,
1930-
);
1931-
} else {
1932-
// We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
1933-
// to prevent the user from getting a papercut while trying to use the unique closure
1934-
// syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
1935-
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
1936-
diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
1937-
}
1938-
}
1939-
diag.emit();
1901+
infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx)
1902+
}
19401903

1941-
fn_sig
1942-
}
1943-
None => <dyn AstConv<'_>>::ty_of_fn(
1904+
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) => {
1905+
// Do not try to inference the return type for a impl method coming from a trait
1906+
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
1907+
tcx.hir().get(tcx.hir().get_parent_node(hir_id))
1908+
&& i.of_trait.is_some()
1909+
{
1910+
<dyn AstConv<'_>>::ty_of_fn(
19441911
&icx,
19451912
hir_id,
19461913
sig.header.unsafety,
@@ -1949,7 +1916,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
19491916
generics,
19501917
Some(ident.span),
19511918
None,
1952-
),
1919+
)
1920+
} else {
1921+
infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx)
19531922
}
19541923
}
19551924

@@ -2011,6 +1980,70 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
20111980
}
20121981
}
20131982

1983+
fn infer_return_ty_for_fn_sig<'tcx>(
1984+
tcx: TyCtxt<'tcx>,
1985+
sig: &hir::FnSig<'_>,
1986+
ident: Ident,
1987+
generics: &hir::Generics<'_>,
1988+
def_id: LocalDefId,
1989+
icx: &ItemCtxt<'tcx>,
1990+
) -> ty::PolyFnSig<'tcx> {
1991+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
1992+
1993+
match get_infer_ret_ty(&sig.decl.output) {
1994+
Some(ty) => {
1995+
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
1996+
// Typeck doesn't expect erased regions to be returned from `type_of`.
1997+
let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
1998+
ty::ReErased => tcx.lifetimes.re_static,
1999+
_ => r,
2000+
});
2001+
let fn_sig = ty::Binder::dummy(fn_sig);
2002+
2003+
let mut visitor = HirPlaceholderCollector::default();
2004+
visitor.visit_ty(ty);
2005+
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
2006+
let ret_ty = fn_sig.skip_binder().output();
2007+
if !ret_ty.references_error() {
2008+
if !ret_ty.is_closure() {
2009+
let ret_ty_str = match ret_ty.kind() {
2010+
// Suggest a function pointer return type instead of a unique function definition
2011+
// (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid
2012+
// syntax)
2013+
ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(),
2014+
_ => ret_ty.to_string(),
2015+
};
2016+
diag.span_suggestion(
2017+
ty.span,
2018+
"replace with the correct return type",
2019+
ret_ty_str,
2020+
Applicability::MaybeIncorrect,
2021+
);
2022+
} else {
2023+
// We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
2024+
// to prevent the user from getting a papercut while trying to use the unique closure
2025+
// syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
2026+
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
2027+
diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
2028+
}
2029+
}
2030+
diag.emit();
2031+
2032+
fn_sig
2033+
}
2034+
None => <dyn AstConv<'_>>::ty_of_fn(
2035+
icx,
2036+
hir_id,
2037+
sig.header.unsafety,
2038+
sig.header.abi,
2039+
sig.decl,
2040+
generics,
2041+
Some(ident.span),
2042+
None,
2043+
),
2044+
}
2045+
}
2046+
20142047
fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
20152048
let icx = ItemCtxt::new(tcx, def_id);
20162049
match tcx.hir().expect_item(def_id.expect_local()).kind {

src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
#![allow(unused)]
33

44
trait Foo<T>: Sized {
5-
fn bar(i: i32, t: T, s: &Self) {}
5+
fn bar(i: i32, t: T, s: &Self) -> (T, i32);
66
}
77

88
impl Foo<usize> for () {
9-
fn bar(i: i32, t: usize, s: &()) {}
10-
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
9+
fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
10+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
11+
(1, 2)
12+
}
1113
}
1214

1315
fn main() {}

src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
#![allow(unused)]
33

44
trait Foo<T>: Sized {
5-
fn bar(i: i32, t: T, s: &Self) {}
5+
fn bar(i: i32, t: T, s: &Self) -> (T, i32);
66
}
77

88
impl Foo<usize> for () {
9-
fn bar(i: _, t: _, s: _) {}
10-
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
9+
fn bar(i: _, t: _, s: _) -> _ {
10+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
11+
(1, 2)
12+
}
1113
}
1214

1315
fn main() {}

src/test/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
22
--> $DIR/replace-impl-infer-ty-from-trait.rs:9:15
33
|
4-
LL | fn bar(i: _, t: _, s: _) {}
5-
| ^ ^ ^ not allowed in type signatures
6-
| | |
4+
LL | fn bar(i: _, t: _, s: _) -> _ {
5+
| ^ ^ ^ ^ not allowed in type signatures
6+
| | | |
7+
| | | not allowed in type signatures
78
| | not allowed in type signatures
89
| not allowed in type signatures
910
|
1011
help: try replacing `_` with the types in the corresponding trait method signature
1112
|
12-
LL | fn bar(i: i32, t: usize, s: &()) {}
13-
| ~~~ ~~~~~ ~~~
13+
LL | fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
14+
| ~~~ ~~~~~ ~~~ ~~~~~~~~~~~~
1415

1516
error: aborting due to previous error
1617

src/test/ui/typeck/typeck_type_placeholder_item.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ unsafe fn test12(x: *const usize) -> *const *const _ {
5757

5858
impl Clone for Test9 {
5959
fn clone(&self) -> _ { Test9 }
60-
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
60+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
6161

6262
fn clone_from(&mut self, other: _) { *self = Test9; }
6363
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
@@ -113,7 +113,7 @@ pub fn main() {
113113

114114
impl Clone for FnTest9 {
115115
fn clone(&self) -> _ { FnTest9 }
116-
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
116+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
117117

118118
fn clone_from(&mut self, other: _) { *self = FnTest9; }
119119
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions

src/test/ui/typeck/typeck_type_placeholder_item.stderr

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -545,14 +545,16 @@ help: use type parameters instead
545545
LL | fn test10<T>(&self, _x : T) { }
546546
| +++ ~
547547

548-
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
548+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
549549
--> $DIR/typeck_type_placeholder_item.rs:59:24
550550
|
551551
LL | fn clone(&self) -> _ { Test9 }
552-
| ^
553-
| |
554-
| not allowed in type signatures
555-
| help: replace with the correct return type: `Test9`
552+
| ^ not allowed in type signatures
553+
|
554+
help: try replacing `_` with the type in the corresponding trait method signature
555+
|
556+
LL | fn clone(&self) -> Test9 { Test9 }
557+
| ~~~~~
556558

557559
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
558560
--> $DIR/typeck_type_placeholder_item.rs:62:37
@@ -585,14 +587,16 @@ help: use type parameters instead
585587
LL | fn fn_test10<T>(&self, _x : T) { }
586588
| +++ ~
587589

588-
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
590+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
589591
--> $DIR/typeck_type_placeholder_item.rs:115:28
590592
|
591593
LL | fn clone(&self) -> _ { FnTest9 }
592-
| ^
593-
| |
594-
| not allowed in type signatures
595-
| help: replace with the correct return type: `FnTest9`
594+
| ^ not allowed in type signatures
595+
|
596+
help: try replacing `_` with the type in the corresponding trait method signature
597+
|
598+
LL | fn clone(&self) -> FnTest9 { FnTest9 }
599+
| ~~~~~~~
596600

597601
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
598602
--> $DIR/typeck_type_placeholder_item.rs:118:41

0 commit comments

Comments
 (0)