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