@@ -425,7 +425,9 @@ fn scan_block_for_eq<'tcx>(
425425 modifies_any_local ( cx, stmt, & cond_locals)
426426 || !eq_stmts ( stmt, blocks, |b| b. stmts . get ( i) , & mut eq, & mut moved_locals)
427427 } )
428- . map_or ( block. stmts . len ( ) , |( i, _) | i) ;
428+ . map_or ( block. stmts . len ( ) , |( i, stmt) | {
429+ adjust_by_closest_callsite ( i, stmt, block. stmts [ ..i] . iter ( ) . enumerate ( ) . rev ( ) )
430+ } ) ;
429431
430432 if local_needs_ordered_drop {
431433 return BlockEq {
@@ -467,7 +469,9 @@ fn scan_block_for_eq<'tcx>(
467469 . is_none_or ( |s| hash != hash_stmt ( cx, s) )
468470 } )
469471 } )
470- . map_or ( block. stmts . len ( ) - start_end_eq, |( i, _) | i) ;
472+ . map_or ( block. stmts . len ( ) - start_end_eq, |( i, stmt) | {
473+ adjust_by_closest_callsite ( i, stmt, ( 0 ..i) . rev ( ) . zip ( block. stmts [ ( block. stmts . len ( ) - i) ..] . iter ( ) ) )
474+ } ) ;
471475
472476 let moved_locals_at_start = moved_locals. len ( ) ;
473477 let mut i = end_search_start;
@@ -522,6 +526,49 @@ fn scan_block_for_eq<'tcx>(
522526 }
523527}
524528
529+ /// Adjusts the index for which the statements begin to differ to the closest macro callsite. This
530+ /// avoids giving suggestions that requires splitting a macro call in half, when only a part of the
531+ /// macro expansion is equal.
532+ ///
533+ /// For example, for the following macro:
534+ /// ```rust,ignore
535+ /// macro_rules! foo {
536+ /// ($x:expr) => {
537+ /// let y = 42;
538+ /// $x;
539+ /// };
540+ /// }
541+ /// ```
542+ /// If the macro is called like this:
543+ /// ```rust,ignore
544+ /// if false {
545+ /// let z = 42;
546+ /// foo!(println!("Hello"));
547+ /// } else {
548+ /// let z = 42;
549+ /// foo!(println!("World"));
550+ /// }
551+ /// ```
552+ /// Although the expanded `let y = 42;` is equal, the macro call should not be included in the
553+ /// suggestion.
554+ fn adjust_by_closest_callsite < ' tcx > (
555+ i : usize ,
556+ stmt : & ' tcx Stmt < ' tcx > ,
557+ mut iter : impl Iterator < Item = ( usize , & ' tcx Stmt < ' tcx > ) > ,
558+ ) -> usize {
559+ let Some ( ( _, first) ) = iter. next ( ) else {
560+ return 0 ;
561+ } ;
562+
563+ // If it is already at the boundary of a macro call, then just return.
564+ if first. span . source_callsite ( ) != stmt. span . source_callsite ( ) {
565+ return i;
566+ }
567+
568+ iter. find ( |( _, stmt) | stmt. span . source_callsite ( ) != first. span . source_callsite ( ) )
569+ . map_or ( 0 , |( i, _) | i + 1 )
570+ }
571+
525572fn check_for_warn_of_moved_symbol ( cx : & LateContext < ' _ > , symbols : & [ ( HirId , Symbol ) ] , if_expr : & Expr < ' _ > ) -> bool {
526573 get_enclosing_block ( cx, if_expr. hir_id ) . is_some_and ( |block| {
527574 let ignore_span = block. span . shrink_to_lo ( ) . to ( if_expr. span ) ;
0 commit comments