]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/drop_bounds.rs
Auto merge of #3808 - mikerite:useless-format-suggestions, r=oli-obk
[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::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 }