| use crate::utils::{match_def_path, paths, span_lint}; |
| use if_chain::if_chain; |
| use rustc_hir::{GenericBound, GenericParam, WhereBoundPredicate, WherePredicate}; |
| use rustc_lint::{LateContext, LateLintPass}; |
| use rustc_session::{declare_lint_pass, declare_tool_lint}; |
| |
| declare_clippy_lint! { |
| /// **What it does:** Checks for generics with `std::ops::Drop` as bounds. |
| /// |
| /// **Why is this bad?** `Drop` bounds do not really accomplish anything. |
| /// A type may have compiler-generated drop glue without implementing the |
| /// `Drop` trait itself. The `Drop` trait also only has one method, |
| /// `Drop::drop`, and that function is by fiat not callable in user code. |
| /// So there is really no use case for using `Drop` in trait bounds. |
| /// |
| /// The most likely use case of a drop bound is to distinguish between types |
| /// that have destructors and types that don't. Combined with specialization, |
| /// a naive coder would write an implementation that assumed a type could be |
| /// trivially dropped, then write a specialization for `T: Drop` that actually |
| /// calls the destructor. Except that doing so is not correct; String, for |
| /// example, doesn't actually implement Drop, but because String contains a |
| /// Vec, assuming it can be trivially dropped will leak memory. |
| /// |
| /// **Known problems:** None. |
| /// |
| /// **Example:** |
| /// ```rust |
| /// fn foo<T: Drop>() {} |
| /// ``` |
| /// Could be written as: |
| /// ```rust |
| /// fn foo<T>() {} |
| /// ``` |
| pub DROP_BOUNDS, |
| correctness, |
| "bounds of the form `T: Drop` are useless" |
| } |
| |
| const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \ |
| use `std::mem::needs_drop` to detect if a type has drop glue"; |
| |
| declare_lint_pass!(DropBounds => [DROP_BOUNDS]); |
| |
| impl<'tcx> LateLintPass<'tcx> for DropBounds { |
| fn check_generic_param(&mut self, cx: &LateContext<'tcx>, p: &'tcx GenericParam<'_>) { |
| for bound in p.bounds.iter() { |
| lint_bound(cx, bound); |
| } |
| } |
| fn check_where_predicate(&mut self, cx: &LateContext<'tcx>, p: &'tcx WherePredicate<'_>) { |
| if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p { |
| for bound in *bounds { |
| lint_bound(cx, bound); |
| } |
| } |
| } |
| } |
| |
| fn lint_bound<'tcx>(cx: &LateContext<'tcx>, bound: &'tcx GenericBound<'_>) { |
| if_chain! { |
| if let GenericBound::Trait(t, _) = bound; |
| if let Some(def_id) = t.trait_ref.path.res.opt_def_id(); |
| if match_def_path(cx, def_id, &paths::DROP_TRAIT); |
| then { |
| span_lint( |
| cx, |
| DROP_BOUNDS, |
| t.span, |
| DROP_BOUNDS_SUMMARY |
| ); |
| } |
| } |
| } |