Skip to content

Commit 33052be

Browse files
authored
Fix branches_sharing_code suggests wrongly when dealing with macros (#14907)
Closes #14873 changelog: [`branches_sharing_code`] fix wrong suggestions when dealing with macros
2 parents f2922e7 + 1c070ca commit 33052be

File tree

7 files changed

+215
-5
lines changed

7 files changed

+215
-5
lines changed

clippy_lints/src/copies.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
525572
fn 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);

tests/ui/branches_sharing_code/shared_at_bottom.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,40 @@ fn fp_if_let_issue7054() {
239239
}
240240

241241
fn main() {}
242+
243+
mod issue14873 {
244+
fn foo() -> i32 {
245+
todo!()
246+
}
247+
248+
macro_rules! qux {
249+
($a:ident, $b:ident, $condition:expr) => {
250+
if $condition {
251+
"."
252+
} else {
253+
""
254+
};
255+
$a = foo();
256+
$b = foo();
257+
};
258+
}
259+
260+
fn share_on_bottom() {
261+
let mut a = 0;
262+
let mut b = 0;
263+
if false {
264+
qux!(a, b, a == b);
265+
} else {
266+
qux!(a, b, a != b);
267+
};
268+
269+
if false {
270+
qux!(a, b, a == b);
271+
let y = 1;
272+
} else {
273+
qux!(a, b, a != b);
274+
let y = 1;
275+
//~^ branches_sharing_code
276+
}
277+
}
278+
}

tests/ui/branches_sharing_code/shared_at_bottom.stderr

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,20 @@ LL ~ if x == 17 { b = 1; a = 0x99; } else { }
157157
LL + a = 0x99;
158158
|
159159

160-
error: aborting due to 9 previous errors
160+
error: all if blocks contain the same code at the end
161+
--> tests/ui/branches_sharing_code/shared_at_bottom.rs:274:9
162+
|
163+
LL | / let y = 1;
164+
LL | |
165+
LL | | }
166+
| |_________^
167+
|
168+
= warning: some moved values might need to be renamed to avoid wrong references
169+
help: consider moving these statements after the if
170+
|
171+
LL ~ }
172+
LL + let y = 1;
173+
|
174+
175+
error: aborting due to 10 previous errors
161176

tests/ui/branches_sharing_code/shared_at_top.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,34 @@ fn pf_local_with_inferred_type_issue7053() {
124124
}
125125

126126
fn main() {}
127+
128+
mod issue14873 {
129+
fn foo() -> i32 {
130+
todo!()
131+
}
132+
133+
macro_rules! qux {
134+
($a:ident, $b:ident, $condition:expr) => {
135+
let $a: i32 = foo();
136+
let $b: i32 = foo();
137+
if $condition { "." } else { "" }
138+
};
139+
}
140+
141+
fn share_on_top() {
142+
if false {
143+
qux!(a, b, a == b);
144+
} else {
145+
qux!(a, b, a != b);
146+
};
147+
148+
if false {
149+
//~^ branches_sharing_code
150+
let x = 1;
151+
qux!(a, b, a == b);
152+
} else {
153+
let x = 1;
154+
qux!(a, b, a != b);
155+
}
156+
}
157+
}

tests/ui/branches_sharing_code/shared_at_top.stderr

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,20 @@ note: the lint level is defined here
125125
LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)]
126126
| ^^^^^^^^^^^^^^^^^^^^^^^^^
127127

128-
error: aborting due to 7 previous errors
128+
error: all if blocks contain the same code at the start
129+
--> tests/ui/branches_sharing_code/shared_at_top.rs:148:9
130+
|
131+
LL | / if false {
132+
LL | |
133+
LL | | let x = 1;
134+
| |______________________^
135+
|
136+
= warning: some moved values might need to be renamed to avoid wrong references
137+
help: consider moving these statements before the if
138+
|
139+
LL ~ let x = 1;
140+
LL + if false {
141+
|
142+
143+
error: aborting due to 8 previous errors
129144

tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,42 @@ fn added_note_for_expression_use() -> u32 {
128128
}
129129

130130
fn main() {}
131+
132+
mod issue14873 {
133+
fn foo() -> i32 {
134+
todo!()
135+
}
136+
137+
macro_rules! qux {
138+
($a:ident, $b:ident, $condition:expr) => {
139+
let mut $a: i32 = foo();
140+
let mut $b: i32 = foo();
141+
if $condition {
142+
"."
143+
} else {
144+
""
145+
};
146+
$a = foo();
147+
$b = foo();
148+
};
149+
}
150+
151+
fn share_on_top_and_bottom() {
152+
if false {
153+
qux!(a, b, a == b);
154+
} else {
155+
qux!(a, b, a != b);
156+
};
157+
158+
if false {
159+
//~^ branches_sharing_code
160+
let x = 1;
161+
qux!(a, b, a == b);
162+
let y = 1;
163+
} else {
164+
let x = 1;
165+
qux!(a, b, a != b);
166+
let y = 1;
167+
}
168+
}
169+
}

tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,31 @@ LL ~ }
159159
LL + x * 4
160160
|
161161

162-
error: aborting due to 5 previous errors
162+
error: all if blocks contain the same code at both the start and the end
163+
--> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9
164+
|
165+
LL | / if false {
166+
LL | |
167+
LL | | let x = 1;
168+
| |______________________^
169+
|
170+
note: this code is shared at the end
171+
--> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9
172+
|
173+
LL | / let y = 1;
174+
LL | | }
175+
| |_________^
176+
= warning: some moved values might need to be renamed to avoid wrong references
177+
help: consider moving these statements before the if
178+
|
179+
LL ~ let x = 1;
180+
LL + if false {
181+
|
182+
help: consider moving these statements after the if
183+
|
184+
LL ~ }
185+
LL + let y = 1;
186+
|
187+
188+
error: aborting due to 6 previous errors
163189

0 commit comments

Comments
 (0)