]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/drop_bounds.rs
Add a lint to warn on `T: Drop` bounds
[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 /// **Known problems:** None.
16 ///
17 /// **Example:**
18 /// ```rust
19 /// fn foo<T: Drop>() {}
20 /// ```
21 declare_clippy_lint! {
22     pub DROP_BOUNDS,
23     correctness,
24     "Bounds of the form `T: Drop` are useless"
25 }
26
27 const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
28                                    Use `std::mem::needs_drop` to detect if a type has drop glue.";
29
30 pub struct Pass;
31
32 impl LintPass for Pass {
33     fn get_lints(&self) -> LintArray {
34         lint_array!(DROP_BOUNDS)
35     }
36
37     fn name(&self) -> &'static str {
38         "DropBounds"
39     }
40 }
41
42 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
43     fn check_generic_param(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam) {
44         for bound in &p.bounds {
45             lint_bound(cx, bound);
46         }
47     }
48     fn check_where_predicate(&mut self, cx: &rustc::lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate) {
49         if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p {
50             for bound in bounds {
51                 lint_bound(cx, bound);
52             }
53         }
54     }
55 }
56
57 fn lint_bound<'a, 'tcx>(cx: &rustc::lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound) {
58     if_chain! {
59         if let GenericBound::Trait(t, _) = bound;
60         if let Some(def_id) = t.trait_ref.path.def.opt_def_id();
61         if match_def_path(cx.tcx, def_id, &paths::DROP_TRAIT);
62         then {
63             span_lint(
64                 cx,
65                 DROP_BOUNDS,
66                 t.span,
67                 DROP_BOUNDS_SUMMARY
68             );
69         }
70     }
71 }