Skip to content

Commit dfb9253

Browse files
committed
Auto merge of rust-lang#12870 - lrh2000:sig-drop-while, r=blyxyas
`significant_drop_in_scrutinee`: Trigger lint also for scrutinees in `while let` and `if let` This lint should also work for `if let` and `while let`, so this PR makes it actually work. For `while let`, I can't think of any reason why this lint shouldn't be enabled. The only problem is that the lint suggests moving the significant drop above the `while let`, which is clearly invalid in the case of `while let`. I don't know if this is fixable, but this PR simply disables the wrong suggestions. For `if let`, it seems that another lint called `if_let_mutex` has some overlapping functionality. But `significant_drop_in_scrutinee` is a bit stricter, as it will trigger even if the `else` branch does not try to lock the same mutex. changelog: [`significant_drop_in_scrutinee`]: Trigger lint also for scrutinees in `while let` and `if let`. r? `@blyxyas` (the third PR as promised in rust-lang/rust-clippy#12740 (comment), thanks for your review!)
2 parents edc6b00 + 378962f commit dfb9253

File tree

4 files changed

+149
-30
lines changed

4 files changed

+149
-30
lines changed

clippy_lints/src/matches/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,7 @@ impl_lint_pass!(Matches => [
10191019
]);
10201020

10211021
impl<'tcx> LateLintPass<'tcx> for Matches {
1022+
#[expect(clippy::too_many_lines)]
10221023
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
10231024
if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) {
10241025
return;
@@ -1037,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10371038
return;
10381039
}
10391040
if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
1040-
significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
1041+
significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
10411042
}
10421043

10431044
collapsible_match::check_match(cx, arms, &self.msrv);
@@ -1084,6 +1085,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10841085
}
10851086
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
10861087
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv);
1088+
significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
10871089
if !from_expansion {
10881090
if let Some(else_expr) = if_let.if_else {
10891091
if self.msrv.meets(msrvs::MATCHES_MACRO) {
@@ -1126,8 +1128,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
11261128
);
11271129
needless_match::check_if_let(cx, expr, &if_let);
11281130
}
1129-
} else if !from_expansion {
1130-
redundant_pattern_match::check(cx, expr);
1131+
} else {
1132+
if let Some(while_let) = higher::WhileLet::hir(expr) {
1133+
significant_drop_in_scrutinee::check_while_let(cx, expr, while_let.let_expr, while_let.if_then);
1134+
}
1135+
if !from_expansion {
1136+
redundant_pattern_match::check(cx, expr);
1137+
}
11311138
}
11321139
}
11331140

clippy_lints/src/matches/significant_drop_in_scrutinee.rs

Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_span::Span;
1616

1717
use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
1818

19-
pub(super) fn check<'tcx>(
19+
pub(super) fn check_match<'tcx>(
2020
cx: &LateContext<'tcx>,
2121
expr: &'tcx Expr<'tcx>,
2222
scrutinee: &'tcx Expr<'_>,
@@ -27,10 +27,89 @@ pub(super) fn check<'tcx>(
2727
return;
2828
}
2929

30-
let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source);
30+
let scrutinee = match (source, &scrutinee.kind) {
31+
(MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
32+
_ => scrutinee,
33+
};
34+
35+
let message = if source == MatchSource::Normal {
36+
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
37+
} else {
38+
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
39+
};
40+
41+
let arms = arms.iter().map(|arm| arm.body).collect::<Vec<_>>();
42+
43+
check(cx, expr, scrutinee, &arms, message, Suggestion::Emit);
44+
}
45+
46+
pub(super) fn check_if_let<'tcx>(
47+
cx: &LateContext<'tcx>,
48+
expr: &'tcx Expr<'tcx>,
49+
scrutinee: &'tcx Expr<'_>,
50+
if_then: &'tcx Expr<'_>,
51+
if_else: Option<&'tcx Expr<'_>>,
52+
) {
53+
if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
54+
return;
55+
}
56+
57+
let message =
58+
"temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression";
59+
60+
if let Some(if_else) = if_else {
61+
check(cx, expr, scrutinee, &[if_then, if_else], message, Suggestion::Emit);
62+
} else {
63+
check(cx, expr, scrutinee, &[if_then], message, Suggestion::Emit);
64+
}
65+
}
66+
67+
pub(super) fn check_while_let<'tcx>(
68+
cx: &LateContext<'tcx>,
69+
expr: &'tcx Expr<'tcx>,
70+
scrutinee: &'tcx Expr<'_>,
71+
body: &'tcx Expr<'_>,
72+
) {
73+
if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
74+
return;
75+
}
76+
77+
check(
78+
cx,
79+
expr,
80+
scrutinee,
81+
&[body],
82+
"temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression",
83+
// Don't emit wrong suggestions: We cannot fix the significant drop in the `while let` scrutinee by simply
84+
// moving it out. We need to change the `while` to a `loop` instead.
85+
Suggestion::DontEmit,
86+
);
87+
}
88+
89+
#[derive(Copy, Clone, Debug)]
90+
enum Suggestion {
91+
Emit,
92+
DontEmit,
93+
}
94+
95+
fn check<'tcx>(
96+
cx: &LateContext<'tcx>,
97+
expr: &'tcx Expr<'tcx>,
98+
scrutinee: &'tcx Expr<'_>,
99+
arms: &[&'tcx Expr<'_>],
100+
message: &'static str,
101+
sugg: Suggestion,
102+
) {
103+
let mut helper = SigDropHelper::new(cx);
104+
let suggestions = helper.find_sig_drop(scrutinee);
105+
31106
for found in suggestions {
32107
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
33-
set_diagnostic(diag, cx, expr, found);
108+
match sugg {
109+
Suggestion::Emit => set_suggestion(diag, cx, expr, found),
110+
Suggestion::DontEmit => (),
111+
}
112+
34113
let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
35114
diag.span_label(s, "temporary lives until here");
36115
for span in has_significant_drop_in_arms(cx, arms) {
@@ -41,7 +120,7 @@ pub(super) fn check<'tcx>(
41120
}
42121
}
43122

44-
fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
123+
fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
45124
let original = snippet(cx, found.found_span, "..");
46125
let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
47126

@@ -79,26 +158,6 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
79158
);
80159
}
81160

