11use clippy_utils:: diagnostics:: { span_lint_and_note, span_lint_and_then} ;
22use 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;
35use 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} ;
79use core:: iter;
10+ use core:: ops:: ControlFlow ;
811use rustc_errors:: Applicability ;
912use 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 } ;
1114use rustc_lint:: { LateContext , LateLintPass } ;
1215use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1316use rustc_span:: hygiene:: walk_chain;
@@ -214,7 +217,7 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&
214217fn 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.
345363fn 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
0 commit comments