Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 28cdbc2

Browse files
committed
Uplift clippy::drop_ref to rustc
1 parent 7dab609 commit 28cdbc2

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass
520520
.specifically = this associated type bound is unsatisfied for `{$proj_ty}`
521521
522522
lint_opaque_hidden_inferred_bound_sugg = add this bound
523+
524+
lint_drop_ref = calls to `std::mem::drop` with a reference instead of an owned value
525+
.note = argument has type `{$arg_ty}`
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use rustc_hir::{Arm, Expr, ExprKind, Node};
2+
use rustc_span::sym;
3+
4+
use crate::{lints::DropRefDiag, LateContext, LateLintPass, LintContext};
5+
6+
declare_lint! {
7+
/// The `drop_ref` lint checks for calls to `std::mem::drop` with a reference
8+
/// instead of an owned value.
9+
///
10+
/// ### Example
11+
///
12+
/// ```rust
13+
/// # fn operation_that_requires_mutex_to_be_unlocked() {} // just to make it compile
14+
/// # let mutex = std::sync::Mutex::new(1); // just to make it compile
15+
/// let mut lock_guard = mutex.lock();
16+
/// std::mem::drop(&lock_guard); // Should have been drop(lock_guard), mutex
17+
/// // still locked
18+
/// operation_that_requires_mutex_to_be_unlocked();
19+
/// ```
20+
///
21+
/// {{produces}}
22+
///
23+
/// ### Explanation
24+
///
25+
/// Calling `drop` on a reference will only drop the
26+
/// reference itself, which is a no-op. It will not call the `drop` method (from
27+
/// the `Drop` trait implementation) on the underlying referenced value, which
28+
/// is likely what was intended.
29+
pub DROP_REF,
30+
Warn,
31+
"calls to `std::mem::drop` with a reference instead of an owned value"
32+
}
33+
34+
declare_lint_pass!(DropForgetUseless => [DROP_REF]);
35+
36+
impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
37+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
38+
if let ExprKind::Call(path, [arg]) = expr.kind
39+
&& let ExprKind::Path(ref qpath) = path.kind
40+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
41+
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
42+
{
43+
let arg_ty = cx.typeck_results().expr_ty(arg);
44+
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
45+
match fn_name {
46+
sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => {
47+
cx.emit_spanned_lint(DROP_REF, expr.span, DropRefDiag { arg_ty, note: arg.span });
48+
},
49+
_ => return,
50+
};
51+
}
52+
}
53+
}
54+
55+
// Dropping returned value of a function, as in the following snippet is considered idiomatic, see
56+
// rust-lang/rust-clippy#9482 for examples.
57+
//
58+
// ```
59+
// match <var> {
60+
// <pat> => drop(fn_with_side_effect_and_returning_some_value()),
61+
// ..
62+
// }
63+
// ```
64+
fn is_single_call_in_arm<'tcx>(
65+
cx: &LateContext<'tcx>,
66+
arg: &'tcx Expr<'_>,
67+
drop_expr: &'tcx Expr<'_>,
68+
) -> bool {
69+
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
70+
let parent_node = cx.tcx.hir().find_parent(drop_expr.hir_id);
71+
if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
72+
return body.hir_id == drop_expr.hir_id;
73+
}
74+
}
75+
false
76+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod array_into_iter;
5252
pub mod builtin;
5353
mod context;
5454
mod deref_into_dyn_supertrait;
55+
mod drop_forget_useless;
5556
mod early;
5657
mod enum_intrinsics_non_enums;
5758
mod errors;
@@ -96,6 +97,7 @@ use rustc_span::Span;
9697
use array_into_iter::ArrayIntoIter;
9798
use builtin::*;
9899
use deref_into_dyn_supertrait::*;
100+
use drop_forget_useless::*;
99101
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
100102
use for_loops_over_fallibles::*;
101103
use hidden_unicode_codepoints::*;
@@ -201,6 +203,7 @@ late_lint_methods!(
201203
[
202204
ForLoopsOverFallibles: ForLoopsOverFallibles,
203205
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
206+
DropForgetUseless: DropForgetUseless,
204207
HardwiredLints: HardwiredLints,
205208
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
206209
ImproperCTypesDefinitions: ImproperCTypesDefinitions,

compiler/rustc_lint/src/lints.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,15 @@ pub struct ForLoopsOverFalliblesSuggestion<'a> {
662662
pub end_span: Span,
663663
}
664664

665+
// drop_ref.rs
666+
#[derive(LintDiagnostic)]
667+
#[diag(lint_drop_ref)]
668+
pub struct DropRefDiag<'a> {
669+
pub arg_ty: Ty<'a>,
670+
#[note]
671+
pub note: Span,
672+
}
673+
665674
// hidden_unicode_codepoints.rs
666675
#[derive(LintDiagnostic)]
667676
#[diag(lint_hidden_unicode_codepoints)]

