1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use rustc_hir as hir;
use rustc_session::{declare_lint, declare_lint_pass};

use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
    /// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
    /// supertraits.
    ///
    /// ### Example
    ///
    /// ```rust
    /// #![feature(multiple_supertrait_upcastable)]
    /// trait A {}
    /// trait B {}
    ///
    /// #[warn(multiple_supertrait_upcastable)]
    /// trait C: A + B {}
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// To support upcasting with multiple supertraits, we need to store multiple vtables and this
    /// can result in extra space overhead, even if no code actually uses upcasting.
    /// This lint allows users to identify when such scenarios occur and to decide whether the
    /// additional overhead is justified.
    pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
    Allow,
    "detect when an object-safe trait has multiple supertraits",
    @feature_gate = multiple_supertrait_upcastable;
}

declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);

impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
        let def_id = item.owner_id.to_def_id();
        // NOTE(nbdd0121): use `object_safety_violations` instead of `is_object_safe` because
        // the latter will report `where_clause_object_safety` lint.
        if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind
            && cx.tcx.is_object_safe(def_id)
        {
            let direct_super_traits_iter = cx
                .tcx
                .explicit_super_predicates_of(def_id)
                .iter_identity_copied()
                .filter_map(|(pred, _)| pred.as_trait_clause());
            if direct_super_traits_iter.count() > 1 {
                cx.emit_span_lint(
                    MULTIPLE_SUPERTRAIT_UPCASTABLE,
                    cx.tcx.def_span(def_id),
                    crate::lints::MultipleSupertraitUpcastable { ident: item.ident },
                );
            }
        }
    }
}