|
| 1 | +use crate::utils::{match_def_path, paths, span_lint}; |
| 2 | +use if_chain::if_chain; |
| 3 | +use rustc::hir::*; |
| 4 | +use rustc::lint::{LateLintPass, LintArray, LintPass}; |
| 5 | +use rustc::{declare_tool_lint, lint_array}; |
| 6 | + |
| 7 | +/// **What it does:** Checks for generics with `std::ops::Drop` as bounds. |
| 8 | +/// |
| 9 | +/// **Why is this bad?** `Drop` bounds do not really accomplish anything. |
| 10 | +/// A type may have compiler-generated drop glue without implementing the |
| 11 | +/// `Drop` trait itself. The `Drop` trait also only has one method, |
| 12 | +/// `Drop::drop`, and that function is by fiat not callable in user code. |
| 13 | +/// So there is really no use case for using `Drop` in trait bounds. |
| 14 | +/// |
| 15 | +/// The most likely use case of a drop bound is to distinguish between types |
| 16 | +/// that have destructors and types that don't. Combined with specialization, |
| 17 | +/// a naive coder would write an implementation that assumed a type could be |
| 18 | +/// trivially dropped, then write a specialization for `T: Drop` that actually |
| 19 | +/// calls the destructor. Except that doing so is not correct; String, for |
| 20 | +/// example, doesn't actually implement Drop, but because String contains a |
| 21 | +/// Vec, assuming it can be trivially dropped will leak memory. |
| 22 | +/// |
| 23 | +/// **Known problems:** None. |
| 24 | +/// |
| 25 | +/// **Example:** |
| 26 | +/// ```rust |
| 27 | +/// fn foo<T: Drop>() {} |
| 28 | +/// ``` |
| 29 | +declare_clippy_lint! { |
| 30 | + pub DROP_BOUNDS, |
| 31 | + correctness, |
| 32 | + "Bounds of the form `T: Drop` are useless" |
| 33 | +} |
| 34 | + |
| 35 | +const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ |
| 36 | + Use `std::mem::needs_drop` to detect if a type has drop glue."; |
| 37 | + |
| 38 | +pub struct Pass; |
| 39 | + |
| 40 | +impl LintPass for Pass { |
| 41 | + fn get_lints(&self) -> LintArray { |
| 42 | + lint_array!(DROP_BOUNDS) |
| 43 | + } |
| 44 | + |
| 45 | + fn name(&self) -> &'static str { |
| 46 | + "DropBounds" |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { |
| 51 | + fn check_generic_param(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam) { |
| 52 | + for bound in &p.bounds { |
| 53 | + lint_bound(cx, bound); |
| 54 | + } |
| 55 | + } |
| 56 | + fn check_where_predicate(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate) { |
| 57 | + if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p { |
| 58 | + for bound in bounds { |
| 59 | + lint_bound(cx, bound); |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +fn lint_bound<'a, 'tcx>(cx: &rustc::lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound) { |
| 66 | + if_chain! { |
| 67 | + if let GenericBound::Trait(t, _) = bound; |
| 68 | + if let Some(def_id) = t.trait_ref.path.def.opt_def_id(); |
| 69 | + if match_def_path(cx.tcx, def_id, &paths::DROP_TRAIT); |
| 70 | + then { |
| 71 | + span_lint( |
| 72 | + cx, |
| 73 | + DROP_BOUNDS, |
| 74 | + t.span, |
| 75 | + DROP_BOUNDS_SUMMARY |
| 76 | + ); |
| 77 | + } |
| 78 | + } |
| 79 | +} |
0 commit comments