Skip to content

Commit 42dbbab

Browse files
Suggest replacing _ in type signature of impl for Trait
1 parent 18f314e commit 42dbbab

File tree

6 files changed

+147
-15
lines changed

6 files changed

+147
-15
lines changed

compiler/rustc_typeck/src/astconv/mod.rs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,12 +2563,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25632563

25642564
// We proactively collect all the inferred type params to emit a single error per fn def.
25652565
let mut visitor = HirPlaceholderCollector::default();
2566-
for ty in decl.inputs {
2567-
visitor.visit_ty(ty);
2568-
}
2566+
let mut infer_replacements = vec![];
2567+
25692568
walk_generics(&mut visitor, generics);
25702569

2571-
let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None));
2570+
let input_tys: Vec<_> = decl
2571+
.inputs
2572+
.iter()
2573+
.enumerate()
2574+
.map(|(i, a)| {
2575+
if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
2576+
if let Some(suggested_ty) =
2577+
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, i) {
2578+
infer_replacements.push((a.span, suggested_ty.to_string()));
2579+
return suggested_ty;
2580+
}
2581+
}
2582+
2583+
// Only visit the type looking for `_` if we didn't fix the type above
2584+
visitor.visit_ty(a);
2585+
self.ty_of_arg(a, None)
2586+
})
2587+
.collect();
2588+
25722589
let output_ty = match decl.output {
25732590
hir::FnRetTy::Return(output) => {
25742591
visitor.visit_ty(output);
@@ -2579,24 +2596,34 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25792596

25802597
debug!("ty_of_fn: output_ty={:?}", output_ty);
25812598

2582-
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi);
2599+
let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
25832600
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
25842601

2585-
if !self.allow_ty_infer() {
2602+
if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
25862603
// We always collect the spans for placeholder types when evaluating `fn`s, but we
25872604
// only want to emit an error complaining about them if infer types (`_`) are not
25882605
// allowed. `allow_ty_infer` gates this behavior. We check for the presence of
25892606
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
25902607

2591-
crate::collect::placeholder_type_error(
2608+
let mut diag = crate::collect::placeholder_type_error_diag(
25922609
tcx,
25932610
ident_span.map(|sp| sp.shrink_to_hi()),
25942611
generics.params,
25952612
visitor.0,
2613+
infer_replacements.iter().map(|(s, _)| *s).collect(),
25962614
true,
25972615
hir_ty,
25982616
"function",
25992617
);
2618+
2619+
if !infer_replacements.is_empty() {
2620+
diag.multipart_suggestion(&format!(
2621+
"try replacing `_` with the type{} in the corresponding trait method signature",
2622+
if infer_replacements.len() > 1 { "s" } else { "" }
2623+
), infer_replacements, Applicability::MachineApplicable);
2624+
}
2625+
2626+
diag.emit();
26002627
}
26012628

26022629
// Find any late-bound regions declared in return type that do
@@ -2624,6 +2651,39 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26242651
bare_fn_ty
26252652
}
26262653

2654+
/// Given a fn_hir_id for a impl function, suggest the type that is found on the
2655+
/// corresponding function in the trait that the impl implements, if it exists.
2656+
fn suggest_trait_fn_ty_for_impl_fn_infer(
2657+
&self,
2658+
fn_hir_id: hir::HirId,
2659+
arg_idx: usize,
2660+
) -> Option<Ty<'tcx>> {
2661+
let tcx = self.tcx();
2662+
let hir = tcx.hir();
2663+
2664+
let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
2665+
hir.get(fn_hir_id) else { return None };
2666+
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
2667+
hir.get(hir.get_parent_node(fn_hir_id)) else { return None };
2668+
2669+
let trait_ref =
2670+
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
2671+
2672+
let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
2673+
tcx,
2674+
*ident,
2675+
ty::AssocKind::Fn,
2676+
trait_ref.def_id,
2677+
)?;
2678+
2679+
let fn_sig = tcx.fn_sig(x.def_id).subst(
2680+
tcx,
2681+
trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)),
2682+
);
2683+
2684+
Some(tcx.erase_late_bound_regions(fn_sig.input(arg_idx)))
2685+
}
2686+
26272687
fn validate_late_bound_regions(
26282688
&self,
26292689
constrained_regions: FxHashSet<ty::BoundRegionKind>,

compiler/rustc_typeck/src/collect.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,33 @@ crate fn placeholder_type_error<'tcx>(
160160
return;
161161
}
162162

