Skip to content

Commit d6991ab

Browse files
committed
Auto merge of #12764 - lrh2000:ignore-place, r=blyxyas
`significant_drop_in_scrutinee`: Fix false positives due to false drops of place expressions Place expressions do not really create temporaries, so they will not create significant drops. For example, the following code snippet is quite good (#8963): ```rust fn main() { let x = std::sync::Mutex::new(vec![1, 2, 3]); let x_guard = x.lock().unwrap(); match x_guard[0] { 1 => println!("1!"), x => println!("{x}"), } drop(x_guard); // Some "usage" } ``` Also, the previous logic thinks that references like `&MutexGuard<_>`/`Ref<'_, MutexGuard<'_, _>>` have significant drops, which is simply not true, so it is fixed together in this PR. Fixes #8963 Fixes #9072 changelog: [`significant_drop_in_scrutinee`]: Fix false positives due to false drops of place expressions. r? `@blyxyas`
2 parents a4a1a73 + 509ca90 commit d6991ab

File tree

3 files changed

+126
-40
lines changed

3 files changed

+126
-40
lines changed

clippy_lints/src/matches/significant_drop_in_scrutinee.rs

+45-37
Original file line numberDiff line numberDiff line change
@@ -115,45 +115,60 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
115115
}
116116
}
117117

118-
fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
119-
self.cx.typeck_results().expr_ty(ex)
118+
fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool {
119+
!ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
120120
}
121121

122-
fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
123-
!self.seen_types.insert(ty)
122+
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
123+
self.seen_types.clear();
124+
self.has_sig_drop_attr_impl(ty)
124125
}
125126

126-
fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
127+
fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool {
127128
if let Some(adt) = ty.ty_adt_def() {
128-
if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
129+
if get_attr(
130+
self.cx.sess(),
131+
self.cx.tcx.get_attrs_unchecked(adt.did()),
132+
"has_significant_drop",
133+
)
134+
.count()
135+
> 0
136+
{
129137
return true;
130138
}
131139
}
132140

133-
match ty.kind() {
134-
rustc_middle::ty::Adt(a, b) => {
135-
for f in a.all_fields() {
136-
let ty = f.ty(cx.tcx, b);
137-
if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
138-
return true;
139-
}
140-
}
141+
if !self.seen_types.insert(ty) {
142+
return false;
143+
}
141144

142-
for generic_arg in *b {
143-
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
144-
if self.has_sig_drop_attr(cx, ty) {
145-
return true;
146-
}
147-
}
148-
}
149-
false
145+
let result = match ty.kind() {
146+
rustc_middle::ty::Adt(adt, args) => {
147+
// if some field has significant drop,
148+
adt.all_fields()
149+
.map(|field| field.ty(self.cx.tcx, args))
150+
.any(|ty| self.has_sig_drop_attr_impl(ty))
151+
// or if there is no generic lifetime and..
152+
// (to avoid false positive on `Ref<'a, MutexGuard<Foo>>`)
153+
|| (args
154+
.iter()
155+
.all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
156+
// some generic parameter has significant drop
157+
// (to avoid false negative on `Box<MutexGuard<Foo>>`)
158+
&& args
159+
.iter()
160+
.filter_map(|arg| match arg.unpack() {
161+
GenericArgKind::Type(ty) => Some(ty),
162+
_ => None,
163+
})
164+
.any(|ty| self.has_sig_drop_attr_impl(ty)))
150165
},
151-
rustc_middle::ty::Array(ty, _)
152-
| rustc_middle::ty::RawPtr(ty, _)
153-
| rustc_middle::ty::Ref(_, ty, _)
154-
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
166+
rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)),
167+
rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty),
155168
_ => false,
156-
}
169+
};
170+
171+
result
157172
}
158173
}
159174

