Skip to content

Commit

Permalink
Lint significant drop on while let and if let
Browse files Browse the repository at this point in the history
  • Loading branch information
lrh2000 committed Apr 30, 2024
1 parent db6d63b commit c00cd51
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 28 deletions.
12 changes: 9 additions & 3 deletions clippy_lints/src/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
return;
}
if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
}

collapsible_match::check_match(cx, arms);
Expand Down Expand Up @@ -1084,6 +1084,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
}
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
if !from_expansion {
if let Some(else_expr) = if_let.if_else {
if self.msrv.meets(msrvs::MATCHES_MACRO) {
Expand Down Expand Up @@ -1118,8 +1119,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
);
needless_match::check_if_let(cx, expr, &if_let);
}
} else if !from_expansion {
redundant_pattern_match::check(cx, expr);
} else {
if let Some(while_let) = higher::WhileLet::hir(expr) {
significant_drop_in_scrutinee::check_while_let(cx, expr, while_let.let_expr, while_let.if_then);
}
if !from_expansion {
redundant_pattern_match::check(cx, expr);
}
}
}

Expand Down
93 changes: 69 additions & 24 deletions clippy_lints/src/matches/significant_drop_in_scrutinee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_span::Span;

use super::SIGNIFICANT_DROP_IN_SCRUTINEE;

pub(super) fn check<'tcx>(
pub(super) fn check_match<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
scrutinee: &'tcx Expr<'_>,
Expand All @@ -27,7 +27,72 @@ pub(super) fn check<'tcx>(
return;
}

let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source);
let scrutinee = match (source, &scrutinee.kind) {
(MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
_ => scrutinee,
};

let message = if source == MatchSource::Normal {
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
} else {
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
};

let arms = arms.iter().map(|arm| arm.body).collect::<Vec<_>>();

check(cx, expr, scrutinee, &arms, message);
}

pub(super) fn check_if_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
scrutinee: &'tcx Expr<'_>,
if_then: &'tcx Expr<'_>,
if_else: Option<&'tcx Expr<'_>>,
) {
if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
return;
}

let message =
"temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression";

if let Some(if_else) = if_else {
check(cx, expr, scrutinee, &[if_then, if_else], message);
} else {
check(cx, expr, scrutinee, &[if_then], message);
}
}

pub(super) fn check_while_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
scrutinee: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
) {
if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
return;
}

check(
cx,
expr,
scrutinee,
&[body],
"temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression",
);
}

fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
scrutinee: &'tcx Expr<'_>,
arms: &[&'tcx Expr<'_>],
message: &'static str,
) {
let mut helper = SigDropHelper::new(cx);
let suggestions = helper.find_sig_drop(scrutinee);

for found in suggestions {
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
set_diagnostic(diag, cx, expr, found);
Expand Down Expand Up @@ -81,26 +146,6 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
);
}

/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
/// may have a surprising lifetime.
fn has_significant_drop_in_scrutinee<'tcx>(
cx: &LateContext<'tcx>,
scrutinee: &'tcx Expr<'tcx>,
source: MatchSource,
) -> (Vec<FoundSigDrop>, &'static str) {
let mut helper = SigDropHelper::new(cx);
let scrutinee = match (source, &scrutinee.kind) {
(MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
_ => scrutinee,
};
let message = if source == MatchSource::Normal {
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
} else {
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
};
(helper.find_sig_drop(scrutinee), message)
}

struct SigDropChecker<'a, 'tcx> {
seen_types: FxHashSet<Ty<'tcx>>,
cx: &'a LateContext<'tcx>,
Expand Down Expand Up @@ -430,10 +475,10 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
}
}

fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> {
let mut helper = ArmSigDropHelper::new(cx);
for arm in arms {
helper.visit_expr(arm.body);
helper.visit_expr(arm);
}
helper.found_sig_drop_spans
}
Expand Down
26 changes: 26 additions & 0 deletions tests/ui/significant_drop_in_scrutinee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,4 +801,30 @@ fn should_not_trigger_lint_with_explicit_drop() {
}
}

fn should_trigger_lint_in_if_let() {
let mutex = Mutex::new(vec![1]);

if let Some(val) = mutex.lock().unwrap().first().copied() {
//~^ ERROR: temporary with significant `Drop` in `if let` scrutinee will live until the
//~| NOTE: this might lead to deadlocks or other unexpected behavior
println!("{}", val);
}

// Should not trigger lint without the final `copied()`, because we actually hold a reference
// (i.e., the `val`) to the locked data.
if let Some(val) = mutex.lock().unwrap().first() {
println!("{}", val);
};
}

fn should_trigger_lint_in_while_let() {
let mutex = Mutex::new(vec![1]);

while let Some(val) = mutex.lock().unwrap().pop() {
//~^ ERROR: temporary with significant `Drop` in `while let` scrutinee will live until the
//~| NOTE: this might lead to deadlocks or other unexpected behavior
println!("{}", val);
}
}

fn main() {}
34 changes: 33 additions & 1 deletion tests/ui/significant_drop_in_scrutinee.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -541,5 +541,37 @@ LL ~ let value = mutex.lock().unwrap()[0];
LL ~ for val in [value, 2] {
|

error: aborting due to 27 previous errors
error: temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression
--> tests/ui/significant_drop_in_scrutinee.rs:807:24
|
LL | if let Some(val) = mutex.lock().unwrap().first().copied() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | }
| - temporary lives until here
|
= note: this might lead to deadlocks or other unexpected behavior
help: try moving the temporary above the match and create a copy
|
LL ~ let value = mutex.lock().unwrap().first().copied();
LL ~ if let Some(val) = value {
|

error: temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression
--> tests/ui/significant_drop_in_scrutinee.rs:823:27
|
LL | while let Some(val) = mutex.lock().unwrap().pop() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | }
| - temporary lives until here
|
= note: this might lead to deadlocks or other unexpected behavior
help: try moving the temporary above the match and create a copy
|
LL ~ let value = mutex.lock().unwrap().pop();
LL ~ while let Some(val) = value {
|

error: aborting due to 29 previous errors

0 comments on commit c00cd51

Please sign in to comment.