82-
/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
83-
/// may have a surprising lifetime.
84-
fn has_significant_drop_in_scrutinee<'tcx>(
85-
cx: &LateContext<'tcx>,
86-
scrutinee: &'tcx Expr<'tcx>,
87-
source: MatchSource,
88-
) -> (Vec<FoundSigDrop>, &'static str) {
89-
let mut helper = SigDropHelper::new(cx);
90-
let scrutinee = match (source, &scrutinee.kind) {
91-
(MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
92-
_ => scrutinee,
93-
};
94-
let message = if source == MatchSource::Normal {
95-
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
96-
} else {
97-
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
98-
};
99-
(helper.find_sig_drop(scrutinee), message)
100-
}
101-
102161
struct SigDropChecker<'a, 'tcx> {
103162
seen_types: FxHashSet<Ty<'tcx>>,
104163
cx: &'a LateContext<'tcx>,
@@ -428,10 +487,10 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
428487
}
429488
}
430489

431-
fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
490+
fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> {
432491
let mut helper = ArmSigDropHelper::new(cx);
433492
for arm in arms {
434-
helper.visit_expr(arm.body);
493+
helper.visit_expr(arm);
435494
}
436495
helper.found_sig_drop_spans
437496
}

tests/ui/significant_drop_in_scrutinee.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,4 +801,30 @@ fn should_not_trigger_lint_with_explicit_drop() {
801801
}
802802
}
803803

804+
fn should_trigger_lint_in_if_let() {
805+
let mutex = Mutex::new(vec![1]);
806+
807+
if let Some(val) = mutex.lock().unwrap().first().copied() {
808+
//~^ ERROR: temporary with significant `Drop` in `if let` scrutinee will live until the
809+
//~| NOTE: this might lead to deadlocks or other unexpected behavior
810+
println!("{}", val);
811+
}
812+
813+
// Should not trigger lint without the final `copied()`, because we actually hold a reference
814+
// (i.e., the `val`) to the locked data.
815+
if let Some(val) = mutex.lock().unwrap().first() {
816+
println!("{}", val);
817+
};
818+
}
819+
820+
fn should_trigger_lint_in_while_let() {
821+
let mutex = Mutex::new(vec![1]);
822+
823+
while let Some(val) = mutex.lock().unwrap().pop() {
824+
//~^ ERROR: temporary with significant `Drop` in `while let` scrutinee will live until the
825+
//~| NOTE: this might lead to deadlocks or other unexpected behavior
826+
println!("{}", val);
827+
}
828+
}
829+
804830
fn main() {}

tests/ui/significant_drop_in_scrutinee.stderr

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,5 +541,32 @@ LL ~ let value = mutex.lock().unwrap()[0];
541541
LL ~ for val in [value, 2] {
542542
|
543543

544-
error: aborting due to 27 previous errors
544+
error: temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression
545+
--> tests/ui/significant_drop_in_scrutinee.rs:807:24
546+
|
547+
LL | if let Some(val) = mutex.lock().unwrap().first().copied() {
548+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
549+
...
550+
LL | }
551+
| - temporary lives until here
552+
|
553+
= note: this might lead to deadlocks or other unexpected behavior
554+
help: try moving the temporary above the match
555+
|
556+
LL ~ let value = mutex.lock().unwrap().first().copied();
557+
LL ~ if let Some(val) = value {
558+
|
559+
560+
error: temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression
561+
--> tests/ui/significant_drop_in_scrutinee.rs:823:27
562+
|
563+
LL | while let Some(val) = mutex.lock().unwrap().pop() {
564+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
565+
...
566+
LL | }
567+
| - temporary lives until here
568+
|
569+
= note: this might lead to deadlocks or other unexpected behavior
570+
571+
error: aborting due to 29 previous errors
545572

0 commit comments

Comments
 (0)