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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
use rustc_errors::fluent;
use rustc_hir as hir;
use rustc_span::symbol::sym;
declare_lint! {
/// The `drop_bounds` lint checks for generics with `std::ops::Drop` as
/// bounds.
///
/// ### Example
///
/// ```rust
/// fn foo<T: Drop>() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// A generic trait bound of the form `T: Drop` is most likely misleading
/// and not what the programmer intended (they probably should have used
/// `std::mem::needs_drop` instead).
///
/// `Drop` bounds do not actually indicate whether a type can be trivially
/// dropped or not, because a composite type containing `Drop` types does
/// not necessarily implement `Drop` itself. Naïvely, one might be tempted
/// to write an implementation that assumes that a type can be trivially
/// dropped while also supplying a specialization for `T: Drop` that
/// actually calls the destructor. However, this breaks down e.g. when `T`
/// is `String`, which does not implement `Drop` itself but contains a
/// `Vec`, which does implement `Drop`, so assuming `T` can be trivially
/// dropped would lead to a memory leak here.
///
/// Furthermore, the `Drop` trait only contains one method, `Drop::drop`,
/// which may not be called explicitly in user code (`E0040`), so there is
/// really no use case for using `Drop` in trait bounds, save perhaps for
/// some obscure corner cases, which can use `#[allow(drop_bounds)]`.
pub DROP_BOUNDS,
Warn,
"bounds of the form `T: Drop` are most likely incorrect"
}
declare_lint! {
/// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
///
/// ### Example
///
/// ```rust
/// fn foo(_x: Box<dyn Drop>) {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// A trait object bound of the form `dyn Drop` is most likely misleading
/// and not what the programmer intended.
///
/// `Drop` bounds do not actually indicate whether a type can be trivially
/// dropped or not, because a composite type containing `Drop` types does
/// not necessarily implement `Drop` itself. Naïvely, one might be tempted
/// to write a deferred drop system, to pull cleaning up memory out of a
/// latency-sensitive code path, using `dyn Drop` trait objects. However,
/// this breaks down e.g. when `T` is `String`, which does not implement
/// `Drop`, but should probably be accepted.
///
/// To write a trait object bound that accepts anything, use a placeholder
/// trait with a blanket implementation.
///
/// ```rust
/// trait Placeholder {}
/// impl<T> Placeholder for T {}
/// fn foo(_x: Box<dyn Placeholder>) {}
/// ```
pub DYN_DROP,
Warn,
"trait objects of the form `dyn Drop` are useless"
}
declare_lint_pass!(
/// Lint for bounds of the form `T: Drop`, which usually
/// indicate an attempt to emulate `std::mem::needs_drop`.
DropTraitConstraints => [DROP_BOUNDS, DYN_DROP]
);
impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
use rustc_middle::ty::PredicateKind::*;
let predicates = cx.tcx.explicit_predicates_of(item.def_id);
for &(predicate, span) in predicates.predicates {
let Trait(trait_predicate) = predicate.kind().skip_binder() else {
continue
};
let def_id = trait_predicate.trait_ref.def_id;
if cx.tcx.lang_items().drop_trait() == Some(def_id) {
// Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue;
}
cx.struct_span_lint(DROP_BOUNDS, span, |lint| {
let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
return
};
lint.build(fluent::lint::drop_trait_constraints)
.set_arg("predicate", predicate)
.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
.emit();
});
}
}
}
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else {
return
};
for bound in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id();
if cx.tcx.lang_items().drop_trait() == def_id {
cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
return
};
lint.build(fluent::lint::drop_glue)
.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
.emit();
});
}
}
}
}