@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils:: higher:: ForLoop ;
44use clippy_utils:: source:: snippet_opt;
55use clippy_utils:: ty:: { get_iterator_item_ty, implements_trait} ;
6- use clippy_utils:: { fn_def_id, get_parent_expr} ;
6+ use clippy_utils:: { can_mut_borrow_both , fn_def_id, get_parent_expr, path_to_local } ;
77use rustc_errors:: Applicability ;
88use rustc_hir:: def_id:: DefId ;
9- use rustc_hir:: { Expr , ExprKind } ;
9+ use rustc_hir:: { BindingAnnotation , Expr , ExprKind , Node , PatKind , StmtKind } ;
1010use rustc_lint:: LateContext ;
1111use rustc_span:: { sym, Symbol } ;
1212
@@ -40,6 +40,56 @@ pub fn check_for_loop_iter(
4040 && !clone_or_copy_needed
4141 && let Some ( receiver_snippet) = snippet_opt ( cx, receiver. span )
4242 {
43+ // Issue 12098
44+ // https://github.com/rust-lang/rust-clippy/issues/12098
45+ // if the assignee have `mut borrow` conflict with the iteratee
46+ // the lint should not execute, former didn't consider the mut case
47+
48+ // check whether `expr` is mutable
49+ fn is_mutable ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
50+ if let Some ( hir_id) = path_to_local ( expr)
51+ && let Node :: Pat ( pat) = cx. tcx . hir_node ( hir_id)
52+ {
53+ matches ! ( pat. kind, PatKind :: Binding ( BindingAnnotation :: MUT , ..) )
54+ } else {
55+ true
56+ }
57+ }
58+
59+ fn is_caller_or_fields_changed ( cx : & LateContext < ' _ > , body : & Expr < ' _ > , caller : & Expr < ' _ > ) -> bool {
60+ if let ExprKind :: Block ( block, ..) = body. kind {
61+ for stmt in block. stmts {
62+ if let StmtKind :: Semi ( e) = stmt. kind {
63+ // dont check `is_mutable` for `lhs` here because
64+ // assign to an immutable variable is an error
65+ if let Some ( assignee) = match e. kind {
66+ ExprKind :: Assign ( assignee, _, _) | ExprKind :: AssignOp ( _, assignee, _) => Some ( assignee) ,
67+ _ => None ,
68+ } {
69+ if !can_mut_borrow_both ( cx, caller, assignee) {
70+ return true ;
71+ }
72+ }
73+ }
74+ }
75+ }
76+ false
77+ }
78+
79+ if let ExprKind :: Call ( _, [ child, ..] ) = expr. kind {
80+ // filter first layer of iterator
81+ let mut child = child;
82+ // get inner real caller requests for clone
83+ while let ExprKind :: MethodCall ( _, caller, _, _) = child. kind {
84+ child = caller;
85+ }
86+ if is_mutable ( cx, child) && is_caller_or_fields_changed ( cx, body, child) {
87+ // skip lint
88+ return true ;
89+ }
90+ } ;
91+
92+ // the lint should not be executed if no violation happens
4393 let snippet = if let ExprKind :: MethodCall ( maybe_iter_method_name, collection, [ ] , _) = receiver. kind
4494 && maybe_iter_method_name. ident . name == sym:: iter
4595 && let Some ( iterator_trait_id) = cx. tcx . get_diagnostic_item ( sym:: Iterator )
0 commit comments