Skip to content

Commit ee830ba

Browse files
committed
Extend IF_SAME_THEN_ELSE to ifs sequences
1 parent 88beb35 commit ee830ba

File tree

3 files changed

+67
-34
lines changed

3 files changed

+67
-34
lines changed

src/copies.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_front::hir::*;
33
use std::collections::HashMap;
44
use std::collections::hash_map::Entry;
55
use utils::{SpanlessEq, SpanlessHash};
6-
use utils::{get_parent_expr, in_macro, span_lint, span_note_and_lint};
6+
use utils::{get_parent_expr, in_macro, span_note_and_lint};
77

88
/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
99
/// `Warn` by default.
@@ -56,26 +56,40 @@ impl LateLintPass for CopyAndPaste {
5656
}
5757

5858
let (conds, blocks) = if_sequence(expr);
59-
lint_same_then_else(cx, expr);
59+
lint_same_then_else(cx, &blocks);
6060
lint_same_cond(cx, &conds);
6161
}
6262
}
6363
}
6464

6565
/// Implementation of `IF_SAME_THEN_ELSE`.
66-
fn lint_same_then_else(cx: &LateContext, expr: &Expr) {
67-
if let ExprIf(_, ref then_block, Some(ref else_expr)) = expr.node {
68-
if let ExprBlock(ref else_block) = else_expr.node {
69-
if SpanlessEq::new(cx).eq_block(&then_block, &else_block) {
70-
span_lint(cx, IF_SAME_THEN_ELSE, expr.span, "this if has the same then and else blocks");
71-
}
72-
}
66+
fn lint_same_then_else(cx: &LateContext, blocks: &[&Block]) {
67+
let hash = |block| -> u64 {
68+
let mut h = SpanlessHash::new(cx);
69+
h.hash_block(block);
70+
h.finish()
71+
};
72+
let eq = |lhs, rhs| -> bool {
73+
SpanlessEq::new(cx).eq_block(lhs, rhs)
74+
};
75+
76+
if let Some((i, j)) = search_same(blocks, hash, eq) {
77+
span_note_and_lint(cx, IF_SAME_THEN_ELSE, j.span, "this if has identical blocks", i.span, "same as this");
7378
}
7479
}
7580

7681
/// Implementation of `IFS_SAME_COND`.
7782
fn lint_same_cond(cx: &LateContext, conds: &[&Expr]) {
78-
if let Some((i, j)) = search_same(cx, conds) {
83+
let hash = |expr| -> u64 {
84+
let mut h = SpanlessHash::new(cx);
85+
h.hash_expr(expr);
86+
h.finish()
87+
};
88+
let eq = |lhs, rhs| -> bool {
89+
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs)
90+
};
91+
92+
if let Some((i, j)) = search_same(conds, hash, eq) {
7993
span_note_and_lint(cx, IFS_SAME_COND, j.span, "this if has the same condition as a previous if", i.span, "same as this");
8094
}
8195
}
@@ -109,13 +123,17 @@ fn if_sequence(mut expr: &Expr) -> (Vec<&Expr>, Vec<&Block>) {
109123
(conds, blocks)
110124
}
111125

112-
fn search_same<'a>(cx: &LateContext, exprs: &[&'a Expr]) -> Option<(&'a Expr, &'a Expr)> {
126+
fn search_same<'a, T, Hash, Eq>(exprs: &[&'a T],
127+
hash: Hash,
128+
eq: Eq) -> Option<(&'a T, &'a T)>
129+
where Hash: Fn(&'a T) -> u64,
130+
Eq: Fn(&'a T, &'a T) -> bool {
113131
// common cases
114132
if exprs.len() < 2 {
115133
return None;
116134
}
117135
else if exprs.len() == 2 {
118-
return if SpanlessEq::new(cx).ignore_fn().eq_expr(&exprs[0], &exprs[1]) {
136+
return if eq(&exprs[0], &exprs[1]) {
119137
Some((&exprs[0], &exprs[1]))
120138
}
121139
else {
@@ -126,14 +144,10 @@ fn search_same<'a>(cx: &LateContext, exprs: &[&'a Expr]) -> Option<(&'a Expr, &'
126144
let mut map : HashMap<_, Vec<&'a _>> = HashMap::with_capacity(exprs.len());
127145

128146
for &expr in exprs {
129-
let mut h = SpanlessHash::new(cx);
130-
h.hash_expr(expr);
131-
let h = h.finish();
132-
133-
match map.entry(h) {
147+
match map.entry(hash(expr)) {
134148
Entry::Occupied(o) => {
135149
for o in o.get() {
136-
if SpanlessEq::new(cx).ignore_fn().eq_expr(o, expr) {
150+
if eq(o, expr) {
137151
return Some((o, expr))
138152
}
139153
}

src/utils/hir.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
246246
}
247247

248248

249+
/// Type used to hash an ast element. This is different from the `Hash` trait on ast types as this
250+
/// trait would consider IDs and spans.
251+
///
252+
/// All expressions kind are hashed, but some might have a weaker hash.
249253
pub struct SpanlessHash<'a, 'tcx: 'a> {
250254
/// Context used to evaluate constant expressions.
251255
cx: &'a LateContext<'a, 'tcx>,

tests/compile-fail/copies.rs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
#![allow(let_and_return)]
66
#![allow(needless_return)]
77
#![allow(unused_variables)]
8-
#![deny(if_same_then_else)]
9-
#![deny(ifs_same_cond)]
108

119
fn foo() -> bool { unimplemented!() }
1210

11+
#[deny(if_same_then_else)]
1312
fn if_same_then_else() -> &'static str {
14-
if true { //~ERROR this if has the same then and else blocks
13+
if true {
1514
foo();
1615
}
17-
else {
16+
else { //~ERROR this if has identical blocks
1817
foo();
1918
}
2019

@@ -26,11 +25,11 @@ fn if_same_then_else() -> &'static str {
2625
foo();
2726
}
2827

29-
let _ = if true { //~ERROR this if has the same then and else blocks
28+
let _ = if true {
3029
foo();
3130
42
3231
}
33-
else {
32+
else { //~ERROR this if has identical blocks
3433
foo();
3534
42
3635
};
@@ -39,14 +38,14 @@ fn if_same_then_else() -> &'static str {
3938
foo();
4039
}
4140

42-
let _ = if true { //~ERROR this if has the same then and else blocks
41+
let _ = if true {
4342
42
4443
}
45-
else {
44+
else { //~ERROR this if has identical blocks
4645
42
4746
};
4847

49-
if true { //~ERROR this if has the same then and else blocks
48+
if true {
5049
let bar = if true {
5150
42
5251
}
@@ -57,7 +56,7 @@ fn if_same_then_else() -> &'static str {
5756
while foo() { break; }
5857
bar + 1;
5958
}
60-
else {
59+
else { //~ERROR this if has identical blocks
6160
let bar = if true {
6261
42
6362
}
@@ -69,15 +68,18 @@ fn if_same_then_else() -> &'static str {
6968
bar + 1;
7069
}
7170

72-
if true { //~ERROR this if has the same then and else blocks
71+
if true {
7372
match 42 {
7473
42 => (),
7574
a if a > 0 => (),
7675
10...15 => (),
7776
_ => (),
7877
}
7978
}
80-
else {
79+
else if false {
80+
foo();
81+
}
82+
else if foo() { //~ERROR this if has identical blocks
8183
match 42 {
8284
42 => (),
8385
a if a > 0 => (),
@@ -86,23 +88,36 @@ fn if_same_then_else() -> &'static str {
8688
}
8789
}
8890

89-
if true { //~ERROR this if has the same then and else blocks
91+
if true {
92+
if let Some(a) = Some(42) {}
93+
}
94+
else { //~ERROR this if has identical blocks
95+
if let Some(a) = Some(42) {}
96+
}
97+
98+
if true {
9099
if let Some(a) = Some(42) {}
91100
}
92101
else {
93-
if let Some(a) = Some(42) {}
102+
if let Some(a) = Some(43) {}
94103
}
95104

96-
if true { //~ERROR this if has the same then and else blocks
105+
if true {
97106
let foo = "";
98107
return &foo[0..];
99108
}
100-
else {
109+
else if false {
110+
let foo = "bar";
111+
return &foo[0..];
112+
}
113+
else { //~ERROR this if has identical blocks
101114
let foo = "";
102115
return &foo[0..];
103116
}
104117
}
105118

119+
#[deny(ifs_same_cond)]
120+
#[allow(if_same_then_else)] // all empty blocks
106121
fn ifs_same_cond() {
107122
let a = 0;
108123
let b = false;

0 commit comments

Comments
 (0)