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