@@ -2,10 +2,12 @@ use std::ops::ControlFlow;
22
33use clippy_utils:: diagnostics:: span_lint;
44use clippy_utils:: res:: MaybeDef ;
5+ use clippy_utils:: ty:: expr_sig;
56use clippy_utils:: visitors:: for_each_expr;
67use clippy_utils:: { SpanlessEq , higher, peel_hir_expr_while, sym} ;
7- use rustc_hir:: { Expr , ExprKind , UnOp } ;
8+ use rustc_hir:: { Expr , ExprKind , Node , UnOp } ;
89use rustc_lint:: { LateContext , LateLintPass } ;
10+ use rustc_middle:: ty;
911use rustc_session:: declare_lint_pass;
1012use rustc_span:: Span ;
1113use rustc_span:: symbol:: Symbol ;
@@ -112,6 +114,45 @@ fn try_parse_op_call<'tcx>(
112114 None
113115}
114116
117+ fn is_set_mutated < ' tcx > ( cx : & LateContext < ' tcx > , contains_expr : & OpExpr < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
118+ let expr = peel_hir_expr_while ( expr, |e| {
119+ if let ExprKind :: Unary ( UnOp :: Not , e) = e. kind {
120+ Some ( e)
121+ } else {
122+ None
123+ }
124+ } ) ;
125+
126+ if let ExprKind :: MethodCall ( _, receiver, ..) = expr. kind
127+ && let receiver = receiver. peel_borrows ( )
128+ && let receiver_ty = cx. typeck_results ( ) . expr_ty ( receiver) . peel_refs ( )
129+ && ( receiver_ty. is_diag_item ( cx, sym:: HashSet ) || receiver_ty. is_diag_item ( cx, sym:: BTreeSet ) )
130+ && SpanlessEq :: new ( cx) . eq_expr ( contains_expr. receiver , receiver)
131+ && let Some ( method_def) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
132+ && let method_fn_sig = cx. tcx . fn_sig ( method_def) . instantiate_identity ( ) . skip_binder ( )
133+ && let Some ( self_ty) = method_fn_sig. inputs ( ) . first ( )
134+ && let ty:: Ref ( _, _, ty:: Mutability :: Mut ) = self_ty. kind ( )
135+ {
136+ return true ;
137+ }
138+
139+ let child = expr. peel_borrows ( ) ;
140+ let child_ty = cx. typeck_results ( ) . expr_ty ( child) . peel_refs ( ) ;
141+ if ( child_ty. is_diag_item ( cx, sym:: HashSet ) || child_ty. is_diag_item ( cx, sym:: BTreeSet ) )
142+ && SpanlessEq :: new ( cx) . eq_expr ( contains_expr. receiver , child)
143+ && let Node :: Expr ( parent) = cx. tcx . parent_hir_node ( expr. hir_id )
144+ && let ExprKind :: Call ( func, args) = parent. kind
145+ && let Some ( func_sig) = expr_sig ( cx, func)
146+ && let Some ( set_index) = args. iter ( ) . position ( |arg| arg. hir_id == expr. hir_id )
147+ && let Some ( set_ty) = func_sig. input ( set_index) . map ( |ty| ty. skip_binder ( ) )
148+ && let ty:: Ref ( _, _, ty:: Mutability :: Mut ) = set_ty. kind ( )
149+ {
150+ return true ;
151+ }
152+
153+ false
154+ }
155+
115156fn find_insert_calls < ' tcx > (
116157 cx : & LateContext < ' tcx > ,
117158 contains_expr : & OpExpr < ' tcx > ,
@@ -122,9 +163,14 @@ fn find_insert_calls<'tcx>(
122163 && SpanlessEq :: new ( cx) . eq_expr ( contains_expr. receiver , insert_expr. receiver )
123164 && SpanlessEq :: new ( cx) . eq_expr ( contains_expr. value , insert_expr. value )
124165 {
125- ControlFlow :: Break ( insert_expr)
126- } else {
127- ControlFlow :: Continue ( ( ) )
166+ return ControlFlow :: Break ( Some ( insert_expr) ) ;
167+ }
168+
169+ if is_set_mutated ( cx, contains_expr, e) {
170+ return ControlFlow :: Break ( None ) ;
128171 }
172+
173+ ControlFlow :: Continue ( ( ) )
129174 } )
175+ . flatten ( )
130176}
0 commit comments