rust/clippy_lints/src/drop_bounds.rs

71 lines
2.7 KiB
Rust
Raw Normal View History

2019-05-14 03:06:21 -05:00
use crate::utils::{match_def_path, paths, span_lint};
use if_chain::if_chain;
2019-12-03 17:16:03 -06:00
use rustc::declare_lint_pass;
use rustc::hir::*;
use rustc::lint::{LateLintPass, LintArray, LintPass};
2019-12-03 17:16:03 -06:00
use rustc_session::declare_tool_lint;
declare_clippy_lint! {
/// **What it does:** Checks for generics with `std::ops::Drop` as bounds.
///
/// **Why is this bad?** `Drop` bounds do not really accomplish anything.
/// A type may have compiler-generated drop glue without implementing the
/// `Drop` trait itself. The `Drop` trait also only has one method,
/// `Drop::drop`, and that function is by fiat not callable in user code.
/// So there is really no use case for using `Drop` in trait bounds.
///
/// The most likely use case of a drop bound is to distinguish between types
/// that have destructors and types that don't. Combined with specialization,
/// a naive coder would write an implementation that assumed a type could be
/// trivially dropped, then write a specialization for `T: Drop` that actually
/// calls the destructor. Except that doing so is not correct; String, for
/// example, doesn't actually implement Drop, but because String contains a
/// Vec, assuming it can be trivially dropped will leak memory.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo<T: Drop>() {}
/// ```
pub DROP_BOUNDS,
correctness,
"Bounds of the form `T: Drop` are useless"
}
const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
Use `std::mem::needs_drop` to detect if a type has drop glue.";
2019-04-08 15:43:55 -05:00
declare_lint_pass!(DropBounds => [DROP_BOUNDS]);
2019-04-08 15:43:55 -05:00
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropBounds {
fn check_generic_param(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam) {
for bound in &p.bounds {
lint_bound(cx, bound);
}
}
fn check_where_predicate(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate) {
if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p {
for bound in bounds {
lint_bound(cx, bound);
}
}
}
}
fn lint_bound<'a, 'tcx>(cx: &rustc::lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound) {
if_chain! {
if let GenericBound::Trait(t, _) = bound;
if let Some(def_id) = t.trait_ref.path.res.opt_def_id();
2019-05-17 16:53:54 -05:00
if match_def_path(cx, def_id, &paths::DROP_TRAIT);
then {
span_lint(
cx,
DROP_BOUNDS,
t.span,
DROP_BOUNDS_SUMMARY
);
}
}
}