rustc_lint/multiple_supertrait_upcastable.rs
1use rustc_hir as hir;
2use rustc_session::{declare_lint, declare_lint_pass};
3
4use crate::{LateContext, LateLintPass, LintContext};
5
6declare_lint! {
7 /// The `multiple_supertrait_upcastable` lint detects when a dyn-compatible trait has multiple
8 /// supertraits.
9 ///
10 /// ### Example
11 ///
12 /// ```rust
13 /// #![feature(multiple_supertrait_upcastable)]
14 /// trait A {}
15 /// trait B {}
16 ///
17 /// #[warn(multiple_supertrait_upcastable)]
18 /// trait C: A + B {}
19 /// ```
20 ///
21 /// {{produces}}
22 ///
23 /// ### Explanation
24 ///
25 /// To support upcasting with multiple supertraits, we need to store multiple vtables and this
26 /// can result in extra space overhead, even if no code actually uses upcasting.
27 /// This lint allows users to identify when such scenarios occur and to decide whether the
28 /// additional overhead is justified.
29 pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
30 Allow,
31 "detect when a dyn-compatible trait has multiple supertraits",
32 @feature_gate = multiple_supertrait_upcastable;
33}
34
35declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
36
37impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
38 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
39 let def_id = item.owner_id.to_def_id();
40 // NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because
41 // the latter will report `where_clause_object_safety` lint.
42 if let hir::ItemKind::Trait(_, _, ident, ..) = item.kind
43 && cx.tcx.is_dyn_compatible(def_id)
44 {
45 let direct_super_traits_iter = cx
46 .tcx
47 .explicit_super_predicates_of(def_id)
48 .iter_identity_copied()
49 .filter_map(|(pred, _)| pred.as_trait_clause());
50 if direct_super_traits_iter.count() > 1 {
51 cx.emit_span_lint(
52 MULTIPLE_SUPERTRAIT_UPCASTABLE,
53 cx.tcx.def_span(def_id),
54 crate::lints::MultipleSupertraitUpcastable { ident },
55 );
56 }
57 }
58 }
59}