1 use crate::lints::{DropGlue, DropTraitConstraintsDiag}; 2 use crate::LateContext; 3 use crate::LateLintPass; 4 use crate::LintContext; 5 use rustc_hir as hir; 6 use rustc_span::symbol::sym; 7 8 declare_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 45 declare_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 82 declare_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 88 impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>)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.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() { 101 continue; 102 } 103 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { 104 return 105 }; 106 cx.emit_spanned_lint( 107 DROP_BOUNDS, 108 span, 109 DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id }, 110 ); 111 } 112 } 113 } 114 check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>)115 fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { 116 let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { 117 return 118 }; 119 for bound in &bounds[..] { 120 let def_id = bound.trait_ref.trait_def_id(); 121 if cx.tcx.lang_items().drop_trait() == def_id { 122 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { 123 return 124 }; 125 cx.emit_spanned_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); 126 } 127 } 128 } 129 } 130