Skip to content

Commit 075c212

Browse files
committed
Auto merge of #3776 - notriddle:drop-bounds, r=oli-obk
Add a lint to warn on `T: Drop` bounds **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. **Known problems:** None. **Example:** ```rust fn foo<T: Drop>() {} ``` Fixes #3773
2 parents d61b254 + bc4c3b6 commit 075c212

File tree

7 files changed

+110
-1
lines changed

7 files changed

+110
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ All notable changes to this project will be documented in this file.
785785
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
786786
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
787787
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
788+
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
788789
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
789790
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
790791
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
99

10-
[There are 296 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
10+
[There are 297 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1111

1212
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1313

clippy_lints/src/drop_bounds.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
}

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ pub mod derive;
144144
pub mod doc;
145145
pub mod double_comparison;
146146
pub mod double_parens;
147+
pub mod drop_bounds;
147148
pub mod drop_forget_ref;
148149
pub mod duration_subsec;
149150
pub mod else_if_without_else;
@@ -467,6 +468,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
467468
reg.register_late_lint_pass(box derive::Derive);
468469
reg.register_late_lint_pass(box types::CharLitAsU8);
469470
reg.register_late_lint_pass(box vec::Pass);
471+
reg.register_late_lint_pass(box drop_bounds::Pass);
470472
reg.register_late_lint_pass(box drop_forget_ref::Pass);
471473
reg.register_late_lint_pass(box empty_enum::EmptyEnum);
472474
reg.register_late_lint_pass(box types::AbsurdExtremeComparisons);
@@ -653,6 +655,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
653655
derive::DERIVE_HASH_XOR_EQ,
654656
double_comparison::DOUBLE_COMPARISONS,
655657
double_parens::DOUBLE_PARENS,
658+
drop_bounds::DROP_BOUNDS,
656659
drop_forget_ref::DROP_COPY,
657660
drop_forget_ref::DROP_REF,
658661
drop_forget_ref::FORGET_COPY,
@@ -1017,6 +1020,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10171020
copies::IFS_SAME_COND,
10181021
copies::IF_SAME_THEN_ELSE,
10191022
derive::DERIVE_HASH_XOR_EQ,
1023+
drop_bounds::DROP_BOUNDS,
10201024
drop_forget_ref::DROP_COPY,
10211025
drop_forget_ref::DROP_REF,
10221026
drop_forget_ref::FORGET_COPY,

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "der
2424
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
2525
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
2626
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
27+
pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
2728
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
2829
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
2930
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];

tests/ui/drop_bounds.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![allow(unused)]
2+
fn foo<T: Drop>() {}
3+
fn bar<T>()
4+
where
5+
T: Drop,
6+
{
7+
}
8+
fn main() {}

tests/ui/drop_bounds.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
2+
--> $DIR/drop_bounds.rs:2:11
3+
|
4+
LL | fn foo<T: Drop>() {}
5+
| ^^^^
6+
|
7+
= note: #[deny(clippy::drop_bounds)] on by default
8+
9+
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
10+
--> $DIR/drop_bounds.rs:5:8
11+
|
12+
LL | T: Drop,
13+
| ^^^^
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)