Skip to content

Commit cb0f269

Browse files
committed
The shared_code_in_if_blocks lint only operats on entire if expr not else ifs
1 parent 58db3fd commit cb0f269

File tree

3 files changed

+148
-21
lines changed

3 files changed

+148
-21
lines changed

clippy_lints/src/copies.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
use clippy_utils::diagnostics::span_lint_and_note;
2-
use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
3-
use clippy_utils::{get_parent_expr, if_sequence};
4-
use rustc_hir::{Block, Expr, ExprKind};
1+
use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
2+
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,
5+
};
6+
use rustc_data_structures::fx::FxHashSet;
7+
use rustc_errors::Applicability;
8+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
9+
use rustc_hir::{Block, Expr, HirId};
510
use rustc_lint::{LateContext, LateLintPass};
611
use rustc_middle::hir::map::Map;
712
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -136,7 +141,7 @@ declare_clippy_lint! {
136141
/// };
137142
/// ```
138143
pub SHARED_CODE_IN_IF_BLOCKS,
139-
pedantic,
144+
nursery,
140145
"`if` statement with shared code in all blocks"
141146
}
142147

@@ -180,7 +185,7 @@ fn lint_same_then_else<'tcx>(
180185
) {
181186
// We only lint ifs with multiple blocks
182187
// TODO xFrednet 2021-01-01: Check if it's an else if block
183-
if blocks.len() < 2 {
188+
if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
184189
return;
185190
}
186191

@@ -190,7 +195,7 @@ fn lint_same_then_else<'tcx>(
190195
let mut start_eq = usize::MAX;
191196
let mut end_eq = usize::MAX;
192197
let mut expr_eq = true;
193-
for (index, win) in blocks.windows(2).enumerate() {
198+
for win in blocks.windows(2) {
194199
let l_stmts = win[0].stmts;
195200
let r_stmts = win[1].stmts;
196201

@@ -202,9 +207,7 @@ fn lint_same_then_else<'tcx>(
202207
let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r));
203208

204209
// IF_SAME_THEN_ELSE
205-
// We only lint the first two blocks (index == 0). Further blocks will be linted when that if
206-
// statement is checked
207-
if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq {
210+
if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq {
208211
span_lint_and_note(
209212
cx,
210213
IF_SAME_THEN_ELSE,
@@ -215,16 +218,24 @@ fn lint_same_then_else<'tcx>(
215218
);
216219

217220
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+
)
218229
}
219230

220231
start_eq = start_eq.min(current_start_eq);
221232
end_eq = end_eq.min(current_end_eq);
222233
expr_eq &= block_expr_eq;
234+
}
223235

224-
// We can return if the eq count is 0 from both sides or if it has no unconditional else case
225-
if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
226-
return;
227-
}
236+
// SHARED_CODE_IN_IF_BLOCKS prerequisites
237+
if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
238+
return;
228239
}
229240

230241
if has_expr && !expr_eq {
@@ -275,11 +286,14 @@ fn lint_same_then_else<'tcx>(
275286
end_eq -= moved_start;
276287
}
277288

278-
let mut end_linable = true;
279-
if let Some(expr) = block.expr {
289+
let end_linable = if let Some(expr) = block.expr {
280290
intravisit::walk_expr(&mut walker, expr);
281-
end_linable = walker.uses.iter().any(|x| !block_defs.contains(x));
282-
}
291+
walker.uses.iter().any(|x| !block_defs.contains(x))
292+
} else if end_eq == 0 {
293+
false
294+
} else {
295+
true
296+
};
283297

284298
emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr);
285299
}
@@ -351,7 +365,7 @@ fn emit_shared_code_in_if_blocks_lint(
351365
SHARED_CODE_IN_IF_BLOCKS,
352366
*span,
353367
"All code blocks contain the same code",
354-
"Consider moving the code out like this",
368+
"Consider moving the statements out like this",
355369
sugg.clone(),
356370
Applicability::Unspecified,
357371
);

