Skip to content

Commit 77e6a31

Browse files
committed
Fix suggestions that cause mutable borrow overlaps
1 parent 72e8f2a commit 77e6a31

7 files changed

+43
-43
lines changed

clippy_lints/src/methods/unnecessary_collection_clone.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_path_mutable;
23
use clippy_utils::source::snippet_with_applicability;
34
use clippy_utils::ty::{deref_chain, get_inherent_method, implements_trait, make_normalized_projection};
45
use rustc_errors::Applicability;
@@ -30,6 +31,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) {
3031

3132
// If the call before `into_iter` is `.clone()`
3233
if let Some(("clone", collection_expr, [], _, _)) = method_call(recv)
34+
// and the binding being cloned is not mutable
35+
&& !is_path_mutable(cx, collection_expr).unwrap_or(false)
3336
// and the result of `into_iter` is an Iterator
3437
&& let Some(&iterator_def_id) = diagnostic_items.name_to_id.get(&sym::Iterator)
3538
&& let expr_ty = typeck_results.expr_ty(expr)

clippy_lints/src/methods/unnecessary_iter_cloned.rs

+8-18
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use clippy_utils::higher::ForLoop;
44
use clippy_utils::source::SpanRangeExt;
55
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
66
use clippy_utils::visitors::for_each_expr_without_closures;
7-
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
7+
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, is_path_mutable};
88
use core::ops::ControlFlow;
99
use rustc_errors::Applicability;
1010
use rustc_hir::def_id::DefId;
11-
use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
11+
use rustc_hir::{Expr, ExprKind};
1212
use rustc_lint::LateContext;
1313
use rustc_span::{Symbol, sym};
1414

@@ -42,22 +42,7 @@ pub fn check_for_loop_iter(
4242
&& !clone_or_copy_needed
4343
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
4444
{
45-
// Issue 12098
46-
// https://github.com/rust-lang/rust-clippy/issues/12098
47-
// if the assignee have `mut borrow` conflict with the iteratee
48-
// the lint should not execute, former didn't consider the mut case
49-
5045
// check whether `expr` is mutable
51-
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
52-
if let Some(hir_id) = path_to_local(expr)
53-
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
54-
{
55-
matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
56-
} else {
57-
true
58-
}
59-
}
60-
6146
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
6247
let mut change = false;
6348
if let ExprKind::Block(block, ..) = body.kind {
@@ -82,7 +67,12 @@ pub fn check_for_loop_iter(
8267
while let ExprKind::MethodCall(_, caller, _, _) = child.kind {
8368
child = caller;
8469
}
85-
if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) {
70+
71+
// Issue 12098
72+
// https://github.com/rust-lang/rust-clippy/issues/12098
73+
// if the assignee have `mut borrow` conflict with the iteratee
74+
// the lint should not execute, former didn't consider the mut case
75+
if is_path_mutable(cx, child).unwrap_or(true) && is_caller_or_fields_change(cx, body, child) {
8676
// skip lint
8777
return true;
8878
}

clippy_lints/src/unnecessary_struct_initialization.rs

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::source::snippet;
33
use clippy_utils::ty::is_copy;
4-
use clippy_utils::{get_parent_expr, path_to_local};
5-
use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp};
4+
use clippy_utils::{get_parent_expr, is_path_mutable, path_to_local};
5+
use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, UnOp};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::declare_lint_pass;
88

@@ -155,16 +155,6 @@ fn same_path_in_all_fields<'tcx>(
155155
}
156156
}
157157

158-
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
159-
if let Some(hir_id) = path_to_local(expr)
160-
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
161-
{
162-
matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
163-
} else {
164-
true
165-
}
166-
}
167-
168158
fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
169159
if let Some(parent) = get_parent_expr(cx, expr_a)
170160
&& let parent_ty = cx.typeck_results().expr_ty_adjusted(parent)
@@ -176,7 +166,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>)
176166
return false;
177167
}
178168

179-
if parent_ty.is_mutable_ptr() && !is_mutable(cx, expr_b) {
169+
if parent_ty.is_mutable_ptr() && !is_path_mutable(cx, expr_b).unwrap_or(true) {
180170
// The original can be used in a mutable reference context only if it is mutable.
181171
return false;
182172
}

clippy_utils/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,17 @@ pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -
412412
}
413413
}
414414

415+
/// Checks if `expr` is a path to a mutable binding.
416+
pub fn is_path_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<bool> {
417+
if let Some(hir_id) = path_to_local(expr)
418+
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
419+
{
420+
Some(matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)))
421+
} else {
422+
None
423+
}
424+
}
425+
415426
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
416427
match *path {
417428
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),

tests/ui/unnecessary_collection_clone.fixed

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@ fn generic<T: Clone>(val: Vec<T>) -> Vec<T> {
1919
//~^ error: using clone on collection to own iterated items
2020
}
2121

22-
fn used_mutably<T: Clone>(mut vals: Vec<T>) {
23-
fn use_mutable<T>(_: &mut T) {}
22+
fn use_mutable<T>(_: &mut T) {}
2423

25-
for val in vals.iter().cloned() {
24+
// Should not lint, as the replacement causes the mutable borrow to overlap
25+
fn used_mutably<T: Clone>(mut vals: Vec<T>) {
26+
for val in vals.clone().into_iter() {
2627
use_mutable(&mut vals)
2728
}
2829
}
2930

31+
// Should not lint, as the replacement causes the mutable borrow to overlap
32+
fn used_mutably_chain<T: Clone>(mut vals: Vec<T>) {
33+
vals.clone().into_iter().for_each(|_| use_mutable(&mut vals));
34+
}
35+
3036
// Should not lint, as `Src` has no `iter` method to use.
3137
fn too_generic<Src, Dst, T: Clone>(val: Src) -> Dst
3238
where

tests/ui/unnecessary_collection_clone.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@ fn generic<T: Clone>(val: Vec<T>) -> Vec<T> {
1919
//~^ error: using clone on collection to own iterated items
2020
}
2121

22-
fn used_mutably<T: Clone>(mut vals: Vec<T>) {
23-
fn use_mutable<T>(_: &mut T) {}
22+
fn use_mutable<T>(_: &mut T) {}
2423

24+
// Should not lint, as the replacement causes the mutable borrow to overlap
25+
fn used_mutably<T: Clone>(mut vals: Vec<T>) {
2526
for val in vals.clone().into_iter() {
2627
use_mutable(&mut vals)
2728
}
2829
}
2930

31+
// Should not lint, as the replacement causes the mutable borrow to overlap
32+
fn used_mutably_chain<T: Clone>(mut vals: Vec<T>) {
33+
vals.clone().into_iter().for_each(|_| use_mutable(&mut vals));
34+
}
35+
3036
// Should not lint, as `Src` has no `iter` method to use.
3137
fn too_generic<Src, Dst, T: Clone>(val: Src) -> Dst
3238
where

tests/ui/unnecessary_collection_clone.stderr

+1-7
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,5 @@ error: using clone on collection to own iterated items
1919
LL | val.clone().into_iter().collect()
2020
| ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `val.iter().cloned()`
2121

22-
error: using clone on collection to own iterated items
23-
--> tests/ui/unnecessary_collection_clone.rs:25:16
24-
|
25-
LL | for val in vals.clone().into_iter() {
26-
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `vals.iter().cloned()`
27-
28-
error: aborting due to 4 previous errors
22+
error: aborting due to 3 previous errors
2923

0 commit comments

Comments
 (0)