Suggest replacing _ in type signature of impl for Trait

This commit is contained in:
Michael Goulet 2022-03-27 19:43:05 -07:00
parent 18f314e702
commit 42dbbabcb0
6 changed files with 147 additions and 15 deletions

View File

@ -2563,12 +2563,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// We proactively collect all the inferred type params to emit a single error per fn def.
let mut visitor = HirPlaceholderCollector::default();
for ty in decl.inputs {
visitor.visit_ty(ty);
}
let mut infer_replacements = vec![];
walk_generics(&mut visitor, generics);
let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None));
let input_tys: Vec<_> = decl
.inputs
.iter()
.enumerate()
.map(|(i, a)| {
if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
if let Some(suggested_ty) =
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, i) {
infer_replacements.push((a.span, suggested_ty.to_string()));
return suggested_ty;
}
}
// Only visit the type looking for `_` if we didn't fix the type above
visitor.visit_ty(a);
self.ty_of_arg(a, None)
})
.collect();
let output_ty = match decl.output {
hir::FnRetTy::Return(output) => {
visitor.visit_ty(output);
@ -2579,24 +2596,34 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("ty_of_fn: output_ty={:?}", output_ty);
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi);
let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
if !self.allow_ty_infer() {
if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
// We always collect the spans for placeholder types when evaluating `fn`s, but we
// only want to emit an error complaining about them if infer types (`_`) are not
// allowed. `allow_ty_infer` gates this behavior. We check for the presence of
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
crate::collect::placeholder_type_error(
let mut diag = crate::collect::placeholder_type_error_diag(
tcx,
ident_span.map(|sp| sp.shrink_to_hi()),
generics.params,
visitor.0,
infer_replacements.iter().map(|(s, _)| *s).collect(),
true,
hir_ty,
"function",
);
if !infer_replacements.is_empty() {
diag.multipart_suggestion(&format!(
"try replacing `_` with the type{} in the corresponding trait method signature",
if infer_replacements.len() > 1 { "s" } else { "" }
), infer_replacements, Applicability::MachineApplicable);
}
diag.emit();
}
// Find any late-bound regions declared in return type that do
@ -2624,6 +2651,39 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
bare_fn_ty
}
/// Given a fn_hir_id for a impl function, suggest the type that is found on the
/// corresponding function in the trait that the impl implements, if it exists.
fn suggest_trait_fn_ty_for_impl_fn_infer(
&self,
fn_hir_id: hir::HirId,
arg_idx: usize,
) -> Option<Ty<'tcx>> {
let tcx = self.tcx();
let hir = tcx.hir();
let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
hir.get(fn_hir_id) else { return None };
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
hir.get(hir.get_parent_node(fn_hir_id)) else { return None };
let trait_ref =
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
tcx,
*ident,
ty::AssocKind::Fn,
trait_ref.def_id,
)?;
let fn_sig = tcx.fn_sig(x.def_id).subst(
tcx,
trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)),
);
Some(tcx.erase_late_bound_regions(fn_sig.input(arg_idx)))
}
fn validate_late_bound_regions(
&self,
constrained_regions: FxHashSet<ty::BoundRegionKind>,

View File

@ -160,6 +160,33 @@ crate fn placeholder_type_error<'tcx>(
return;
}
placeholder_type_error_diag(
tcx,
span,
generics,
placeholder_types,
vec![],
suggest,
hir_ty,
kind,
)
.emit();
}
crate fn placeholder_type_error_diag<'tcx>(
tcx: TyCtxt<'tcx>,
span: Option<Span>,
generics: &[hir::GenericParam<'_>],
placeholder_types: Vec<Span>,
additional_spans: Vec<Span>,
suggest: bool,
hir_ty: Option<&hir::Ty<'_>>,
kind: &'static str,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
if placeholder_types.is_empty() {
return bad_placeholder(tcx, additional_spans, kind);
}
let type_name = generics.next_type_param_name(None);
let mut sugg: Vec<_> =
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
@ -182,7 +209,8 @@ crate fn placeholder_type_error<'tcx>(
sugg.push((span, format!(", {}", type_name)));
}
let mut err = bad_placeholder(tcx, placeholder_types, kind);
let mut err =
bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
// Suggest, but only if it is not a function in const or static
if suggest {
@ -218,7 +246,8 @@ crate fn placeholder_type_error<'tcx>(
);
}
}
err.emit();
err
}
fn reject_placeholder_type_signatures_in_item<'tcx>(

View File

@ -0,0 +1,13 @@
// run-rustfix
#![allow(unused)]
trait Foo<T>: Sized {
fn bar(i: i32, t: T, s: &Self) {}
}
impl Foo<usize> for () {
fn bar(i: i32, t: usize, s: &()) {}
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
}
fn main() {}

View File

@ -0,0 +1,13 @@
// run-rustfix
#![allow(unused)]
trait Foo<T>: Sized {
fn bar(i: i32, t: T, s: &Self) {}
}
impl Foo<usize> for () {
fn bar(i: _, t: _, s: _) {}
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
}
fn main() {}

View File

@ -0,0 +1,17 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/replace-impl-infer-ty-from-trait.rs:9:15
|
LL | fn bar(i: _, t: _, s: _) {}
| ^ ^ ^ not allowed in type signatures
| | |
| | not allowed in type signatures
| not allowed in type signatures
|
help: try replacing `_` with the types in the corresponding trait method signature
|
LL | fn bar(i: i32, t: usize, s: &()) {}
| ~~~ ~~~~~ ~~~
error: aborting due to previous error
For more information about this error, try `rustc --explain E0121`.

View File

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