Skip to content

Commit aa88925

Browse files
committed
Improved shared_code_in_if_blocks output readability and added tests
1 parent 72bb0e9 commit aa88925

File tree

4 files changed

+163
-41
lines changed

4 files changed

+163
-41
lines changed

clippy_lints/src/copies.rs

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
22
use crate::utils::{
3-
first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline,
4-
snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
3+
first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet,
4+
snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
55
};
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_errors::Applicability;
88
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
9-
use rustc_hir::{Block, Expr, HirId};
9+
use rustc_hir::{Block, Expr, ExprKind, HirId};
1010
use rustc_lint::{LateContext, LateLintPass};
1111
use rustc_middle::hir::map::Map;
1212
use rustc_session::{declare_lint_pass, declare_tool_lint};
13-
use rustc_span::source_map::Span;
13+
use rustc_span::{source_map::Span, BytePos};
1414
use std::borrow::Cow;
1515

1616
declare_clippy_lint! {
@@ -218,14 +218,6 @@ fn lint_same_then_else<'tcx>(
218218
);
219219

220220
return;
221-
} else {
222-
println!(
223-
"{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}",
224-
win[0].span,
225-
block_expr_eq,
226-
l_stmts.len(),
227-
r_stmts.len()
228-
)
229221
}
230222

231223
start_eq = start_eq.min(current_start_eq);
@@ -328,7 +320,7 @@ fn emit_shared_code_in_if_blocks_lint(
328320
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
329321

330322
let span = span_start.to(span_end);
331-
suggestions.push(("START HELP", span, suggestion.to_string()));
323+
suggestions.push(("start", span, suggestion.to_string()));
332324
}
333325

334326
if lint_end {
@@ -354,31 +346,56 @@ fn emit_shared_code_in_if_blocks_lint(
354346
let suggestion = "}\n".to_string() + &moved_snipped;
355347
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
356348

357-
let span = moved_start.to(span_end);
358-
suggestions.push(("END_RANGE", span, suggestion.to_string()));
349+
let mut span = moved_start.to(span_end);
350+
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
351+
let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt());
352+
if snippet_opt(cx, test_span)
353+
.map(|snip| snip == " ")
354+
.unwrap_or_default()
355+
{
356+
span = span.with_lo(test_span.lo());
357+
}
358+
359+
suggestions.push(("end", span, suggestion.to_string()));
359360
}
360361

361362
if suggestions.len() == 1 {
362-
let (_, span, sugg) = &suggestions[0];
363+
let (place_str, span, sugg) = suggestions.pop().unwrap();
364+
let msg = format!("All if blocks contain the same code at the {}", place_str);
365+
let help = format!("Consider moving the {} statements out like this", place_str);
363366
span_lint_and_sugg(
364367
cx,
365368
SHARED_CODE_IN_IF_BLOCKS,
366-
*span,
367-
"All code blocks contain the same code",
368-
"Consider moving the statements out like this",
369-
sugg.clone(),
369+
span,
370+
msg.as_str(),
371+
help.as_str(),
372+
sugg,
370373
Applicability::Unspecified,
371374
);
372-
} else {
375+
} else if suggestions.len() == 2 {
376+
let (_, end_span, end_sugg) = suggestions.pop().unwrap();
377+
let (_, start_span, start_sugg) = suggestions.pop().unwrap();
373378
span_lint_and_then(
374379
cx,
375380
SHARED_CODE_IN_IF_BLOCKS,
376-
if_expr.span,
377-
"All if blocks contain the same code",
381+
start_span,
382+
"All if blocks contain the same code at the start and the end. Here at the start:",
378383
move |diag| {
379-
for (help, span, sugg) in suggestions {
380-
diag.span_suggestion(span, help, sugg, Applicability::Unspecified);
381-
}
384+
diag.span_note(end_span, "And here at the end:");
385+
386+
diag.span_suggestion(
387+
start_span,
388+
"Consider moving the start statements out like this:",
389+
start_sugg,
390+
Applicability::Unspecified,
391+
);
392+
393+
diag.span_suggestion(
394+
end_span,
395+
"And consider moving the end statements out like this:",
396+
end_sugg,
397+
Applicability::Unspecified,
398+
);
382399
},
383400
);
384401
}

clippy_lints/src/utils/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,8 @@ pub fn if_sequence<'tcx>(
14301430
(conds, blocks)
14311431
}
14321432

1433+
/// This function returns true if the given expression is the `else` or `if else` part of an if
1434+
/// statement
14331435
pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
14341436
let map = cx.tcx.hir();
14351437
let parent_id = map.get_parent_node(expr.hir_id);

tests/ui/shared_code_in_if_blocks/shared_at_bot.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,40 @@ fn simple_examples() {
3333
result
3434
};
3535

