Suggest boxing if then expr if that solves divergent arms

When encountering

```rust
let _ = if true {
    Struct
} else {
    foo() // -> Box<dyn Trait>
};
```

if `Struct` implements `Trait`, suggest boxing the then arm tail expression.

Part of #102629.
This commit is contained in:
Esteban Küber 2024-01-22 20:53:41 +00:00
parent 390ef9ba02
commit ac56a2b564
4 changed files with 63 additions and 1 deletions

View File

@ -330,6 +330,38 @@ fn foo(&self, x: T) -> T { x }
);
}
}
(ty::Adt(_, _), ty::Adt(def, args))
if let ObligationCauseCode::IfExpression(cause) = cause.code()
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
&& let Some(then) = blk.expr
&& def.is_box()
&& let boxed_ty = args.type_at(0)
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
&& let Some(def_id) = t.principal_def_id()
&& let mut impl_def_ids = vec![]
&& let _ =
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
impl_def_ids.push(did)
})
&& let [_] = &impl_def_ids[..] =>
{
// We have divergent if/else arms where the expected value is a type that
// implements the trait of the found boxed trait object.
diag.multipart_suggestion(
format!(
"`{}` implements `{}` so you can box it to coerce to the trait \
object `{}`",
values.expected,
tcx.item_name(def_id),
values.found,
),
vec![
(then.span.shrink_to_lo(), "Box::new(".to_string()),
(then.span.shrink_to_hi(), ")".to_string()),
],
MachineApplicable,
);
}
_ => {}
}
debug!(

View File

@ -6,6 +6,11 @@ fn foo() -> Box<dyn Trait> {
Box::new(Struct)
}
fn main() {
let _ = if true {
Box::new(Struct)
} else {
foo() //~ ERROR E0308
};
let _ = if true {
foo()
} else {

View File

@ -6,6 +6,11 @@ fn foo() -> Box<dyn Trait> {
Box::new(Struct)
}
fn main() {
let _ = if true {
Struct
} else {
foo() //~ ERROR E0308
};
let _ = if true {
foo()
} else {

View File

@ -3,6 +3,26 @@ error[E0308]: `if` and `else` have incompatible types
|
LL | let _ = if true {
| _____________-
LL | | Struct
| | ------ expected because of this
LL | | } else {
LL | | foo()
| | ^^^^^ expected `Struct`, found `Box<dyn Trait>`
LL | | };
| |_____- `if` and `else` have incompatible types
|
= note: expected struct `Struct`
found struct `Box<dyn Trait>`
help: `Struct` implements `Trait` so you can box it to coerce to the trait object `Box<dyn Trait>`
|
LL | Box::new(Struct)
| +++++++++ +
error[E0308]: `if` and `else` have incompatible types
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:17:9
|
LL | let _ = if true {
| _____________-
LL | | foo()
| | ----- expected because of this
LL | | } else {
@ -19,6 +39,6 @@ help: store this in the heap by calling `Box::new`
LL | Box::new(Struct)
| +++++++++ +
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.