tests/ui/lint/drop_ref.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// check-pass
2+
3+
#![warn(drop_ref)]
4+
5+
struct SomeStruct;
6+
7+
fn main() {
8+
drop(&SomeStruct); //~ WARN calls to `std::mem::drop`
9+
10+
let mut owned1 = SomeStruct;
11+
drop(&owned1); //~ WARN calls to `std::mem::drop`
12+
drop(&&owned1); //~ WARN calls to `std::mem::drop`
13+
drop(&mut owned1); //~ WARN calls to `std::mem::drop`
14+
drop(owned1);
15+
16+
let reference1 = &SomeStruct;
17+
drop(reference1); //~ WARN calls to `std::mem::drop`
18+
19+
let reference2 = &mut SomeStruct;
20+
drop(reference2); //~ WARN calls to `std::mem::drop`
21+
22+
let ref reference3 = SomeStruct;
23+
drop(reference3); //~ WARN calls to `std::mem::drop`
24+
}
25+
26+
#[allow(dead_code)]
27+
fn test_generic_fn_drop<T>(val: T) {
28+
drop(&val); //~ WARN calls to `std::mem::drop`
29+
drop(val);
30+
}
31+
32+
#[allow(dead_code)]
33+
fn test_similarly_named_function() {
34+
fn drop<T>(_val: T) {}
35+
drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name
36+
std::mem::drop(&SomeStruct); //~ WARN calls to `std::mem::drop`
37+
}
38+
39+
#[derive(Copy, Clone)]
40+
pub struct Error;
41+
fn produce_half_owl_error() -> Result<(), Error> {
42+
Ok(())
43+
}
44+
45+
fn produce_half_owl_ok() -> Result<bool, ()> {
46+
Ok(true)
47+
}
48+
49+
#[allow(dead_code)]
50+
fn test_owl_result() -> Result<(), ()> {
51+
produce_half_owl_error().map_err(|_| ())?;
52+
produce_half_owl_ok().map(|_| ())?;
53+
// the following should not be linted,
54+
// we should not force users to use toilet closures
55+
// to produce owl results when drop is more convenient
56+
produce_half_owl_error().map_err(drop)?;
57+
produce_half_owl_ok().map_err(drop)?;
58+
Ok(())
59+
}
60+
61+
#[allow(dead_code)]
62+
fn test_owl_result_2() -> Result<u8, ()> {
63+
produce_half_owl_error().map_err(|_| ())?;
64+
produce_half_owl_ok().map(|_| ())?;
65+
// the following should not be linted,
66+
// we should not force users to use toilet closures
67+
// to produce owl results when drop is more convenient
68+
produce_half_owl_error().map_err(drop)?;
69+
produce_half_owl_ok().map(drop)?;
70+
Ok(1)
71+
}
72+
73+
#[allow(unused)]
74+
#[allow(clippy::unit_cmp)]
75+
fn issue10122(x: u8) {
76+
// This is a function which returns a reference and has a side-effect, which means
77+
// that calling drop() on the function is considered an idiomatic way of achieving
78+
// the side-effect in a match arm.
79+
fn println_and<T>(t: &T) -> &T {
80+
println!("foo");
81+
t
82+
}
83+
84+
match x {
85+
// Don't lint (copy type), we only care about side-effects
86+
0 => drop(println_and(&12)),
87+
// Don't lint (no copy type), we only care about side-effects
88+
1 => drop(println_and(&String::new())),
89+
2 => {
90+
// Lint, even if we only care about the side-effect, it's already in a block
91+
drop(println_and(&13)); //~ WARN calls to `std::mem::drop`
92+
},
93+
// Lint, idiomatic use is only in body of `Arm`
94+
3 if drop(println_and(&14)) == () => (), //~ WARN calls to `std::mem::drop`
95+
// Lint, not a fn/method call
96+
4 => drop(&2), //~ WARN calls to `std::mem::drop`
97+
_ => (),
98+
}
99+
}

