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