|
| 1 | +use crate::{LateContext, LateLintPass, LintContext}; |
| 2 | + |
| 3 | +use rustc_errors::DelayDm; |
| 4 | +use rustc_hir as hir; |
| 5 | +use rustc_middle::{traits::util::supertraits, ty}; |
| 6 | + |
| 7 | +declare_lint! { |
| 8 | + /// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the |
| 9 | + /// `Deref` implementation with a `dyn SuperTrait` type as `Output`. |
| 10 | + /// |
| 11 | + /// These implementations will become shadowed when the `trait_upcasting` feature is stabilized. |
| 12 | + /// The `deref` functions will no longer be called implicitly, so there might be behavior change. |
| 13 | + /// |
| 14 | + /// ### Example |
| 15 | + /// |
| 16 | + /// ```rust,compile_fail |
| 17 | + /// #![deny(deref_into_dyn_supertrait)] |
| 18 | + /// #![allow(dead_code)] |
| 19 | + /// |
| 20 | + /// use core::ops::Deref; |
| 21 | + /// |
| 22 | + /// trait A {} |
| 23 | + /// trait B: A {} |
| 24 | + /// impl<'a> Deref for dyn 'a + B { |
| 25 | + /// type Target = dyn A; |
| 26 | + /// fn deref(&self) -> &Self::Target { |
| 27 | + /// todo!() |
| 28 | + /// } |
| 29 | + /// } |
| 30 | + /// |
| 31 | + /// fn take_a(_: &dyn A) { } |
| 32 | + /// |
| 33 | + /// fn take_b(b: &dyn B) { |
| 34 | + /// take_a(b); |
| 35 | + /// } |
| 36 | + /// ``` |
| 37 | + /// |
| 38 | + /// {{produces}} |
| 39 | + /// |
| 40 | + /// ### Explanation |
| 41 | + /// |
| 42 | + /// The dyn upcasting coercion feature adds new coercion rules, taking priority |
| 43 | + /// over certain other coercion rules, which will cause some behavior change. |
| 44 | + pub DEREF_INTO_DYN_SUPERTRAIT, |
| 45 | + Warn, |
| 46 | + "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", |
| 47 | + @future_incompatible = FutureIncompatibleInfo { |
| 48 | + reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>", |
| 49 | + }; |
| 50 | +} |
| 51 | + |
| 52 | +declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]); |
| 53 | + |
| 54 | +impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { |
| 55 | + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { |
| 56 | + // `Deref` is being implemented for `t` |
| 57 | + if let hir::ItemKind::Impl(impl_) = item.kind |
| 58 | + && let Some(trait_) = &impl_.of_trait |
| 59 | + && let t = cx.tcx.type_of(item.owner_id) |
| 60 | + && let opt_did @ Some(did) = trait_.trait_def_id() |
| 61 | + && opt_did == cx.tcx.lang_items().deref_trait() |
| 62 | + // `t` is `dyn t_principal` |
| 63 | + && let ty::Dynamic(data, _, ty::Dyn) = t.kind() |
| 64 | + && let Some(t_principal) = data.principal() |
| 65 | + // `<T as Deref>::Target` is `dyn target_principal` |
| 66 | + && let Some(target) = cx.get_associated_type(t, did, "Target") |
| 67 | + && let ty::Dynamic(data, _, ty::Dyn) = target.kind() |
| 68 | + && let Some(target_principal) = data.principal() |
| 69 | + // `target_principal` is a supertrait of `t_principal` |
| 70 | + && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) |
| 71 | + .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) |
| 72 | + { |
| 73 | + cx.struct_span_lint( |
| 74 | + DEREF_INTO_DYN_SUPERTRAIT, |
| 75 | + item.span, |
| 76 | + DelayDm(|| { |
| 77 | + format!( |
| 78 | + "`{t}` implements `Deref` with supertrait `{target_principal}` as output" |
| 79 | + ) |
| 80 | + }), |
| 81 | + |lint| lint, |
| 82 | + ) |
| 83 | + } |
| 84 | + } |
| 85 | +} |
0 commit comments