tests/ui/lint/drop_ref.stderr

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
warning: calls to `std::mem::drop` with a reference instead of an owned value
2+
--> $DIR/drop_ref.rs:8:5
3+
|
4+
LL | drop(&SomeStruct);
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
note: argument has type `&SomeStruct`
8+
--> $DIR/drop_ref.rs:8:10
9+
|
10+
LL | drop(&SomeStruct);
11+
| ^^^^^^^^^^^
12+
note: the lint level is defined here
13+
--> $DIR/drop_ref.rs:3:9
14+
|
15+
LL | #![warn(drop_ref)]
16+
| ^^^^^^^^
17+
18+
warning: calls to `std::mem::drop` with a reference instead of an owned value
19+
--> $DIR/drop_ref.rs:11:5
20+
|
21+
LL | drop(&owned1);
22+
| ^^^^^^^^^^^^^
23+
|
24+
note: argument has type `&SomeStruct`
25+
--> $DIR/drop_ref.rs:11:10
26+
|
27+
LL | drop(&owned1);
28+
| ^^^^^^^
29+
30+
warning: calls to `std::mem::drop` with a reference instead of an owned value
31+
--> $DIR/drop_ref.rs:12:5
32+
|
33+
LL | drop(&&owned1);
34+
| ^^^^^^^^^^^^^^
35+
|
36+
note: argument has type `&&SomeStruct`
37+
--> $DIR/drop_ref.rs:12:10
38+
|
39+
LL | drop(&&owned1);
40+
| ^^^^^^^^
41+
42+
warning: calls to `std::mem::drop` with a reference instead of an owned value
43+
--> $DIR/drop_ref.rs:13:5
44+
|
45+
LL | drop(&mut owned1);
46+
| ^^^^^^^^^^^^^^^^^
47+
|
48+
note: argument has type `&mut SomeStruct`
49+
--> $DIR/drop_ref.rs:13:10
50+
|
51+
LL | drop(&mut owned1);
52+
| ^^^^^^^^^^^
53+
54+
warning: calls to `std::mem::drop` with a reference instead of an owned value
55+
--> $DIR/drop_ref.rs:17:5
56+
|
57+
LL | drop(reference1);
58+
| ^^^^^^^^^^^^^^^^
59+
|
60+
note: argument has type `&SomeStruct`
61+
--> $DIR/drop_ref.rs:17:10
62+
|
63+
LL | drop(reference1);
64+
| ^^^^^^^^^^
65+
66+
warning: calls to `std::mem::drop` with a reference instead of an owned value
67+
--> $DIR/drop_ref.rs:20:5
68+
|
69+
LL | drop(reference2);
70+
| ^^^^^^^^^^^^^^^^
71+
|
72+
note: argument has type `&mut SomeStruct`
73+
--> $DIR/drop_ref.rs:20:10
74+
|
75+
LL | drop(reference2);
76+
| ^^^^^^^^^^
77+
78+
warning: calls to `std::mem::drop` with a reference instead of an owned value
79+
--> $DIR/drop_ref.rs:23:5
80+
|
81+
LL | drop(reference3);
82+
| ^^^^^^^^^^^^^^^^
83+
|
84+
note: argument has type `&SomeStruct`
85+
--> $DIR/drop_ref.rs:23:10
86+
|
87+
LL | drop(reference3);
88+
| ^^^^^^^^^^
89+
90+
warning: calls to `std::mem::drop` with a reference instead of an owned value
91+
--> $DIR/drop_ref.rs:28:5
92+
|
93+
LL | drop(&val);
94+
| ^^^^^^^^^^
95+
|
96+
note: argument has type `&T`
97+
--> $DIR/drop_ref.rs:28:10
98+
|
99+
LL | drop(&val);
100+
| ^^^^
101+
102+
warning: calls to `std::mem::drop` with a reference instead of an owned value
103+
--> $DIR/drop_ref.rs:36:5
104+
|
105+
LL | std::mem::drop(&SomeStruct);
106+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
107+
|
108+
note: argument has type `&SomeStruct`
109+
--> $DIR/drop_ref.rs:36:20
110+
|
111+
LL | std::mem::drop(&SomeStruct);
112+
| ^^^^^^^^^^^
113+
114+
warning: calls to `std::mem::drop` with a reference instead of an owned value
115+
--> $DIR/drop_ref.rs:91:13
116+
|
117+
LL | drop(println_and(&13));
118+
| ^^^^^^^^^^^^^^^^^^^^^^
119+
|
120+
note: argument has type `&i32`
121+
--> $DIR/drop_ref.rs:91:18
122+
|
123+
LL | drop(println_and(&13));
124+
| ^^^^^^^^^^^^^^^^
125+
126+
warning: calls to `std::mem::drop` with a reference instead of an owned value
127+
--> $DIR/drop_ref.rs:94:14
128+
|
129+
LL | 3 if drop(println_and(&14)) == () => (),
130+
| ^^^^^^^^^^^^^^^^^^^^^^
131+
|
132+
note: argument has type `&i32`
133+
--> $DIR/drop_ref.rs:94:19
134+
|
135+
LL | 3 if drop(println_and(&14)) == () => (),
136+
| ^^^^^^^^^^^^^^^^
137+
138+
warning: calls to `std::mem::drop` with a reference instead of an owned value
139+
--> $DIR/drop_ref.rs:96:14
140+
|
141+
LL | 4 => drop(&2),
142+
| ^^^^^^^^
143+
|
144+
note: argument has type `&i32`
145+
--> $DIR/drop_ref.rs:96:19
146+
|
147+
LL | 4 => drop(&2),
148+
| ^^
149+
150+
warning: 12 warnings emitted
151+

0 commit comments

Comments
 (0)