163+
placeholder_type_error_diag(
164+
tcx,
165+
span,
166+
generics,
167+
placeholder_types,
168+
vec![],
169+
suggest,
170+
hir_ty,
171+
kind,
172+
)
173+
.emit();
174+
}
175+
176+
crate fn placeholder_type_error_diag<'tcx>(
177+
tcx: TyCtxt<'tcx>,
178+
span: Option<Span>,
179+
generics: &[hir::GenericParam<'_>],
180+
placeholder_types: Vec<Span>,
181+
additional_spans: Vec<Span>,
182+
suggest: bool,
183+
hir_ty: Option<&hir::Ty<'_>>,
184+
kind: &'static str,
185+
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
186+
if placeholder_types.is_empty() {
187+
return bad_placeholder(tcx, additional_spans, kind);
188+
}
189+
163190
let type_name = generics.next_type_param_name(None);
164191
let mut sugg: Vec<_> =
165192
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
@@ -182,7 +209,8 @@ crate fn placeholder_type_error<'tcx>(
182209
sugg.push((span, format!(", {}", type_name)));
183210
}
184211

185-
let mut err = bad_placeholder(tcx, placeholder_types, kind);
212+
let mut err =
213+
bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
186214

187215
// Suggest, but only if it is not a function in const or static
188216
if suggest {
@@ -218,7 +246,8 @@ crate fn placeholder_type_error<'tcx>(
218246
);
219247
}
220248
}
221-
err.emit();
249+
250+
err
222251
}
223252

224253
fn reject_placeholder_type_signatures_in_item<'tcx>(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
4+
trait Foo<T>: Sized {
5+
fn bar(i: i32, t: T, s: &Self) {}
6+
}
7+
8+
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
11+
}
12+
13+
fn main() {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
4+
trait Foo<T>: Sized {
5+
fn bar(i: i32, t: T, s: &Self) {}
6+
}
7+
8+
impl Foo<usize> for () {
9+
fn bar(i: _, t: _, s: _) {}
10+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
11+
}
12+
13+
fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
2+
--> $DIR/replace-impl-infer-ty-from-trait.rs:9:15
3+
|
4+
LL | fn bar(i: _, t: _, s: _) {}
5+
| ^ ^ ^ not allowed in type signatures
6+
| | |
7+
| | not allowed in type signatures
8+
| not allowed in type signatures
9+
|
10+
help: try replacing `_` with the types in the corresponding trait method signature
11+
|
12+
LL | fn bar(i: i32, t: usize, s: &()) {}
13+
| ~~~ ~~~~~ ~~~
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0121`.

src/test/ui/typeck/typeck_type_placeholder_item.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,10 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
560560
LL | fn clone_from(&mut self, other: _) { *self = Test9; }
561561
| ^ not allowed in type signatures
562562
|
563-
help: use type parameters instead
563+
help: try replacing `_` with the type in the corresponding trait method signature
564564
|
565-
LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; }
566-
| +++ ~
565+
LL | fn clone_from(&mut self, other: &Test9) { *self = Test9; }
566+
| ~~~~~~
567567

568568
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
569569
--> $DIR/typeck_type_placeholder_item.rs:107:31
@@ -600,10 +600,10 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
600600
LL | fn clone_from(&mut self, other: _) { *self = FnTest9; }
601601
| ^ not allowed in type signatures
602602
|
603-
help: use type parameters instead
603+
help: try replacing `_` with the type in the corresponding trait method signature
604604
|
605-
LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
606-
| +++ ~
605+
LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; }
606+
| ~~~~~~~~
607607

608608
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types
609609
--> $DIR/typeck_type_placeholder_item.rs:201:14

0 commit comments

Comments
 (0)