diff --git a/CHANGELOG.md b/CHANGELOG.md index 64864c2e2780..835ed4fb7e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2180,6 +2180,7 @@ Released 2018-09-13 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated +[`shared_code_in_if_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#shared_code_in_if_blocks [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 46ce92ea6d78..4866aa3d952c 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,8 +1,20 @@ -use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note}; -use rustc_hir::{Block, Expr}; +use crate::utils::ast_utils::IdentIter; +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, multispan_sugg_with_applicability, + reindent_multiline, snippet, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, +}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::StmtKind; +use rustc_hir::{Block, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::Ident; +use std::borrow::Cow; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -103,7 +115,45 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); +declare_clippy_lint! { + /// **What it does:** Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// **Why is this bad?** Duplicate code is less maintainable. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Could be written as: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + pub SHARED_CODE_IN_IF_BLOCKS, + pedantic, + "`if` statement with shared code in all blocks" +} + +declare_lint_pass!(CopyAndPaste => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + SHARED_CODE_IN_IF_BLOCKS +]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -118,30 +168,256 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { } let (conds, blocks) = if_sequence(expr); - lint_same_then_else(cx, &blocks); + // Conditions lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); + // Block duplication + lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); } } } -/// Implementation of `IF_SAME_THEN_ELSE`. -fn lint_same_then_else(cx: &LateContext<'_>, blocks: &[&Block<'_>]) { - let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) }; +/// Implementation of `SHARED_CODE_IN_IF_BLOCKS` and `IF_SAME_THEN_ELSE` if the blocks are equal. +fn lint_same_then_else<'tcx>( + cx: &LateContext<'tcx>, + blocks: &[&Block<'tcx>], + has_unconditional_else: bool, + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + // TODO xFrednet 2021-01-01: Check if it's an else if block + if blocks.len() < 2 { + return; + } - if let Some((i, j)) = search_same_sequenced(blocks, eq) { - span_lint_and_note( + let has_expr = blocks[0].expr.is_some(); + + // Check if each block has shared code + 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() { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; + + let mut evaluator = SpanlessEq::new(cx); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { + evaluator.eq_stmt(l, r) + }); + 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 { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return; + } + + 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; + } + } + + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + // Only the start is the same + if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { + emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr); + } else if end_eq != 0 && (!has_expr || !expr_eq) { + let block = blocks[blocks.len() - 1]; + let stmts = block.stmts.split_at(start_eq).1; + let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq); + + // Scan block + let mut walker = SymbolFinderVisitor::new(cx); + for stmt in block_stmts { + intravisit::walk_stmt(&mut walker, stmt); + } + let mut block_defs = walker.defs; + + // Scan moved stmts + let mut moved_start: Option = None; + let mut walker = SymbolFinderVisitor::new(cx); + for (index, stmt) in moved_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut walker, stmt); + + for value in &walker.uses { + // Well we can't move this and all prev statements. So reset + if block_defs.contains(&value) { + moved_start = Some(index + 1); + walker.defs.drain().for_each(|x| { + block_defs.insert(x); + }); + } + } + + walker.uses.clear(); + } + + if let Some(moved_start) = moved_start { + end_eq -= moved_start; + } + + let mut end_linable = true; + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + end_linable = walker.uses.iter().any(|x| !block_defs.contains(x)); + } + + emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); + } +} + +fn emit_shared_code_in_if_blocks_lint( + cx: &LateContext<'tcx>, + start_stmts: usize, + end_stmts: usize, + lint_end: bool, + blocks: &[&Block<'tcx>], + if_expr: &'tcx Expr<'_>, +) { + if start_stmts == 0 && !lint_end { + return; + } + + // (help, span, suggestion) + let mut suggestions: Vec<(&str, Span, String)> = vec![]; + + if start_stmts > 0 { + let block = blocks[0]; + let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); + let span_end = block.stmts[start_stmts - 1].span.source_callsite(); + + let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_span = block.stmts[0].span.source_callsite().to(span_end); + let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + + let span = span_start.to(span_end); + suggestions.push(("START HELP", span, suggestion.to_string())); + } + + if lint_end { + let block = blocks[blocks.len() - 1]; + let span_end = block.span.shrink_to_hi(); + + let moved_start = if end_stmts == 0 && block.expr.is_some() { + block.expr.unwrap().span + } else { + block.stmts[block.stmts.len() - end_stmts].span + } + .source_callsite(); + let moved_end = if let Some(expr) = block.expr { + expr.span + } else { + block.stmts[block.stmts.len() - 1].span + } + .source_callsite(); + + let moved_span = moved_start.to(moved_end); + let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let indent = indent_of(cx, if_expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); + + let span = moved_start.to(span_end); + suggestions.push(("END_RANGE", span, suggestion.to_string())); + } + + if suggestions.len() == 1 { + let (_, span, sugg) = &suggestions[0]; + span_lint_and_sugg( cx, - IF_SAME_THEN_ELSE, - j.span, - "this `if` has identical blocks", - Some(i.span), - "same as this", + SHARED_CODE_IN_IF_BLOCKS, + *span, + "All code blocks contain the same code", + "Consider moving the code out like this", + sugg.clone(), + Applicability::Unspecified, + ); + } else { + span_lint_and_then( + cx, + SHARED_CODE_IN_IF_BLOCKS, + if_expr.span, + "All if blocks contain the same code", + move |diag| { + for (help, span, sugg) in suggestions { + diag.span_suggestion(span, help, sugg, Applicability::Unspecified); + } + }, ); } } +pub struct SymbolFinderVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + defs: FxHashSet, + uses: FxHashSet, +} + +impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + SymbolFinderVisitor { + cx, + defs: FxHashSet::default(), + uses: FxHashSet::default(), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { + let local_id = l.pat.hir_id; + self.defs.insert(local_id); + if let Some(expr) = l.init { + intravisit::walk_expr(self, expr); + } + } + + fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { + if let rustc_hir::QPath::Resolved(_, ref path) = *qpath { + if path.segments.len() == 1 { + if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { + self.uses.insert(var); + } + } + } + } +} + /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { @@ -195,15 +471,3 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { ); } } - -fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> -where - Eq: Fn(&T, &T) -> bool, -{ - for win in exprs.windows(2) { - if eq(&win[0], &win[1]) { - return Some((&win[0], &win[1])); - } - } - None -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f12994c7a605..4610b0748d27 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -567,6 +567,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, + &copies::SHARED_CODE_IN_IF_BLOCKS, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, @@ -1285,6 +1286,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 87a957a9bd24..dbac1e749042 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -463,32 +463,28 @@ impl DecimalLiteralRepresentation { { return Err(WarningType::DecimalRepresentation); } - } else if digits.len() < 4 { - // Lint for Literals with a hex-representation of 2 or 3 digits - let f = &digits[0..1]; // first digit - let s = &digits[1..]; // suffix - - // Powers of 2 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) - // Powers of 2 minus 1 - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } } else { - // Lint for Literals with a hex-representation of 4 digits or more let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix - - // Powers of 2 with a margin of +15/-16 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) - // Lint for representations with only 0s and Fs, while allowing 7 as the first - // digit - || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) - { - return Err(WarningType::DecimalRepresentation); + if digits.len() < 4 { + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } + } else { + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 44c974b9d985..668d12119a02 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -39,44 +39,28 @@ pub fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>] // "mul" is omitted because lhs can be negative. _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } else { match (mm, arith) { (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (), _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); } #[derive(PartialEq, Eq)] diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 8d8ad497be6a..007953aa9c11 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -369,6 +369,15 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Counts how many elements of the slices are equal as per `eq_fn`. +pub fn count_eq( + left: &mut dyn Iterator, + right: &mut dyn Iterator, + mut eq_fn: impl FnMut(&X, &X) -> bool, +) -> usize { + left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count() +} + /// Checks if two expressions evaluate to the same value, and don't contain any side effects. pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad77283..b4728713e3a4 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -26,7 +26,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::diagnostics::*; -pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::collections::hash_map::Entry; diff --git a/tests/ui/a1.rs b/tests/ui/a1.rs new file mode 100644 index 000000000000..cc1730799c80 --- /dev/null +++ b/tests/ui/a1.rs @@ -0,0 +1,161 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the start of blocks + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + println!("This is the same: vvv"); + let _z = y; + + println!("Different end 1"); + } else { + let y = 1 << 7; + println!("This is the same: vvv"); + let _z = y; + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + println!("Well I'm the number 2"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + println!("Well I'm the value x"); + } + + // Only in else if + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code with else"); + println!("x is 17"); + } else { + println!("I share code with else"); + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // In method argument + let mut arg = 0; + fn test_fn(_: i32, _: i32) {} + test_fn( + if x == 1 { 11 } else { arg }, + if x == 1 { + arg += 1; + println!("Argument and therefore not surely moveable"); + 33 + } else { + arg += 1; + println!("Argument and therefore not surely moveable"); + 44 + }, + ); + + // In condition + while (if x == 1 { + println!("This is readable right?"); + true + } else { + println!("This is readable right?"); + false + }) { + println!("I'm a loop"); + } +} + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + y + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +fn simple_but_not_fixable() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +fn main() {} diff --git a/tests/ui/a2.rs b/tests/ui/a2.rs new file mode 100644 index 000000000000..0f77aa9b89e7 --- /dev/null +++ b/tests/ui/a2.rs @@ -0,0 +1,166 @@ +// run-rustfix + +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the end of blocks + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + println!("Different start 1"); + + println!("This is the same: vvv"); + let y = 1 << 5; + + let _z = 1 << 1; + } else { + println!("Different start 2"); + + println!("This is the same: vvv"); + let y = 1 << 5; + + let _z = 1 << 16; + } + + // No else + if x == 2 { + println!("Hello reviewer :D"); + println!("~ From test"); + } else if x == 9 { + println!("Howdy stranger =^.^="); + println!("~ From test"); + } + + // Only else if + if x == 0 { + println!("I'm THE BRANCH OF POWER"); + } else if x == 99 { + println!("x is 99"); + println!("Doppelgänger"); + } else { + println!("We are in the else branch"); + println!("Doppelgänger"); + } + + // In method argument + let mut arg = 0; + fn test_fn(_: i32, _: i32) {} + test_fn( + if x == 1 { + println!("Branch 1"); + arg + } else { + println!("Branch 2"); + arg + }, + if x == 1 { + println!("Branch A"); + arg += 1; + println!("Argument and therefore not surely moveable"); + 44 + } else { + println!("Branch B"); + arg += 1; + println!("Argument and therefore not surely moveable"); + 44 + }, + ); + + // In condition + while (if x == 1 { + println!("The next line is false:"); + println!("-> This is readable right!"); + false + } else { + println!("The next line is a lie:"); + println!("-> This is readable right!"); + false + }) { + println!("I'm a loop"); + } +} + +fn simple_examples() { + // TODO xFrednet 2021-01-06: Test with const literals at the end + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; +} + +fn simple_but_not_fixable() { + // TODO +} + +fn i_love_tests() { + let x = 9; + + if x == 9 { + println!("1"); + + println!("Value {}", x); + } else { + println!("2"); + + println!("Value {}", x); + } + + fn test_fn(_x: i32, _y: i32) {} + + if x == 9 { + println!("1"); + + let y = 8; + test_fn(x, y); + } else { + println!("2"); + + let y = 16; + test_fn(x, y); + } + + let mut y = 0; + let _ = if x == 8 { + println!("1"); + y = 9; + } else { + println!("2"); + y = 9; + }; + + let _ = if x == 8 { + println!("1"); + let z = y; + } else { + println!("2"); + let z = y; + }; +} + +fn main() {} diff --git a/tests/ui/aaa_1.rs b/tests/ui/aaa_1.rs new file mode 100644 index 000000000000..07f30a043948 --- /dev/null +++ b/tests/ui/aaa_1.rs @@ -0,0 +1,51 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +fn main() { + let x = 19; + + let mut y = 8; + let user = (17, 19); + if x == 1 { + println!("Hello World"); + + println!("11111"); + + let h = user.0; + let x = 0; + y = x; + let z = x; + } else { + println!("Hello World"); + + println!("33333"); + + let h = user.0; + let x = 0; + y = x; + let z = x; + }; + + /* + else if x == 2 { + println!("Hello World"); + + println!("22222"); + + 16 + } + */ + + // TODO xFrednet 2021-01-02: Make a test with overlapping eq regions (else ifs) (Make sure + // suggestions are working) TODO xFrednet 2021-01-03: Test if as function parameter, tuple + // constructor, index, while loop condition + + // TODO xFrednet 2021-01-02: Test where only the expression is the same + // TODO xFrednet 2021-01-02: Test where the block only has an expression + // TODO xFrednet 2021-01-02: Test with let on a previous line let _ = \n if... + // TODO xFrednet 2021-01-03: Tests with unreadable formatting (Inline if, Not indented, The + // Python like Clusterfuck) TODO xFrednet 2021-01-03: Test multiline condition if x == 9 \n + // x == 8 {} TODO xFrednet 2021-01-03: Test if for return/break (Only move to front) + // TODO xFrednet 2021-01-03: Test in inline closures + // TODO xFrednet 2021-01-10: Test with structs and tuples +} diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index c986c992a07a..bb5b6f5ec043 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 9c5fe02f7519..35a2e139c11d 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -5,7 +5,8 @@ clippy::never_loop, clippy::no_effect, clippy::unused_unit, - clippy::zero_divided_by_zero + clippy::zero_divided_by_zero, + clippy::shared_code_in_if_blocks )] struct Foo { diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index d9fdf06fa8b7..2f38052fc209 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -1,111 +1,111 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:28:12 + --> $DIR/if_same_then_else.rs:21:13 | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block +LL | if true { + | _____________^ LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); -LL | | } +LL | | } else { | |_____^ | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else.rs:20:13 + --> $DIR/if_same_then_else.rs:29:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | Foo { bar: 42 }; LL | | 0..10; -LL | | ..; ... | LL | | foo(); -LL | | } else { +LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:66:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | 0.0 -LL | | }; - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:64:21 + --> $DIR/if_same_then_else.rs:65:21 | LL | let _ = if true { | _____________________^ LL | | 0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:73:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:67:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | -0.0 +LL | | 0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:71:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:72:21 | LL | let _ = if true { | _____________________^ LL | | -0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:89:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:74:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | 42 +LL | | -0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:87:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:88:21 | LL | let _ = if true { | _____________________^ LL | | 42 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:101:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:90:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; -LL | | } +LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:94:13 + --> $DIR/if_same_then_else.rs:102:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | let bar = if true { 42 } else { 43 }; LL | | -LL | | while foo() { ... | LL | | bar + 1; -LL | | } else { +LL | | } | |_____^ error: aborting due to 5 previous errors diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index e83ce47e5630..62bd313be54b 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -5,7 +5,8 @@ clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, - clippy::single_element_loop + clippy::single_element_loop, + clippy::shared_code_in_if_blocks )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index f98e30fa376f..3eadaf62d139 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -1,18 +1,4 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:21:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | for _ in &[42] { -LL | | let foo: &Option<_> = &Some::(42); -... | -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::if-same-then-else` implied by `-D warnings` -note: same as this --> $DIR/if_same_then_else2.rs:12:13 | LL | if true { @@ -24,18 +10,22 @@ LL | | if true { LL | | } LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:35:12 + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let Some(a) = Some(42) {} +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::(42); +... | +LL | | } LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { @@ -43,18 +33,18 @@ LL | if true { LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:42:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | if let Some(a) = Some(42) {} LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { @@ -62,18 +52,18 @@ LL | if true { LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:92:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | f32::NAN -LL | | }; +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { @@ -81,18 +71,18 @@ LL | let _ = if true { LL | | f32::NAN LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:99:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | Ok("foo")?; -LL | | } +LL | | f32::NAN +LL | | }; | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { @@ -100,18 +90,18 @@ LL | if true { LL | | Ok("foo")?; LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { @@ -120,6 +110,16 @@ LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:124:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ error: aborting due to 6 previous errors diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 32a67f181df4..9fd3f875a5f1 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -2,7 +2,8 @@ unused_variables, unused_assignments, clippy::similar_names, - clippy::blacklisted_name + clippy::blacklisted_name, + clippy::shared_code_in_if_blocks )] #![warn(clippy::useless_let_if_seq)] diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index 7de560c73486..9cf2e10a5ee5 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:64:5 + --> $DIR/let_if_seq.rs:65:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:69:5 + --> $DIR/let_if_seq.rs:70:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:77:5 + --> $DIR/let_if_seq.rs:78:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:106:5 + --> $DIR/let_if_seq.rs:107:5 | LL | / let mut baz = 0; LL | | if f() { diff --git a/tests/ui/shared_code_in_if_blocks.rs b/tests/ui/shared_code_in_if_blocks.rs new file mode 100644 index 000000000000..71246252298e --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks.rs @@ -0,0 +1,212 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +macro_rules! foo { + () => { + println!("I'm a macro, yay"); + }; +} + +/// This should be caught by the `if_same_then_else` lint and be ignored by the +/// `shared_code_in_if_blocks` lint +fn other_lint() { + let x = 10; + + // Only two blocks are the same + let _ = if x == 9 { + 9 + } else if x == 7 { + 7 + } else { + 7 + }; +} + +fn everything_is_okay() { + let x = 10; + + // Single branch + if x == 1 { + println!("I'm an awesome clippy test") + } + + // Same start in only two blocks + if x == 10 { + println!("x is a value"); + println!("x is 10"); + } else if x == 1 { + println!("x is 1"); + } else { + println!("x is a value"); + } + + // Shared code in the middle + let _ = if x == 7 { + let mut z = 3; + println!("x is a value"); + z *= 2; + z + } else if x == 100 { + let mut y = 1; + println!("x is a value"); + y += 9; + y + } else { + let x = 2; + println!("x is a value"); + x + }; + + // Returns value with the same name + let _ = if x == 9 { + let x = 9; + println!("--"); + x + } else { + let x = 18; + println!("^^"); + x + }; + + for index in 0..2 { + // Okay because we don't have an unconditional else case + if index == 6 { + println!("Six"); + continue; + } else if index == 9 { + println!("Nine"); + continue; + } + + println!("---"); + } +} + +fn shared_code_at_start() { + let x = 17; + + // First statement is the same + let _ = if x == 1 { + // I'm a comment that shouldn't matter for the lint + println!("Hello World"); + + 4 + } else { + // I'm another comment, + // Nice to see you here in the tests + println!("Hello World"); + + 16 + }; + + // First statement is the same + if x == 19 { + println!("Hello World"); + let mut y = 9; + y += 1; + let _z = y; + } else { + println!("Hello World"); + }; + + if x == 8 { + println!("Hello World"); + println!("How are you today?"); + println!("From: Test"); + let _x = 10; + let _ = true; + } else if x != 2 { + println!("Hello World"); + println!("How are you today?"); + println!("From: Test"); + let _x = 10; + let _ = false; + } else { + println!("Hello World"); + println!("How are you today?"); + println!("From: Test"); + let _x = 10; + } +} + +fn shared_code_at_end() { + let x = 10; + + // Same code at the end + if x == 8 { + let _ = true; + println!("Hello World"); + } else if x != 2 { + let _ = false; + println!("Hello World"); + } else { + println!("Hello World"); + } + + let _z = if x == 8 { + println!("Branch 1"); + let z = 10; + foo!(); + z + } else { + println!("Branch 2"); + let z = 10; + foo!(); + z + }; + + let _z = if x == 8 { + println!("Branch 1"); + let mut z = 1; + z += 10; + foo!(); + z + } else { + println!("Branch 2"); + let mut z = 2; + z += 10; + foo!(); + z + }; + + // Lint at start and end + let _ = if x == 1 { + println!("I'm the same as my brother branch"); + let _a = 99; + println!("End of block"); + false + } else { + println!("I'm the same as my brother branch"); + let _b = 17; + println!("End of block"); + false + }; +} + +fn expr_with_unit_type() { + // This lint should also evaluate equality of the expression if it's return type is `()` + let x = 9; + + // Don't lint + if x == 9 { + let _a = 17; + let _b = 49; + + println!("A message") + } else { + let _b = 49; + + println!("A different message message") + } + + // lint + if x == 7 { + let _a = 0; + + println!("We are doppelgänger") + } else { + println!("We are doppelgänger") + } +} + +fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks.stderr new file mode 100644 index 000000000000..5b590a2c6bbe --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks.stderr @@ -0,0 +1,146 @@ +error: this `if` has identical blocks + --> $DIR/shared_code_in_if_blocks.rs:18:22 + | +LL | } else if x == 7 { + | ______________________^ +LL | | 7 +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_code_in_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_code_in_if_blocks.rs:20:12 + | +LL | } else { + | ____________^ +LL | | 7 +LL | | }; + | |_____^ + +error: All if blocks contain the same code at the start + --> $DIR/shared_code_in_if_blocks.rs:89:23 + | +LL | let _ = if x == 1 { + | _______________________^ +LL | | // I'm a comment that shouldn't matter for the lint +LL | | println!("Hello World"); + | |________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_code_in_if_blocks.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the start + --> $DIR/shared_code_in_if_blocks.rs:103:16 + | +LL | if x == 19 { + | ________________^ +LL | | println!("Hello World"); + | |________________________________^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the start + --> $DIR/shared_code_in_if_blocks.rs:112:15 + | +LL | if x == 8 { + | _______________^ +LL | | println!("Hello World"); +LL | | println!("How are you today?"); +LL | | println!("From: Test"); +LL | | let _x = 10; + | |____________________^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the start + --> $DIR/shared_code_in_if_blocks.rs:118:22 + | +LL | } else if x != 2 { + | ______________________^ +LL | | println!("Hello World"); +LL | | println!("How are you today?"); +LL | | println!("From: Test"); +LL | | let _x = 10; + | |____________________^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:138:9 + | +LL | / println!("Hello World"); +LL | | } else if x != 2 { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:141:9 + | +LL | / println!("Hello World"); +LL | | } else { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:148:9 + | +LL | / let z = 10; +LL | | foo!(); +LL | | z +LL | | } else { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:161:9 + | +LL | / z += 10; +LL | | foo!(); +LL | | z +LL | | } else { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the start + --> $DIR/shared_code_in_if_blocks.rs:173:23 + | +LL | let _ = if x == 1 { + | _______________________^ +LL | | println!("I'm the same as my brother branch"); + | |______________________________________________________^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:176:9 + | +LL | / println!("End of block"); +LL | | false +LL | | } else { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: All if blocks contain the same code at the end + --> $DIR/shared_code_in_if_blocks.rs:206:9 + | +LL | / println!("We are doppelgänger") +LL | | } else { + | |_____^ + | + = help: Consider moving the code out of the if statement to prevent code duplication + +error: aborting due to 12 previous errors +