@@ -40,6 +40,53 @@ declare_clippy_lint! {
4040 "consecutive `ifs` with the same condition"
4141}
4242
43+ declare_clippy_lint ! {
44+ /// **What it does:** Checks for consecutive `if`s with the same function call.
45+ ///
46+ /// **Why is this bad?** This is probably a copy & paste error.
47+ /// Despite the fact that function can have side effects and `if` works as
48+ /// intended, such an approach is implicit and can be considered a "code smell".
49+ ///
50+ /// **Known problems:** Hopefully none.
51+ ///
52+ /// **Example:**
53+ /// ```ignore
54+ /// if foo() == bar {
55+ /// …
56+ /// } else if foo() == bar {
57+ /// …
58+ /// }
59+ /// ```
60+ ///
61+ /// This probably should be:
62+ /// ```ignore
63+ /// if foo() == bar {
64+ /// …
65+ /// } else if foo() == baz {
66+ /// …
67+ /// }
68+ /// ```
69+ ///
70+ /// or if the original code was not a typo and called function mutates a state,
71+ /// consider move the mutation out of the `if` condition to avoid similarity to
72+ /// a copy & paste error:
73+ ///
74+ /// ```ignore
75+ /// let first = foo();
76+ /// if first == bar {
77+ /// …
78+ /// } else {
79+ /// let second = foo();
80+ /// if second == bar {
81+ /// …
82+ /// }
83+ /// }
84+ /// ```
85+ pub SAME_FUNCTIONS_IN_IF_CONDITION ,
86+ pedantic,
87+ "consecutive `ifs` with the same function call"
88+ }
89+
4390declare_clippy_lint ! {
4491 /// **What it does:** Checks for `if/else` with the same body as the *then* part
4592 /// and the *else* part.
@@ -102,7 +149,7 @@ declare_clippy_lint! {
102149 "`match` with identical arm bodies"
103150}
104151
105- declare_lint_pass ! ( CopyAndPaste => [ IFS_SAME_COND , IF_SAME_THEN_ELSE , MATCH_SAME_ARMS ] ) ;
152+ declare_lint_pass ! ( CopyAndPaste => [ IFS_SAME_COND , SAME_FUNCTIONS_IN_IF_CONDITION , IF_SAME_THEN_ELSE , MATCH_SAME_ARMS ] ) ;
106153
107154impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for CopyAndPaste {
108155 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -119,6 +166,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
119166 let ( conds, blocks) = if_sequence ( expr) ;
120167 lint_same_then_else ( cx, & blocks) ;
121168 lint_same_cond ( cx, & conds) ;
169+ lint_same_fns_in_if_cond ( cx, & conds) ;
122170 lint_match_arms ( cx, expr) ;
123171 }
124172 }
@@ -163,6 +211,34 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
163211 }
164212}
165213
214+ /// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
215+ fn lint_same_fns_in_if_cond ( cx : & LateContext < ' _ , ' _ > , conds : & [ & Expr ] ) {
216+ let hash: & dyn Fn ( & & Expr ) -> u64 = & |expr| -> u64 {
217+ let mut h = SpanlessHash :: new ( cx, cx. tables ) ;
218+ h. hash_expr ( expr) ;
219+ h. finish ( )
220+ } ;
221+
222+ let eq: & dyn Fn ( & & Expr , & & Expr ) -> bool = & |& lhs, & rhs| -> bool {
223+ // Do not spawn warning if `IFS_SAME_COND` already produced it.
224+ if SpanlessEq :: new ( cx) . ignore_fn ( ) . eq_expr ( lhs, rhs) {
225+ return false ;
226+ }
227+ SpanlessEq :: new ( cx) . eq_expr ( lhs, rhs)
228+ } ;
229+
230+ for ( i, j) in search_same ( conds, hash, eq) {
231+ span_note_and_lint (
232+ cx,
233+ SAME_FUNCTIONS_IN_IF_CONDITION ,
234+ j. span ,
235+ "this `if` has the same function call as a previous if" ,
236+ i. span ,
237+ "same as this" ,
238+ ) ;
239+ }
240+ }
241+
166242/// Implementation of `MATCH_SAME_ARMS`.
167243fn lint_match_arms < ' tcx > ( cx : & LateContext < ' _ , ' tcx > , expr : & Expr ) {
168244 fn same_bindings < ' tcx > (
0 commit comments