@@ -3,16 +3,19 @@ use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, sn
3
3
use clippy_utils:: ty:: needs_ordered_drop;
4
4
use clippy_utils:: visitors:: for_each_expr;
5
5
use clippy_utils:: {
6
- capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence ,
7
- is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName , HirEqInterExpr , SpanlessEq ,
6
+ capture_local_usage, def_path_def_ids , eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
7
+ if_sequence , is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName , HirEqInterExpr , SpanlessEq ,
8
8
} ;
9
9
use core:: iter;
10
10
use core:: ops:: ControlFlow ;
11
+ use rustc_data_structures:: fx:: FxHashSet ;
11
12
use rustc_errors:: Applicability ;
13
+ use rustc_hir:: def_id:: DefId ;
12
14
use rustc_hir:: intravisit;
13
15
use rustc_hir:: { BinOpKind , Block , Expr , ExprKind , HirId , HirIdSet , Stmt , StmtKind } ;
14
16
use rustc_lint:: { LateContext , LateLintPass } ;
15
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
17
+ use rustc_middle:: query:: Key ;
18
+ use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
16
19
use rustc_span:: hygiene:: walk_chain;
17
20
use rustc_span:: source_map:: SourceMap ;
18
21
use rustc_span:: { BytePos , Span , Symbol } ;
@@ -159,7 +162,19 @@ declare_clippy_lint! {
159
162
"`if` statement with shared code in all blocks"
160
163
}
161
164
162
- declare_lint_pass ! ( CopyAndPaste => [
165
+ pub struct CopyAndPaste {
166
+ ignore_interior_mutability : Vec < String > ,
167
+ }
168
+
169
+ impl CopyAndPaste {
170
+ pub fn new ( ignore_interior_mutability : Vec < String > ) -> Self {
171
+ Self {
172
+ ignore_interior_mutability,
173
+ }
174
+ }
175
+ }
176
+
177
+ impl_lint_pass ! ( CopyAndPaste => [
163
178
IFS_SAME_COND ,
164
179
SAME_FUNCTIONS_IN_IF_CONDITION ,
165
180
IF_SAME_THEN_ELSE ,
@@ -170,7 +185,14 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
170
185
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
171
186
if !expr. span . from_expansion ( ) && matches ! ( expr. kind, ExprKind :: If ( ..) ) && !is_else_clause ( cx. tcx , expr) {
172
187
let ( conds, blocks) = if_sequence ( expr) ;
173
- lint_same_cond ( cx, & conds) ;
188
+ let mut ignored_ty_ids = FxHashSet :: default ( ) ;
189
+ for ignored_ty in & self . ignore_interior_mutability {
190
+ let path: Vec < & str > = ignored_ty. split ( "::" ) . collect ( ) ;
191
+ for id in def_path_def_ids ( cx, path. as_slice ( ) ) {
192
+ ignored_ty_ids. insert ( id) ;
193
+ }
194
+ }
195
+ lint_same_cond ( cx, & conds, & ignored_ty_ids) ;
174
196
lint_same_fns_in_if_cond ( cx, & conds) ;
175
197
let all_same =
176
198
!is_lint_allowed ( cx, IF_SAME_THEN_ELSE , expr. hir_id ) && lint_if_same_then_else ( cx, & conds, & blocks) ;
@@ -547,23 +569,41 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
547
569
} )
548
570
}
549
571
572
+ fn method_caller_is_ignored_or_mutable (
573
+ cx : & LateContext < ' _ > ,
574
+ caller_expr : & Expr < ' _ > ,
575
+ ignored_ty_ids : & FxHashSet < DefId > ,
576
+ ) -> bool {
577
+ let caller_ty = cx. typeck_results ( ) . expr_ty ( caller_expr) ;
578
+ let is_ignored_ty = if let Some ( adt_id) = caller_ty. ty_adt_id ( ) && ignored_ty_ids. contains ( & adt_id) {
579
+ true
580
+ } else {
581
+ false
582
+ } ;
583
+
584
+ if is_ignored_ty
585
+ || caller_ty. is_mutable_ptr ( )
586
+ || path_to_local ( caller_expr)
587
+ . and_then ( |hid| find_binding_init ( cx, hid) )
588
+ . is_none ( )
589
+ {
590
+ return true ;
591
+ }
592
+
593
+ false
594
+ }
595
+
550
596
/// Implementation of `IFS_SAME_COND`.
551
- fn lint_same_cond ( cx : & LateContext < ' _ > , conds : & [ & Expr < ' _ > ] ) {
597
+ fn lint_same_cond ( cx : & LateContext < ' _ > , conds : & [ & Expr < ' _ > ] , ignored_ty_ids : & FxHashSet < DefId > ) {
552
598
for ( i, j) in search_same (
553
599
conds,
554
600
|e| hash_expr ( cx, e) ,
555
601
|lhs, rhs| {
556
- // If any side (ex. lhs) is a method call, and the caller is not mutable,
557
- // then we can ignore side effects?
558
602
if let ExprKind :: MethodCall ( _, caller, _, _) = lhs. kind {
559
- if path_to_local ( caller)
560
- . and_then ( |hir_id| find_binding_init ( cx, hir_id) )
561
- . is_some ( )
562
- {
563
- // caller is not declared as mutable
564
- SpanlessEq :: new ( cx) . eq_expr ( lhs, rhs)
565
- } else {
603
+ if method_caller_is_ignored_or_mutable ( cx, caller, ignored_ty_ids) {
566
604
false
605
+ } else {
606
+ SpanlessEq :: new ( cx) . eq_expr ( lhs, rhs)
567
607
}
568
608
} else {
569
609
eq_expr_value ( cx, lhs, rhs)
0 commit comments