Skip to content

Commit 55563f9

Browse files
committed
Fixes for branches_sharing_code
* Don't suggest moving modifications to locals used in any of the condition expressions * Don't suggest moving anything after a local with a significant drop
1 parent d251bd9 commit 55563f9

File tree

3 files changed

+113
-7
lines changed

3 files changed

+113
-7
lines changed

clippy_lints/src/copies.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
22
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
3+
use clippy_utils::ty::needs_ordered_drop;
4+
use clippy_utils::visitors::for_each_expr;
35
use clippy_utils::{
4-
eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed,
5-
search_same, ContainsName, HirEqInterExpr, SpanlessEq,
6+
capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
7+
is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
68
};
79
use core::iter;
10+
use core::ops::ControlFlow;
811
use rustc_errors::Applicability;
912
use rustc_hir::intravisit;
10-
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind};
13+
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
1114
use rustc_lint::{LateContext, LateLintPass};
1215
use rustc_session::{declare_lint_pass, declare_tool_lint};
1316
use rustc_span::hygiene::walk_chain;
@@ -214,7 +217,7 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&
214217
fn lint_branches_sharing_code<'tcx>(
215218
cx: &LateContext<'tcx>,
216219
conds: &[&'tcx Expr<'_>],
217-
blocks: &[&Block<'tcx>],
220+
blocks: &[&'tcx Block<'_>],
218221
expr: &'tcx Expr<'_>,
219222
) {
220223
// We only lint ifs with multiple blocks
@@ -340,6 +343,21 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
340343
}
341344
}
342345

346+
/// Checks if the statement modifies or moves any of the given locals.
347+
fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
348+
for_each_expr(s, |e| {
349+
if let Some(id) = path_to_local(e)
350+
&& locals.contains(&id)
351+
&& !capture_local_usage(cx, e).is_imm_ref()
352+
{
353+
ControlFlow::Break(())
354+
} else {
355+
ControlFlow::Continue(())
356+
}
357+
})
358+
.is_some()
359+
}
360+
343361
/// Checks if the given statement should be considered equal to the statement in the same position
344362
/// for each block.
345363
fn eq_stmts(
@@ -365,18 +383,52 @@ fn eq_stmts(
365383
.all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
366384
}
367385

368-
fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq {
386+
#[expect(clippy::too_many_lines)]
387+
fn scan_block_for_eq<'tcx>(
388+
cx: &LateContext<'tcx>,
389+
conds: &[&'tcx Expr<'_>],
390+
block: &'tcx Block<'_>,
391+
blocks: &[&'tcx Block<'_>],
392+
) -> BlockEq {
369393
let mut eq = SpanlessEq::new(cx);
370394
let mut eq = eq.inter_expr();
371395
let mut moved_locals = Vec::new();
372396

397+
let mut cond_locals = HirIdSet::default();
398+
for &cond in conds {
399+
let _: Option<!> = for_each_expr(cond, |e| {
400+
if let Some(id) = path_to_local(e) {
401+
cond_locals.insert(id);
402+
}
403+
ControlFlow::Continue(())
404+
});
405+
}
406+
407+
let mut local_needs_ordered_drop = false;
373408
let start_end_eq = block
374409
.stmts
375410
.iter()
376411
.enumerate()
377-
.find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals))
412+
.find(|&(i, stmt)| {
413+
if let StmtKind::Local(l) = stmt.kind
414+
&& needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
415+
{
416+
local_needs_ordered_drop = true;
417+
return true;
418+
}
419+
modifies_any_local(cx, stmt, &cond_locals)
420+
|| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
421+
})
378422
.map_or(block.stmts.len(), |(i, _)| i);
379423

424+
if local_needs_ordered_drop {
425+
return BlockEq {
426+
start_end_eq,
427+
end_begin_eq: None,
428+
moved_locals,
429+
};
430+
}
431+
380432
// Walk backwards through the final expression/statements so long as their hashes are equal. Note
381433
// `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
382434
// to match those in other blocks. e.g. If each block ends with the following the hash value will be

clippy_utils/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Captur
890890
Node::Expr(e) => match e.kind {
891891
ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
892892
ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
893-
ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
893+
ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
894894
return CaptureKind::Ref(Mutability::Mut);
895895
},
896896
ExprKind::Field(..) => {

tests/ui/branches_sharing_code/false_positives.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![allow(dead_code)]
22
#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
33

4+
use std::sync::Mutex;
5+
46
// ##################################
57
// # Issue clippy#7369
68
// ##################################
@@ -38,4 +40,56 @@ fn main() {
3840
let (y, x) = x;
3941
foo(x, y)
4042
};
43+
44+
let m = Mutex::new(0u32);
45+
let l = m.lock().unwrap();
46+
let _ = if true {
47+
drop(l);
48+
println!("foo");
49+
m.lock().unwrap();
50+
0
51+
} else if *l == 0 {
52+
drop(l);
53+
println!("foo");
54+
println!("bar");
55+
m.lock().unwrap();
56+
1
57+
} else {
58+
drop(l);
59+
println!("foo");
60+
println!("baz");
61+
m.lock().unwrap();
62+
2
63+
};
64+
65+
if true {
66+
let _guard = m.lock();
67+
println!("foo");
68+
} else {
69+
println!("foo");
70+
}
71+
72+
if true {
73+
let _guard = m.lock();
74+
println!("foo");
75+
println!("bar");
76+
} else {
77+
let _guard = m.lock();
78+
println!("foo");
79+
println!("baz");
80+
}
81+
82+
let mut c = 0;
83+
for _ in 0..5 {
84+
if c == 0 {
85+
c += 1;
86+
println!("0");
87+
} else if c == 1 {
88+
c += 1;
89+
println!("1");
90+
} else {
91+
c += 1;
92+
println!("more");
93+
}
94+
}
4195
}

0 commit comments

Comments
 (0)