rustc_lint/
traits.rs

1use rustc_hir::{self as hir, AmbigArg, LangItem};
2use rustc_session::{declare_lint, declare_lint_pass};
3use rustc_span::sym;
4
5use crate::lints::{DropGlue, DropTraitConstraintsDiag};
6use crate::{LateContext, LateLintPass, LintContext};
7
8declare_lint! {
9    /// The `drop_bounds` lint checks for generics with `std::ops::Drop` as
10    /// bounds.
11    ///
12    /// ### Example
13    ///
14    /// ```rust
15    /// fn foo<T: Drop>() {}
16    /// ```
17    ///
18    /// {{produces}}
19    ///
20    /// ### Explanation
21    ///
22    /// A generic trait bound of the form `T: Drop` is most likely misleading
23    /// and not what the programmer intended (they probably should have used
24    /// `std::mem::needs_drop` instead).
25    ///
26    /// `Drop` bounds do not actually indicate whether a type can be trivially
27    /// dropped or not, because a composite type containing `Drop` types does
28    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
29    /// to write an implementation that assumes that a type can be trivially
30    /// dropped while also supplying a specialization for `T: Drop` that
31    /// actually calls the destructor. However, this breaks down e.g. when `T`
32    /// is `String`, which does not implement `Drop` itself but contains a
33    /// `Vec`, which does implement `Drop`, so assuming `T` can be trivially
34    /// dropped would lead to a memory leak here.
35    ///
36    /// Furthermore, the `Drop` trait only contains one method, `Drop::drop`,
37    /// which may not be called explicitly in user code (`E0040`), so there is
38    /// really no use case for using `Drop` in trait bounds, save perhaps for
39    /// some obscure corner cases, which can use `#[allow(drop_bounds)]`.
40    pub DROP_BOUNDS,
41    Warn,
42    "bounds of the form `T: Drop` are most likely incorrect"
43}
44
45declare_lint! {
46    /// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
47    ///
48    /// ### Example
49    ///
50    /// ```rust
51    /// fn foo(_x: Box<dyn Drop>) {}
52    /// ```
53    ///
54    /// {{produces}}
55    ///
56    /// ### Explanation
57    ///
58    /// A trait object bound of the form `dyn Drop` is most likely misleading
59    /// and not what the programmer intended.
60    ///
61    /// `Drop` bounds do not actually indicate whether a type can be trivially
62    /// dropped or not, because a composite type containing `Drop` types does
63    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
64    /// to write a deferred drop system, to pull cleaning up memory out of a
65    /// latency-sensitive code path, using `dyn Drop` trait objects. However,
66    /// this breaks down e.g. when `T` is `String`, which does not implement
67    /// `Drop`, but should probably be accepted.
68    ///
69    /// To write a trait object bound that accepts anything, use a placeholder
70    /// trait with a blanket implementation.
71    ///
72    /// ```rust
73    /// trait Placeholder {}
74    /// impl<T> Placeholder for T {}
75    /// fn foo(_x: Box<dyn Placeholder>) {}
76    /// ```
77    pub DYN_DROP,
78    Warn,
79    "trait objects of the form `dyn Drop` are useless"
80}
81
82declare_lint_pass!(
83    /// Lint for bounds of the form `T: Drop`, which usually
84    /// indicate an attempt to emulate `std::mem::needs_drop`.
85    DropTraitConstraints => [DROP_BOUNDS, DYN_DROP]
86);
87
88impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
89    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
90        use rustc_middle::ty::ClauseKind;
91
92        let predicates = cx.tcx.explicit_predicates_of(item.owner_id);
93        for &(predicate, span) in predicates.predicates {
94            let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else {
95                continue;
96            };
97            let def_id = trait_predicate.trait_ref.def_id;
98            if cx.tcx.is_lang_item(def_id, LangItem::Drop) {
99                // Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern.
100                if trait_predicate.trait_ref.self_ty().is_impl_trait() {
101                    continue;
102                }
103                let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
104                cx.emit_span_lint(
105                    DROP_BOUNDS,
106                    span,
107                    DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id },
108                );
109            }
110        }
111    }
112
113    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
114        let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else {
115            return;
116        };
117        for bound in &bounds[..] {
118            let def_id = bound.trait_ref.trait_def_id();
119            if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) {
120                let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
121                cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
122            }
123        }
124    }
125}