tests/ui/shared_code_in_if_blocks/shared_at_bot.rs

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,119 @@ fn simple_examples() {
3232
println!("Block end!");
3333
result
3434
};
35+
36+
if x == 9 {
37+
println!("The index is: 6");
38+
39+
println!("Same end of block");
40+
} else if x == 8 {
41+
println!("The index is: 4");
42+
43+
println!("Same end of block");
44+
} else {
45+
println!("Same end of block");
46+
}
47+
48+
// TODO xFrednet 2021-01.13: Fix lint for `if let`
49+
let index = Some(8);
50+
if let Some(index) = index {
51+
println!("The index is: {}", index);
52+
53+
println!("Same end of block");
54+
} else {
55+
println!("Same end of block");
56+
}
57+
58+
if x == 9 {
59+
if x == 8 {
60+
// No parent!!
61+
println!("Hello World");
62+
println!("---");
63+
} else {
64+
println!("Hello World");
65+
}
66+
}
3567
}
3668

3769
/// Simple examples where the move can cause some problems due to moved values
3870
fn simple_but_suggestion_is_invalid() {
39-
// TODO xFrednet 2021-01-12: This
71+
let x = 16;
72+
73+
// Local value
74+
let later_used_value = 17;
75+
if x == 9 {
76+
let _ = 9;
77+
let later_used_value = "A string value";
78+
println!("{}", later_used_value);
79+
} else {
80+
let later_used_value = "A string value";
81+
println!("{}", later_used_value);
82+
// I'm expecting a note about this
83+
}
84+
println!("{}", later_used_value);
85+
86+
// outer function
87+
if x == 78 {
88+
let simple_examples = "I now identify as a &str :)";
89+
println!("This is the new simple_example: {}", simple_examples);
90+
} else {
91+
println!("Separator print statement");
92+
93+
let simple_examples = "I now identify as a &str :)";
94+
println!("This is the new simple_example: {}", simple_examples);
95+
}
96+
simple_examples();
4097
}
4198

4299
/// Tests where the blocks are not linted due to the used value scope
43100
fn not_moveable_due_to_value_scope() {
44-
// TODO xFrednet 2021-01-12: This
101+
let x = 18;
102+
103+
// Using a local value in the moved code
104+
if x == 9 {
105+
let y = 18;
106+
println!("y is: `{}`", y);
107+
} else {
108+
let y = "A string";
109+
println!("y is: `{}`", y);
110+
}
111+
112+
// Using a local value in the expression
113+
let _ = if x == 0 {
114+
let mut result = x + 1;
115+
116+
println!("1. Doing some calculations");
117+
println!("2. Some more calculations");
118+
println!("3. Setting result");
119+
120+
result
121+
} else {
122+
let mut result = x - 1;
123+
124+
println!("1. Doing some calculations");
125+
println!("2. Some more calculations");
126+
println!("3. Setting result");
127+
128+
result
129+
};
130+
131+
let _ = if x == 7 {
132+
let z1 = 100;
133+
println!("z1: {}", z1);
134+
135+
let z2 = z1;
136+
println!("z2: {}", z2);
137+
138+
z2
139+
} else {
140+
let z1 = 300;
141+
println!("z1: {}", z1);
142+
143+
let z2 = z1;
144+
println!("z2: {}", z2);
145+
146+
z2
147+
};
45148
}
46149

47150
fn main() {}

tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ fn trigger_other_lint() {
152152
":D"
153153
}
154154
};
155+
156+
if x == 0 {
157+
println!("I'm single");
158+
} else if x == 68 {
159+
println!("I'm a doppelgänger");
160+
// Don't listen to my clone below
161+
} else {
162+
// Don't listen to my clone above
163+
println!("I'm a doppelgänger");
164+
}
155165
}
156166

157167
fn main() {}

0 commit comments

Comments
 (0)