36+
// Else if block
3637
if x == 9 {
3738
println!("The index is: 6");
3839

3940
println!("Same end of block");
4041
} else if x == 8 {
4142
println!("The index is: 4");
4243

44+
// We should only get a lint trigger for the last statement
45+
println!("This is also eq with the else block");
4346
println!("Same end of block");
4447
} else {
4548
println!("Same end of block");
49+
println!("This is also eq with the else block");
50+
}
51+
52+
// Use of outer scope value
53+
let outer_scope_value = "I'm outside the if block";
54+
if x < 99 {
55+
let z = "How are you";
56+
println!("I'm a local because I use the value `z`: `{}`", z);
57+
58+
println!(
59+
"I'm moveable because I know: `outer_scope_value`: '{}'",
60+
outer_scope_value
61+
);
62+
} else {
63+
let z = 45678000;
64+
println!("I'm a local because I use the value `z`: `{}`", z);
65+
66+
println!(
67+
"I'm moveable because I know: `outer_scope_value`: '{}'",
68+
outer_scope_value
69+
);
4670
}
4771

4872
// TODO xFrednet 2021-01.13: Fix lint for `if let`
@@ -147,4 +171,15 @@ fn not_moveable_due_to_value_scope() {
147171
};
148172
}
149173

174+
#[rustfmt::skip]
175+
fn test_suggestion_with_weird_formatting() {
176+
let x = 9;
177+
let mut a = 0;
178+
let mut b = 0;
179+
180+
// The error message still looks weird tbh but this is the best I can do
181+
// for weird formatting
182+
if x == 17 { b = 1; a = 0x99; } else { a = 0x99; }
183+
}
184+
150185
fn main() {}

tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,88 @@
33

44
// shared_code_in_if_blocks at the top and bottom of the if blocks
55

6-
fn main() {
7-
// TODO xFrednet 2021-01-12: This
6+
struct DataPack {
7+
id: u32,
8+
name: String,
9+
some_data: Vec<u8>,
810
}
911

10-
// General TODOs By xFrednet:
11-
12-
//
13-
// * Make a test with overlapping eq regions (else ifs)
14-
// * Test if as function parameter, tuple constructor, index, while loop condition
15-
// * Test where only the expression is the same
16-
// * Test where the block only has an expression
17-
// * Test with let on a previous line let _ = \n if...
18-
// * Tests with unreadable formatting (Inline if, Not indented)
19-
// * Test multiline condition if x == 9 \n x == 8 {}
20-
// * Test if for return/break (Only move to front)
21-
// * Test in inline closures
22-
// * Test with structs and tuples
12+
fn overlapping_eq_regions() {
13+
let x = 9;
14+
15+
// Overlap with separator
16+
if x == 7 {
17+
let t = 7;
18+
let _overlap_start = t * 2;
19+
let _overlap_end = 2 * t;
20+
let _u = 9;
21+
} else {
22+
let t = 7;
23+
let _overlap_start = t * 2;
24+
let _overlap_end = 2 * t;
25+
println!("Overlap separator");
26+
let _overlap_start = t * 2;
27+
let _overlap_end = 2 * t;
28+
let _u = 9;
29+
}
30+
31+
// Overlap with separator
32+
if x == 99 {
33+
let r = 7;
34+
let _overlap_start = r;
35+
let _overlap_middle = r * r;
36+
let _overlap_end = r * r * r;
37+
let z = "end";
38+
} else {
39+
let r = 7;
40+
let _overlap_start = r;
41+
let _overlap_middle = r * r;
42+
let _overlap_middle = r * r;
43+
let _overlap_end = r * r * r;
44+
let z = "end";
45+
}
46+
}
47+
48+
fn complexer_example() {
49+
fn gen_id(x: u32, y: u32) -> u32 {
50+
let x = x & 0x0000_ffff;
51+
let y = (y & 0xffff_0000) << 16;
52+
x | y
53+
}
54+
55+
fn process_data(data: DataPack) {
56+
let _ = data;
57+
}
58+
59+
let x = 8;
60+
let y = 9;
61+
if (x > 7 && y < 13) || (x + y) % 2 == 1 {
62+
let a = 0xcafe;
63+
let b = 0xffff00ff;
64+
let e_id = gen_id(a, b);
65+
66+
println!("From the a `{}` to the b `{}`", a, b);
67+
68+
let pack = DataPack {
69+
id: e_id,
70+
name: "Player 1".to_string(),
71+
some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
72+
};
73+
process_data(pack);
74+
} else {
75+
let a = 0xcafe;
76+
let b = 0xffff00ff;
77+
let e_id = gen_id(a, b);
78+
79+
println!("The new ID is '{}'", e_id);
80+
81+
let pack = DataPack {
82+
id: e_id,
83+
name: "Player 1".to_string(),
84+
some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
85+
};
86+
process_data(pack);
87+
}
88+
}
89+
90+
fn main() {}

0 commit comments

Comments
 (0)