From 6ef6803b9835982a89d172ee4f8255acd0027c19 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 19 Nov 2025 20:47:50 +0100 Subject: [PATCH 1/2] tests: Check stderr when trying to mutate inside Option::inspect() --- tests/ui/borrowck/option-inspect-mutation.rs | 13 +++++++++++++ tests/ui/borrowck/option-inspect-mutation.stderr | 11 +++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/ui/borrowck/option-inspect-mutation.rs create mode 100644 tests/ui/borrowck/option-inspect-mutation.stderr diff --git a/tests/ui/borrowck/option-inspect-mutation.rs b/tests/ui/borrowck/option-inspect-mutation.rs new file mode 100644 index 0000000000000..124f21553b666 --- /dev/null +++ b/tests/ui/borrowck/option-inspect-mutation.rs @@ -0,0 +1,13 @@ +//! Regression test for . + +struct Struct { + field: u32, +} + +fn main() { + let mut some_struct = Some(Struct { field: 42 }); + some_struct.as_mut().inspect(|some_struct| { + some_struct.field *= 10; //~ ERROR cannot assign to `some_struct.field`, which is behind a `&` reference + // Users can't change type of `some_struct` param, so above error must not suggest it. + }); +} diff --git a/tests/ui/borrowck/option-inspect-mutation.stderr b/tests/ui/borrowck/option-inspect-mutation.stderr new file mode 100644 index 0000000000000..bfa9b9371a736 --- /dev/null +++ b/tests/ui/borrowck/option-inspect-mutation.stderr @@ -0,0 +1,11 @@ +error[E0594]: cannot assign to `some_struct.field`, which is behind a `&` reference + --> $DIR/option-inspect-mutation.rs:10:9 + | +LL | some_struct.as_mut().inspect(|some_struct| { + | ----------- consider changing this binding's type to be: `&mut &mut Struct` +LL | some_struct.field *= 10; + | ^^^^^^^^^^^^^^^^^^^^^^^ `some_struct` is a `&` reference, so it cannot be written to + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0594`. From b49ff49347b6349b0ec23fad7d7a61e6428517d6 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 19 Nov 2025 20:59:22 +0100 Subject: [PATCH 2/2] rustc_borrowck: Don't suggest changing closure param type not under user control This changes output of a handful of tests more than the one added in the parent commit, but as far as I can tell, all removed suggestions were invalid. --- .../src/diagnostics/mutability_errors.rs | 20 +++++++++++++++++++ .../issue-115259-suggest-iter-mut.stderr | 4 +--- .../issue-62387-suggest-iter-mut-2.stderr | 4 +--- .../issue-62387-suggest-iter-mut.stderr | 8 ++------ .../borrowck/option-inspect-mutation.stderr | 2 -- .../suggest-as-ref-on-mut-closure.stderr | 4 +--- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index c06e3c8b3c174..93677bc94cb89 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1198,6 +1198,26 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); return; } + + // If `local` is a closure arg, and the type of the arg is not under + // local control, do not suggest to change its type. + if self.body.local_kind(local) == LocalKind::Arg + && let InstanceKind::Item(def_id) = self.body.source.instance + && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = + self.infcx.tcx.hir_get_if_local(def_id) + && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind + && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id) + && let ExprKind::MethodCall(path_segment, _, _, _) = expr.kind + && self + .infcx + .tcx + .typeck(path_segment.hir_id.owner.def_id) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| !def_id.is_local()) + { + return; + } + let decl_span = local_decl.source_info.span; let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() { diff --git a/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr index 8191f1f4394b7..60ff010193fd2 100644 --- a/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr +++ b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr @@ -2,9 +2,7 @@ error[E0596]: cannot borrow `**layer` as mutable, as it is behind a `&` referenc --> $DIR/issue-115259-suggest-iter-mut.rs:15:65 | LL | self.layers.iter().fold(0, |result, mut layer| result + layer.process()) - | --------- ^^^^^ `layer` is a `&` reference, so it cannot be borrowed as mutable - | | - | consider changing this binding's type to be: `&mut Box` + | ^^^^^ `layer` is a `&` reference, so it cannot be borrowed as mutable | help: you may want to use `iter_mut` here | diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr index 29121b85f3a08..ba765f06a2c8f 100644 --- a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr +++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr @@ -2,9 +2,7 @@ error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` refer --> $DIR/issue-62387-suggest-iter-mut-2.rs:30:45 | LL | vec.iter().flat_map(|container| container.things()).cloned().collect::>(); - | --------- ^^^^^^^^^ `container` is a `&` reference, so it cannot be borrowed as mutable - | | - | consider changing this binding's type to be: `&mut Container` + | ^^^^^^^^^ `container` is a `&` reference, so it cannot be borrowed as mutable | help: you may want to use `iter_mut` here | diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr index 55c17ab6cdea1..677d71d85580d 100644 --- a/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr +++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr @@ -2,9 +2,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/issue-62387-suggest-iter-mut.rs:18:27 | LL | v.iter().for_each(|a| a.double()); - | - ^ `a` is a `&` reference, so it cannot be borrowed as mutable - | | - | consider changing this binding's type to be: `&mut A` + | ^ `a` is a `&` reference, so it cannot be borrowed as mutable | help: you may want to use `iter_mut` here | @@ -15,9 +13,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/issue-62387-suggest-iter-mut.rs:25:39 | LL | v.iter().rev().rev().for_each(|a| a.double()); - | - ^ `a` is a `&` reference, so it cannot be borrowed as mutable - | | - | consider changing this binding's type to be: `&mut A` + | ^ `a` is a `&` reference, so it cannot be borrowed as mutable | help: you may want to use `iter_mut` here | diff --git a/tests/ui/borrowck/option-inspect-mutation.stderr b/tests/ui/borrowck/option-inspect-mutation.stderr index bfa9b9371a736..d947827b238e0 100644 --- a/tests/ui/borrowck/option-inspect-mutation.stderr +++ b/tests/ui/borrowck/option-inspect-mutation.stderr @@ -1,8 +1,6 @@ error[E0594]: cannot assign to `some_struct.field`, which is behind a `&` reference --> $DIR/option-inspect-mutation.rs:10:9 | -LL | some_struct.as_mut().inspect(|some_struct| { - | ----------- consider changing this binding's type to be: `&mut &mut Struct` LL | some_struct.field *= 10; | ^^^^^^^^^^^^^^^^^^^^^^^ `some_struct` is a `&` reference, so it cannot be written to diff --git a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr index ca0d0bd8a3caa..9b4061af967ed 100644 --- a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr +++ b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr @@ -18,9 +18,7 @@ error[E0596]: cannot borrow `*cb` as mutable, as it is behind a `&` reference --> $DIR/suggest-as-ref-on-mut-closure.rs:12:26 | LL | cb.as_ref().map(|cb| cb()); - | -- ^^ `cb` is a `&` reference, so it cannot be borrowed as mutable - | | - | consider changing this binding's type to be: `&mut &mut dyn FnMut()` + | ^^ `cb` is a `&` reference, so it cannot be borrowed as mutable error: aborting due to 2 previous errors