Skip to content

Commit

Permalink
The shared_code_in_if_blocks lint only operats on entire if expr not …
Browse files Browse the repository at this point in the history
…else ifs
  • Loading branch information
xFrednet committed Jan 13, 2021
1 parent d7195a8 commit d2b051e
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 19 deletions.
43 changes: 26 additions & 17 deletions clippy_lints/src/copies.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
use crate::utils::{
first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, reindent_multiline, snippet,
span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline,
snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
Expand Down Expand Up @@ -141,7 +141,7 @@ declare_clippy_lint! {
/// };
/// ```
pub SHARED_CODE_IN_IF_BLOCKS,
pedantic,
nursery,
"`if` statement with shared code in all blocks"
}

Expand Down Expand Up @@ -183,7 +183,7 @@ fn lint_same_then_else<'tcx>(
) {
// We only lint ifs with multiple blocks
// TODO xFrednet 2021-01-01: Check if it's an else if block
if blocks.len() < 2 {
if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
return;
}

Expand All @@ -193,7 +193,7 @@ fn lint_same_then_else<'tcx>(
let mut start_eq = usize::MAX;
let mut end_eq = usize::MAX;
let mut expr_eq = true;
for (index, win) in blocks.windows(2).enumerate() {
for win in blocks.windows(2) {
let l_stmts = win[0].stmts;
let r_stmts = win[1].stmts;

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

// IF_SAME_THEN_ELSE
// We only lint the first two blocks (index == 0). Further blocks will be linted when that if
// statement is checked
if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq {
if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq {
span_lint_and_note(
cx,
IF_SAME_THEN_ELSE,
Expand All @@ -218,16 +216,24 @@ fn lint_same_then_else<'tcx>(
);

return;
} else {
println!(
"{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}",
win[0].span,
block_expr_eq,
l_stmts.len(),
r_stmts.len()
)
}

start_eq = start_eq.min(current_start_eq);
end_eq = end_eq.min(current_end_eq);
expr_eq &= block_expr_eq;
}

// We can return if the eq count is 0 from both sides or if it has no unconditional else case
if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
return;
}
// SHARED_CODE_IN_IF_BLOCKS prerequisites
if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
return;
}

if has_expr && !expr_eq {
Expand Down Expand Up @@ -278,11 +284,14 @@ fn lint_same_then_else<'tcx>(
end_eq -= moved_start;
}

let mut end_linable = true;
if let Some(expr) = block.expr {
let end_linable = if let Some(expr) = block.expr {
intravisit::walk_expr(&mut walker, expr);
end_linable = walker.uses.iter().any(|x| !block_defs.contains(x));
}
walker.uses.iter().any(|x| !block_defs.contains(x))
} else if end_eq == 0 {
false
} else {
true
};

emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr);
}
Expand Down Expand Up @@ -354,7 +363,7 @@ fn emit_shared_code_in_if_blocks_lint(
SHARED_CODE_IN_IF_BLOCKS,
*span,
"All code blocks contain the same code",
"Consider moving the code out like this",
"Consider moving the statements out like this",
sugg.clone(),
Applicability::Unspecified,
);
Expand Down
107 changes: 105 additions & 2 deletions tests/ui/shared_code_in_if_blocks/shared_at_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,119 @@ fn simple_examples() {
println!("Block end!");
result
};

if x == 9 {
println!("The index is: 6");

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

println!("Same end of block");
} else {
println!("Same end of block");
}

// TODO xFrednet 2021-01.13: Fix lint for `if let`
let index = Some(8);
if let Some(index) = index {
println!("The index is: {}", index);

println!("Same end of block");
} else {
println!("Same end of block");
}

if x == 9 {
if x == 8 {
// No parent!!
println!("Hello World");
println!("---");
} else {
println!("Hello World");
}
}
}

/// Simple examples where the move can cause some problems due to moved values
fn simple_but_suggestion_is_invalid() {
// TODO xFrednet 2021-01-12: This
let x = 16;

// Local value
let later_used_value = 17;
if x == 9 {
let _ = 9;
let later_used_value = "A string value";
println!("{}", later_used_value);
} else {
let later_used_value = "A string value";
println!("{}", later_used_value);
// I'm expecting a note about this
}
println!("{}", later_used_value);

// outer function
if x == 78 {
let simple_examples = "I now identify as a &str :)";
println!("This is the new simple_example: {}", simple_examples);
} else {
println!("Separator print statement");

let simple_examples = "I now identify as a &str :)";
println!("This is the new simple_example: {}", simple_examples);
}
simple_examples();
}

/// Tests where the blocks are not linted due to the used value scope
fn not_moveable_due_to_value_scope() {
// TODO xFrednet 2021-01-12: This
let x = 18;

// Using a local value in the moved code
if x == 9 {
let y = 18;
println!("y is: `{}`", y);
} else {
let y = "A string";
println!("y is: `{}`", y);
}

// Using a local value in the expression
let _ = if x == 0 {
let mut result = x + 1;

println!("1. Doing some calculations");
println!("2. Some more calculations");
println!("3. Setting result");

result
} else {
let mut result = x - 1;

println!("1. Doing some calculations");
println!("2. Some more calculations");
println!("3. Setting result");

result
};

let _ = if x == 7 {
let z1 = 100;
println!("z1: {}", z1);

let z2 = z1;
println!("z2: {}", z2);

z2
} else {
let z1 = 300;
println!("z1: {}", z1);

let z2 = z1;
println!("z2: {}", z2);

z2
};
}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ fn trigger_other_lint() {
":D"
}
};

if x == 0 {
println!("I'm single");
} else if x == 68 {
println!("I'm a doppelgänger");
// Don't listen to my clone below
} else {
// Don't listen to my clone above
println!("I'm a doppelgänger");
}
}

fn main() {}

0 comments on commit d2b051e

Please sign in to comment.