@@ -5,6 +5,7 @@ use clippy_utils::sugg::{DiagExt as _, Sugg};
55use clippy_utils:: ty:: { is_copy, same_type_modulo_regions} ;
66use clippy_utils:: { get_parent_expr, is_ty_alias, sym} ;
77use rustc_errors:: Applicability ;
8+ use rustc_hir:: def:: { DefKind , Res } ;
89use rustc_hir:: def_id:: DefId ;
910use rustc_hir:: { BindingMode , Expr , ExprKind , HirId , MatchSource , Mutability , Node , PatKind } ;
1011use rustc_infer:: infer:: TyCtxtInferExt ;
@@ -329,10 +330,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
329330 // implements Copy, in which case .into_iter() returns a copy of the receiver and
330331 // cannot be safely omitted.
331332 if same_type_modulo_regions ( a, b) && !is_copy ( cx, b) {
332- // Below we check if the parent method call meets the following conditions:
333- // 1. First parameter is `&mut self` (requires mutable reference)
334- // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any)
335- // For methods satisfying these conditions (like any), .into_iter() must be preserved.
333+ // Suppress the lint when the direct receiver of `.into_iter()` is a `const`
334+ // (or `AssocConst`) item AND the parent method takes `&mut self` + `FnMut`.
335+ //
336+ // Background: removing `.into_iter()` from `CONST.method(|x| ...)` where
337+ // `method` takes `&mut self` causes the compiler to take a mutable reference
338+ // to a *temporary copy* of the const, triggering a `const_item_mutation`
339+ // warning. For any other receiver expression this is safe to remove.
340+ //
341+ // See <https://github.com/rust-lang/rust-clippy/issues/14656> for the
342+ // original false positive and <https://github.com/rust-lang/rust-clippy/issues/16794>
343+ // for the regression caused by applying this guard too broadly.
336344 if let Some ( parent) = get_parent_expr ( cx, e)
337345 && let ExprKind :: MethodCall ( _, recv, _, _) = parent. kind
338346 && recv. hir_id == e. hir_id
@@ -352,6 +360,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
352360 false
353361 }
354362 } )
363+ // Only suppress when the `.into_iter()` receiver is a const item.
364+ // For any other expression the removal is safe.
365+ && is_const_item ( cx, into_iter_recv)
355366 {
356367 return ;
357368 }
@@ -480,3 +491,20 @@ fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String {
480491 }
481492 prefix
482493}
494+
495+ /// Returns `true` when `expr` is a direct reference to a `const` or `AssocConst` item.
496+ ///
497+ /// This is used to guard the `.into_iter()` suppression: only const items cause
498+ /// `const_item_mutation` warnings when a `&mut self` method is called directly on
499+ /// them (the compiler takes a mutable reference to a *temporary copy*). For any other
500+ /// expression it is safe to remove the `.into_iter()` call.
501+ fn is_const_item ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
502+ if let ExprKind :: Path ( qpath) = & expr. kind {
503+ matches ! (
504+ cx. qpath_res( qpath, expr. hir_id) ,
505+ Res :: Def ( DefKind :: Const { .. } | DefKind :: AssocConst { .. } , _)
506+ )
507+ } else {
508+ false
509+ }
510+ }
0 commit comments