Auto merge of #75608 - estebank:suggest-boxed-match-exprs, r=lcnr,varkor

More structured suggestions for boxed trait objects instead of impl Trait on non-coerceable tail expressions

When encountering a `match` or `if` as a tail expression where the
different arms do not have the same type *and* the return type of that
`fn` is an `impl Trait`, check whether those arms can implement `Trait`
and if so, suggest using boxed trait objects.

Use structured suggestion for `impl T` to `Box<dyn T>`.

Fix https://github.com/rust-lang/rust/issues/69107
This commit is contained in:
bors 2020-09-14 19:57:57 +00:00
commit 41dc3942eb
9 changed files with 467 additions and 59 deletions

View file

@ -70,7 +70,7 @@ use rustc_middle::ty::{
subst::{Subst, SubstsRef},
Region, Ty, TyCtxt, TypeFoldable,
};
use rustc_span::{DesugaringKind, Pos, Span};
use rustc_span::{BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::{cmp, fmt};
@ -617,11 +617,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
ref prior_arms,
last_ty,
scrut_hir_id,
opt_suggest_box_span,
arm_span,
..
}) => match source {
hir::MatchSource::IfLetDesugar { .. } => {
let msg = "`if let` arms have incompatible types";
err.span_label(cause.span, msg);
if let Some(ret_sp) = opt_suggest_box_span {
self.suggest_boxing_for_return_impl_trait(
err,
ret_sp,
prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
);
}
}
hir::MatchSource::TryDesugar => {
if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
@ -675,9 +684,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
}
if let Some(ret_sp) = opt_suggest_box_span {
// Get return type span and point to it.
self.suggest_boxing_for_return_impl_trait(
err,
ret_sp,
prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
);
}
}
},
ObligationCauseCode::IfExpression(box IfExpressionCause { then, outer, semicolon }) => {
ObligationCauseCode::IfExpression(box IfExpressionCause {
then,
else_sp,
outer,
semicolon,
opt_suggest_box_span,
}) => {
err.span_label(then, "expected because of this");
if let Some(sp) = outer {
err.span_label(sp, "`if` and `else` have incompatible types");
@ -690,11 +713,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
}
if let Some(ret_sp) = opt_suggest_box_span {
self.suggest_boxing_for_return_impl_trait(
err,
ret_sp,
vec![then, else_sp].into_iter(),
);
}
}
_ => (),
}
}
fn suggest_boxing_for_return_impl_trait(
&self,
err: &mut DiagnosticBuilder<'tcx>,
return_sp: Span,
arm_spans: impl Iterator<Item = Span>,
) {
err.multipart_suggestion(
"you could change the return type to be a boxed trait object",
vec![
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
(return_sp.shrink_to_hi(), ">".to_string()),
],
Applicability::MaybeIncorrect,
);
let sugg = arm_spans
.flat_map(|sp| {
vec![
(sp.shrink_to_lo(), "Box::new(".to_string()),
(sp.shrink_to_hi(), ")".to_string()),
]
.into_iter()
})
.collect::<Vec<_>>();
err.multipart_suggestion(
"if you change the return type to expect trait objects, box the returned expressions",
sugg,
Applicability::MaybeIncorrect,
);
}
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
/// populate `other_value` with `other_ty`.