Skip to main content

rustc_lint/
deref_into_dyn_supertrait.rs

1use rustc_hir as hir;
2use rustc_hir::attrs::lang_items::LangItem;
3use rustc_middle::ty;
4use rustc_session::{declare_lint, declare_lint_pass};
5use rustc_span::{Ident, sym};
6use rustc_trait_selection::traits::supertraits;
7
8use crate::diagnostics::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel};
9use crate::{LateContext, LateLintPass, LintContext};
10
11#[doc =
r" The `deref_into_dyn_supertrait` lint is emitted whenever there is a `Deref` implementation"]
#[doc =
r" for `dyn SubTrait` with a `dyn SuperTrait` type as the `Output` type."]
#[doc = r""]
#[doc =
r#" These implementations are "shadowed" by trait upcasting (stabilized since"#]
#[doc =
r" 1.86.0). The `deref` functions is no longer called implicitly, which might"]
#[doc = r" change behavior compared to previous rustc versions."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(deref_into_dyn_supertrait)]"]
#[doc = r" #![allow(dead_code)]"]
#[doc = r""]
#[doc = r" use core::ops::Deref;"]
#[doc = r""]
#[doc = r" trait A {}"]
#[doc = r" trait B: A {}"]
#[doc = r" impl<'a> Deref for dyn 'a + B {"]
#[doc = r"     type Target = dyn A;"]
#[doc = r"     fn deref(&self) -> &Self::Target {"]
#[doc = r"         todo!()"]
#[doc = r"     }"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" fn take_a(_: &dyn A) { }"]
#[doc = r""]
#[doc = r" fn take_b(b: &dyn B) {"]
#[doc = r"     take_a(b);"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" The trait upcasting coercion added a new coercion rule, taking priority over certain other"]
#[doc =
r" coercion rules, which causes some behavior change compared to older `rustc` versions."]
#[doc = r""]
#[doc =
r" `deref` can be still called explicitly, it just isn't called as part of a deref coercion"]
#[doc = r" (since trait upcasting coercion takes priority)."]
pub static DEREF_INTO_DYN_SUPERTRAIT: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "DEREF_INTO_DYN_SUPERTRAIT",
            default_level: ::rustc_lint_defs::Allow,
            desc: "`Deref` implementation with a supertrait trait object for output is shadowed by trait upcasting",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
12    /// The `deref_into_dyn_supertrait` lint is emitted whenever there is a `Deref` implementation
13    /// for `dyn SubTrait` with a `dyn SuperTrait` type as the `Output` type.
14    ///
15    /// These implementations are "shadowed" by trait upcasting (stabilized since
16    /// 1.86.0). The `deref` functions is no longer called implicitly, which might
17    /// change behavior compared to previous rustc versions.
18    ///
19    /// ### Example
20    ///
21    /// ```rust,compile_fail
22    /// #![deny(deref_into_dyn_supertrait)]
23    /// #![allow(dead_code)]
24    ///
25    /// use core::ops::Deref;
26    ///
27    /// trait A {}
28    /// trait B: A {}
29    /// impl<'a> Deref for dyn 'a + B {
30    ///     type Target = dyn A;
31    ///     fn deref(&self) -> &Self::Target {
32    ///         todo!()
33    ///     }
34    /// }
35    ///
36    /// fn take_a(_: &dyn A) { }
37    ///
38    /// fn take_b(b: &dyn B) {
39    ///     take_a(b);
40    /// }
41    /// ```
42    ///
43    /// {{produces}}
44    ///
45    /// ### Explanation
46    ///
47    /// The trait upcasting coercion added a new coercion rule, taking priority over certain other
48    /// coercion rules, which causes some behavior change compared to older `rustc` versions.
49    ///
50    /// `deref` can be still called explicitly, it just isn't called as part of a deref coercion
51    /// (since trait upcasting coercion takes priority).
52    pub DEREF_INTO_DYN_SUPERTRAIT,
53    Allow,
54    "`Deref` implementation with a supertrait trait object for output is shadowed by trait upcasting",
55}
56
57pub struct DerefIntoDynSupertrait;
#[automatically_derived]
impl ::core::marker::Copy for DerefIntoDynSupertrait { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DerefIntoDynSupertrait { }
#[automatically_derived]
impl ::core::clone::Clone for DerefIntoDynSupertrait {
    #[inline]
    fn clone(&self) -> DerefIntoDynSupertrait { *self }
}
impl ::rustc_lint_defs::LintPass for DerefIntoDynSupertrait {
    fn name(&self) -> &'static str { "DerefIntoDynSupertrait" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [DEREF_INTO_DYN_SUPERTRAIT]))
    }
}
impl DerefIntoDynSupertrait {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [DEREF_INTO_DYN_SUPERTRAIT]))
    }
}declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]);
58
59impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
60    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
61        let tcx = cx.tcx;
62        // `Deref` is being implemented for `t`
63        if let hir::ItemKind::Impl(impl_) = item.kind
64            // the trait is a `Deref` implementation
65            && let Some(of_trait) = &impl_.of_trait
66            && let Some(did) = of_trait.trait_ref.trait_def_id()
67            && tcx.is_lang_item(did, LangItem::Deref)
68            // the self type is `dyn t_principal`
69            && let self_ty = tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip()
70            && let ty::Dynamic(data, _) = self_ty.kind()
71            && let Some(self_principal) = data.principal()
72            // `<T as Deref>::Target` is `dyn target_principal`
73            && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target)
74            && let ty::Dynamic(data, _) = target.kind()
75            && let Some(target_principal) = data.principal()
76            // `target_principal` is a supertrait of `t_principal`
77            && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty))
78                .find(|supertrait| supertrait.def_id() == target_principal.def_id())
79        {
80            // erase regions in self type for better diagnostic presentation
81            let (self_ty, target_principal, supertrait_principal) =
82                tcx.erase_and_anonymize_regions((self_ty, target_principal, supertrait_principal));
83            let label2 = tcx
84                .associated_items(item.owner_id)
85                .find_by_ident_and_kind(
86                    tcx,
87                    Ident::with_dummy_span(sym::Target),
88                    ty::AssocTag::Type,
89                    item.owner_id.to_def_id(),
90                )
91                .map(|label| SupertraitAsDerefTargetLabel {
92                    label: tcx.def_span(label.def_id),
93                    self_ty,
94                });
95            let span = tcx.def_span(item.owner_id.def_id);
96            cx.emit_span_lint(
97                DEREF_INTO_DYN_SUPERTRAIT,
98                span,
99                SupertraitAsDerefTarget {
100                    self_ty,
101                    supertrait_principal: supertrait_principal.map_bound(|trait_ref| {
102                        ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
103                    }),
104                    target_principal,
105                    label: span,
106                    label2,
107                },
108            );
109        }
110    }
111}