diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2024-01-23 04:42:26 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2024-01-23 04:42:26 +0000 |
| commit | 34f4f3da4f3e2ac8fa086c4d3a3c89d49d23a263 (patch) | |
| tree | a5ba391740b4306b5237ce8f3616d4d5934ab25b /compiler | |
| parent | ac56a2b564a3e15b8377e72294a3d565a1c8c659 (diff) | |
| download | rust-34f4f3da4f3e2ac8fa086c4d3a3c89d49d23a263.tar.gz rust-34f4f3da4f3e2ac8fa086c4d3a3c89d49d23a263.zip | |
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)
};
```
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs | 86 |
1 files changed, 70 insertions, 16 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index a0dfaf33a6e..01cd3c57925 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -294,8 +294,9 @@ impl<T> Trait<T> for X { ); } } - (ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias)) - if alias.def_id.is_local() + (_, ty::Alias(ty::Opaque, opaque_ty)) + | (ty::Alias(ty::Opaque, opaque_ty), _) => { + if opaque_ty.def_id.is_local() && matches!( tcx.def_kind(body_owner_def_id), DefKind::Fn @@ -303,21 +304,74 @@ impl<T> Trait<T> for X { | DefKind::Const | DefKind::AssocFn | DefKind::AssocConst - ) => - { - if tcx.is_type_alias_impl_trait(alias.def_id) { - if !tcx + ) + && tcx.is_type_alias_impl_trait(opaque_ty.def_id) + && !tcx .opaque_types_defined_by(body_owner_def_id.expect_local()) - .contains(&alias.def_id.expect_local()) - { - let sp = tcx - .def_ident_span(body_owner_def_id) - .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); - diag.span_note( - sp, - "\ - this item must have the opaque type in its signature \ - in order to be able to register hidden types", + .contains(&opaque_ty.def_id.expect_local()) + { + let sp = tcx + .def_ident_span(body_owner_def_id) + .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); + diag.span_note( + sp, + "this item must have the opaque type in its signature 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, ); } } |