@@ -232,7 +247,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
232247
if self.current_sig_drop.is_some() {
233248
return;
234249
}
235-
let ty = self.sig_drop_checker.get_type(expr);
250+
let ty = self.cx.typeck_results().expr_ty(expr);
236251
if ty.is_ref() {
237252
// We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
238253
// but let's avoid any chance of an ICE
@@ -279,11 +294,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
279294

280295
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
281296
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
282-
if !self.is_chain_end
283-
&& self
284-
.sig_drop_checker
285-
.has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
286-
{
297+
if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
287298
self.has_significant_drop = true;
288299
return;
289300
}
@@ -387,10 +398,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'
387398

388399
impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
389400
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
390-
if self
391-
.sig_drop_checker
392-
.has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
393-
{
401+
if self.sig_drop_checker.is_sig_drop_expr(ex) {
394402
self.found_sig_drop_spans.insert(ex.span);
395403
return;
396404
}

tests/ui/significant_drop_in_scrutinee.rs

+56
Original file line numberDiff line numberDiff line change
@@ -675,4 +675,60 @@ fn should_not_trigger_on_significant_iterator_drop() {
675675
}
676676
}
677677

678+
// https://github.com/rust-lang/rust-clippy/issues/9072
679+
fn should_not_trigger_lint_if_place_expr_has_significant_drop() {
680+
let x = Mutex::new(vec![1, 2, 3]);
681+
let x_guard = x.lock().unwrap();
682+
683+
match x_guard[0] {
684+
1 => println!("1!"),
685+
x => println!("{x}"),
686+
}
687+
688+
match x_guard.len() {
689+
1 => println!("1!"),
690+
x => println!("{x}"),
691+
}
692+
}
693+
694+
struct Guard<'a, T>(MutexGuard<'a, T>);
695+
696+
struct Ref<'a, T>(&'a T);
697+
698+
impl<'a, T> Guard<'a, T> {
699+
fn guard(&self) -> &MutexGuard<T> {
700+
&self.0
701+
}
702+
703+
fn guard_ref(&self) -> Ref<MutexGuard<T>> {
704+
Ref(&self.0)
705+
}
706+
707+
fn take(self) -> Box<MutexGuard<'a, T>> {
708+
Box::new(self.0)
709+
}
710+
}
711+
712+
fn should_not_trigger_for_significant_drop_ref() {
713+
let mutex = Mutex::new(vec![1, 2]);
714+
let guard = Guard(mutex.lock().unwrap());
715+
716+
match guard.guard().len() {
717+
0 => println!("empty"),
718+
_ => println!("not empty"),
719+
}
720+
721+
match guard.guard_ref().0.len() {
722+
0 => println!("empty"),
723+
_ => println!("not empty"),
724+
}
725+
726+
match guard.take().len() {
727+
//~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the
728+
//~| NOTE: this might lead to deadlocks or other unexpected behavior
729+
0 => println!("empty"),
730+
_ => println!("not empty"),
731+
};
732+
}
733+
678734
fn main() {}

tests/ui/significant_drop_in_scrutinee.stderr

+25-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ LL | match s.lock_m().get_the_value() {
2828
LL | println!("{}", s.lock_m().get_the_value());
2929
| ---------- another value with significant `Drop` created here
3030
...
31+
LL | println!("{}", s.lock_m().get_the_value());
32+
| ---------- another value with significant `Drop` created here
33+
...
3134
LL | }
3235
| - temporary lives until here
3336
|
@@ -47,6 +50,9 @@ LL | match s.lock_m_m().get_the_value() {
4750
LL | println!("{}", s.lock_m().get_the_value());
4851
| ---------- another value with significant `Drop` created here
4952
...
53+
LL | println!("{}", s.lock_m().get_the_value());
54+
| ---------- another value with significant `Drop` created here
55+
...
5056
LL | }
5157
| - temporary lives until here
5258
|
@@ -360,7 +366,7 @@ LL | match s.lock().deref().deref() {
360366
| ^^^^^^^^^^^^^^^^^^^^^^^^
361367
...
362368
LL | _ => println!("Value is {}", s.lock().deref()),
363-
| ---------------- another value with significant `Drop` created here
369+
| -------- another value with significant `Drop` created here
364370
LL | };
365371
| - temporary lives until here
366372
|
@@ -378,7 +384,7 @@ LL | match s.lock().deref().deref() {
378384
| ^^^^^^^^^^^^^^^^^^^^^^^^
379385
...
380386
LL | matcher => println!("Value is {}", s.lock().deref()),
381-
| ---------------- another value with significant `Drop` created here
387+
| -------- another value with significant `Drop` created here
382388
LL | _ => println!("Value was not a match"),
383389
LL | };
384390
| - temporary lives until here
@@ -499,5 +505,21 @@ LL ~ let value = mutex.lock().unwrap().foo();
499505
LL ~ match value {
500506
|
501507

502-
error: aborting due to 26 previous errors
508+
error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
509+
--> tests/ui/significant_drop_in_scrutinee.rs:726:11
510+
|
511+
LL | match guard.take().len() {
512+
| ^^^^^^^^^^^^^^^^^^
513+
...
514+
LL | };
515+
| - temporary lives until here
516+
|
517+
= note: this might lead to deadlocks or other unexpected behavior
518+
help: try moving the temporary above the match
519+
|
520+
LL ~ let value = guard.take().len();
521+
LL ~ match value {
522+
|
523+
524+
error: aborting due to 27 previous errors
503525

0 commit comments

Comments
 (0)