Suggest boxing both arms of if expr if that solves divergent arms involving impl Trait
When encountering the following ```rust // run-rustfix trait Trait {} struct Struct; impl Trait for Struct {} fn foo() -> Box<dyn Trait> { Box::new(Struct) } fn bar() -> impl Trait { Struct } fn main() { let _ = if true { Struct } else { foo() //~ ERROR E0308 }; let _ = if true { foo() } else { Struct //~ ERROR E0308 }; let _ = if true { Struct } else { bar() // impl Trait }; let _ = if true { bar() // impl Trait } else { Struct }; } ``` suggest boxing both arms ```rust let _ = if true { Box::new(Struct) as Box<dyn Trait> } else { Box::new(bar()) }; let _ = if true { Box::new(bar()) as Box<dyn Trait> } else { Box::new(Struct) }; ```
This commit is contained in:
parent
ac56a2b564
commit
34f4f3da4f
@ -294,8 +294,9 @@ impl<T> Trait<T> for X {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias))
|
(_, ty::Alias(ty::Opaque, opaque_ty))
|
||||||
if alias.def_id.is_local()
|
| (ty::Alias(ty::Opaque, opaque_ty), _) => {
|
||||||
|
if opaque_ty.def_id.is_local()
|
||||||
&& matches!(
|
&& matches!(
|
||||||
tcx.def_kind(body_owner_def_id),
|
tcx.def_kind(body_owner_def_id),
|
||||||
DefKind::Fn
|
DefKind::Fn
|
||||||
@ -303,21 +304,74 @@ impl<T> Trait<T> for X {
|
|||||||
| DefKind::Const
|
| DefKind::Const
|
||||||
| DefKind::AssocFn
|
| DefKind::AssocFn
|
||||||
| DefKind::AssocConst
|
| DefKind::AssocConst
|
||||||
) =>
|
)
|
||||||
{
|
&& tcx.is_type_alias_impl_trait(opaque_ty.def_id)
|
||||||
if tcx.is_type_alias_impl_trait(alias.def_id) {
|
&& !tcx
|
||||||
if !tcx
|
|
||||||
.opaque_types_defined_by(body_owner_def_id.expect_local())
|
.opaque_types_defined_by(body_owner_def_id.expect_local())
|
||||||
.contains(&alias.def_id.expect_local())
|
.contains(&opaque_ty.def_id.expect_local())
|
||||||
{
|
{
|
||||||
let sp = tcx
|
let sp = tcx
|
||||||
.def_ident_span(body_owner_def_id)
|
.def_ident_span(body_owner_def_id)
|
||||||
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
|
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
|
||||||
diag.span_note(
|
diag.span_note(
|
||||||
sp,
|
sp,
|
||||||
"\
|
"this item must have the opaque type in its signature in order to \
|
||||||
this item must have the opaque type in its signature \
|
be able to register hidden types",
|
||||||
in order to be able to register hidden types",
|
);
|
||||||
|
}
|
||||||
|
// If two if arms can be coerced to a trait object, provide a structured
|
||||||
|
// suggestion.
|
||||||
|
let ObligationCauseCode::IfExpression(cause) = cause.code() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(then) = blk.expr else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(else_) = blk.expr else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let expected = match values.found.kind() {
|
||||||
|
ty::Alias(..) => values.expected,
|
||||||
|
_ => values.found,
|
||||||
|
};
|
||||||
|
let preds = tcx.explicit_item_bounds(opaque_ty.def_id);
|
||||||
|
for (pred, _span) in preds.skip_binder() {
|
||||||
|
let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if trait_predicate.polarity != ty::ImplPolarity::Positive {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let def_id = trait_predicate.def_id();
|
||||||
|
let mut impl_def_ids = vec![];
|
||||||
|
tcx.for_each_relevant_impl(def_id, expected, |did| {
|
||||||
|
impl_def_ids.push(did)
|
||||||
|
});
|
||||||
|
if let [_] = &impl_def_ids[..] {
|
||||||
|
let trait_name = tcx.item_name(def_id);
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
format!(
|
||||||
|
"`{expected}` implements `{trait_name}` so you can box \
|
||||||
|
both arms and coerce to the trait object \
|
||||||
|
`Box<dyn {trait_name}>`",
|
||||||
|
),
|
||||||
|
vec![
|
||||||
|
(then.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
|
(
|
||||||
|
then.span.shrink_to_hi(),
|
||||||
|
format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
|
||||||
|
),
|
||||||
|
(else_.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
|
(else_.span.shrink_to_hi(), ")".to_string()),
|
||||||
|
],
|
||||||
|
MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ impl Trait for Struct {}
|
|||||||
fn foo() -> Box<dyn Trait> {
|
fn foo() -> Box<dyn Trait> {
|
||||||
Box::new(Struct)
|
Box::new(Struct)
|
||||||
}
|
}
|
||||||
|
fn bar() -> impl Trait {
|
||||||
|
Struct
|
||||||
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = if true {
|
let _ = if true {
|
||||||
Box::new(Struct)
|
Box::new(Struct)
|
||||||
@ -16,4 +19,14 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
Box::new(Struct) //~ ERROR E0308
|
Box::new(Struct) //~ ERROR E0308
|
||||||
};
|
};
|
||||||
|
let _ = if true {
|
||||||
|
Box::new(Struct) as Box<dyn Trait>
|
||||||
|
} else {
|
||||||
|
Box::new(bar()) //~ ERROR E0308
|
||||||
|
};
|
||||||
|
let _ = if true {
|
||||||
|
Box::new(bar()) as Box<dyn Trait>
|
||||||
|
} else {
|
||||||
|
Box::new(Struct) //~ ERROR E0308
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ impl Trait for Struct {}
|
|||||||
fn foo() -> Box<dyn Trait> {
|
fn foo() -> Box<dyn Trait> {
|
||||||
Box::new(Struct)
|
Box::new(Struct)
|
||||||
}
|
}
|
||||||
|
fn bar() -> impl Trait {
|
||||||
|
Struct
|
||||||
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = if true {
|
let _ = if true {
|
||||||
Struct
|
Struct
|
||||||
@ -16,4 +19,14 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
Struct //~ ERROR E0308
|
Struct //~ ERROR E0308
|
||||||
};
|
};
|
||||||
|
let _ = if true {
|
||||||
|
Struct
|
||||||
|
} else {
|
||||||
|
bar() //~ ERROR E0308
|
||||||
|
};
|
||||||
|
let _ = if true {
|
||||||
|
bar()
|
||||||
|
} else {
|
||||||
|
Struct //~ ERROR E0308
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0308]: `if` and `else` have incompatible types
|
error[E0308]: `if` and `else` have incompatible types
|
||||||
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:12:9
|
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:15:9
|
||||||
|
|
|
|
||||||
LL | let _ = if true {
|
LL | let _ = if true {
|
||||||
| _____________-
|
| _____________-
|
||||||
@ -19,7 +19,7 @@ LL | Box::new(Struct)
|
|||||||
| +++++++++ +
|
| +++++++++ +
|
||||||
|
|
||||||
error[E0308]: `if` and `else` have incompatible types
|
error[E0308]: `if` and `else` have incompatible types
|
||||||
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:17:9
|
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:20:9
|
||||||
|
|
|
|
||||||
LL | let _ = if true {
|
LL | let _ = if true {
|
||||||
| _____________-
|
| _____________-
|
||||||
@ -39,6 +39,56 @@ help: store this in the heap by calling `Box::new`
|
|||||||
LL | Box::new(Struct)
|
LL | Box::new(Struct)
|
||||||
| +++++++++ +
|
| +++++++++ +
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error[E0308]: `if` and `else` have incompatible types
|
||||||
|
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:25:9
|
||||||
|
|
|
||||||
|
LL | fn bar() -> impl Trait {
|
||||||
|
| ---------- the found opaque type
|
||||||
|
...
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________-
|
||||||
|
LL | | Struct
|
||||||
|
| | ------ expected because of this
|
||||||
|
LL | | } else {
|
||||||
|
LL | | bar()
|
||||||
|
| | ^^^^^ expected `Struct`, found opaque type
|
||||||
|
LL | | };
|
||||||
|
| |_____- `if` and `else` have incompatible types
|
||||||
|
|
|
||||||
|
= note: expected struct `Struct`
|
||||||
|
found opaque type `impl Trait`
|
||||||
|
help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>`
|
||||||
|
|
|
||||||
|
LL ~ Box::new(Struct) as Box<dyn Trait>
|
||||||
|
LL | } else {
|
||||||
|
LL ~ Box::new(bar())
|
||||||
|
|
|
||||||
|
|
||||||
|
error[E0308]: `if` and `else` have incompatible types
|
||||||
|
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:30:9
|
||||||
|
|
|
||||||
|
LL | fn bar() -> impl Trait {
|
||||||
|
| ---------- the expected opaque type
|
||||||
|
...
|
||||||
|
LL | let _ = if true {
|
||||||
|
| _____________-
|
||||||
|
LL | | bar()
|
||||||
|
| | ----- expected because of this
|
||||||
|
LL | | } else {
|
||||||
|
LL | | Struct
|
||||||
|
| | ^^^^^^ expected opaque type, found `Struct`
|
||||||
|
LL | | };
|
||||||
|
| |_____- `if` and `else` have incompatible types
|
||||||
|
|
|
||||||
|
= note: expected opaque type `impl Trait`
|
||||||
|
found struct `Struct`
|
||||||
|
help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>`
|
||||||
|
|
|
||||||
|
LL ~ Box::new(bar()) as Box<dyn Trait>
|
||||||
|
LL | } else {
|
||||||
|
LL ~ Box::new(Struct)
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user