1
1
use clippy_utils:: diagnostics:: { span_lint_and_note, span_lint_and_then} ;
2
2
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;
3
5
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 ,
6
8
} ;
7
9
use core:: iter;
10
+ use core:: ops:: ControlFlow ;
8
11
use rustc_errors:: Applicability ;
9
12
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 } ;
11
14
use rustc_lint:: { LateContext , LateLintPass } ;
12
15
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
16
use rustc_span:: hygiene:: walk_chain;
@@ -214,7 +217,7 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&
214
217
fn lint_branches_sharing_code < ' tcx > (
215
218
cx : & LateContext < ' tcx > ,
216
219
conds : & [ & ' tcx Expr < ' _ > ] ,
217
- blocks : & [ & Block < ' tcx > ] ,
220
+ blocks : & [ & ' tcx Block < ' _ > ] ,
218
221
expr : & ' tcx Expr < ' _ > ,
219
222
) {
220
223
// We only lint ifs with multiple blocks
@@ -340,6 +343,21 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
340
343
}
341
344
}
342
345
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
+
343
361
/// Checks if the given statement should be considered equal to the statement in the same position
344
362
/// for each block.
345
363
fn eq_stmts (
@@ -365,18 +383,52 @@ fn eq_stmts(
365
383
. all ( |b| get_stmt ( b) . map_or ( false , |s| eq. eq_stmt ( s, stmt) ) )
366
384
}
367
385
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 {
369
393
let mut eq = SpanlessEq :: new ( cx) ;
370
394
let mut eq = eq. inter_expr ( ) ;
371
395
let mut moved_locals = Vec :: new ( ) ;
372
396
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 ;
373
408
let start_end_eq = block
374
409
. stmts
375
410
. iter ( )
376
411
. 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
+ } )
378
422
. map_or ( block. stmts . len ( ) , |( i, _) | i) ;
379
423
424
+ if local_needs_ordered_drop {
425
+ return BlockEq {
426
+ start_end_eq,
427
+ end_begin_eq : None ,
428
+ moved_locals,
429
+ } ;
430
+ }
431
+
380
432
// Walk backwards through the final expression/statements so long as their hashes are equal. Note
381
433
// `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
382
434
// to match those in other blocks. e.g. If each block ends with the following the hash value will be
0 commit comments