1 use crate::lints
::{DropGlue, DropTraitConstraintsDiag}
;
2 use crate::LateContext
;
3 use crate::LateLintPass
;
4 use crate::LintContext
;
6 use rustc_span
::symbol
::sym
;
9 /// The `drop_bounds` lint checks for generics with `std::ops::Drop` as
15 /// fn foo<T: Drop>() {}
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).
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.
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)]`.
42 "bounds of the form `T: Drop` are most likely incorrect"
46 /// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
51 /// fn foo(_x: Box<dyn Drop>) {}
58 /// A trait object bound of the form `dyn Drop` is most likely misleading
59 /// and not what the programmer intended.
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.
69 /// To write a trait object bound that accepts anything, use a placeholder
70 /// trait with a blanket implementation.
73 /// trait Placeholder {}
74 /// impl<T> Placeholder for T {}
75 /// fn foo(_x: Box<dyn Placeholder>) {}
79 "trait objects of the form `dyn Drop` are useless"
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
]
88 impl<'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
;
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 {
97 let def_id
= trait_predicate
.trait_ref
.def_id
;
98 if cx
.tcx
.lang_items().drop_trait() == Some(def_id
) {
99 // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
100 if trait_predicate
.trait_ref
.self_ty().is_impl_trait() {
103 let Some(def_id
) = cx
.tcx
.get_diagnostic_item(sym
::needs_drop
) else { return }
;
104 cx
.emit_spanned_lint(
107 DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id }
,
113 fn check_ty(&mut self, cx
: &LateContext
<'_
>, ty
: &'tcx hir
::Ty
<'tcx
>) {
114 let hir
::TyKind
::TraitObject(bounds
, _lifetime
, _syntax
) = &ty
.kind
else { return }
;
115 for bound
in &bounds
[..] {
116 let def_id
= bound
.trait_ref
.trait_def_id();
117 if cx
.tcx
.lang_items().drop_trait() == def_id
{
118 let Some(def_id
) = cx
.tcx
.get_diagnostic_item(sym
::needs_drop
) else { return }
;
119 cx
.emit_spanned_lint(DYN_DROP
, bound
.span
, DropGlue { tcx: cx.tcx, def_id }
);