Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,10 +1364,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));

let Some(hir::Node::LetStmt(local)) = node else {
err.span_label(
sugg_span,
format!("consider changing this binding's type to be: `{sugg_str}`"),
);
// Don't suggest changing the binding's type for closure parameters,
// as the type is often constrained by the closure's context (e.g., when passed
// to methods like `Option::inspect`).
if !matches!(node, Some(hir::Node::Param(_))) {
err.span_label(
sugg_span,
format!("consider changing this binding's type to be: `{sugg_str}`"),
);
}
return;
};

Expand Down Expand Up @@ -1494,9 +1499,7 @@ impl<'tcx> Visitor<'tcx> for BindingFinder {
}

fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat
&& *span == self.span
{
if param.pat.span == self.span {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Why did you change this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original code only matched parameters with explicit reference patterns (PatKind::Ref, like |&x|). This meant closure parameters with inferred types like |a| in opt.inspect(|a| ...) weren't found, so they still got the bogus suggestion.

I changed it to match all parameter patterns by just checking param.pat.span == self.span, which allows finding both |a| and |&a| style parameters. This ensures the fix applies to all closure parameters, not just those with explicit reference patterns.

Commit: a80a61d

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot It sounds like it would make sense to split that out in a separate PR since it probably affects test output? If yes, please do that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two changes are interdependent - without extending visit_param to match all patterns, the BindingFinder won't find simple closure parameters like |a| in opt.inspect(|a| ...), so the fix for issue rust-lang#128381 wouldn't work.

Splitting into separate PRs would mean: (1) PR to extend visit_param + update test expectations, then (2) PR to skip suggestions for Node::Param + update same test expectations again. Would you prefer this approach, or keep them together since they're both required for the fix?

Copy link
Owner

@Enselic Enselic Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot I prefer splitting to separate PRs.

ControlFlow::Break(param.hir_id)
} else {
ControlFlow::Continue(())
Expand Down
6 changes: 2 additions & 4 deletions tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
error[E0596]: cannot borrow `**layer` as mutable, as it is behind a `&` reference
--> $DIR/issue-115259-suggest-iter-mut.rs:15:65
--> $DIR/issue-115259-suggest-iter-mut.rs:15:74
|
LL | self.layers.iter().fold(0, |result, mut layer| result + layer.process())
| --------- ^^^^^ `layer` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut Box<dyn Layer>`
| ^^^^^ `layer` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you may want to use `iter_mut` here
|
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/borrowck/issue-128381-mutate-through-option-inspect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Issue #128381: Check that we don't suggest changing closure parameter types
// when they are constrained by method signatures like `Option::inspect`.

#[derive(Debug)]
struct A {
a: u32,
}

fn main() {
let mut opt = Some(A { a: 123 });
let ref_mut_opt = opt.as_mut().inspect(|a| {
a.a += 123;
//~^ ERROR cannot assign to `a.a`, which is behind a `&` reference
});
dbg!(ref_mut_opt);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0594]: cannot assign to `a.a`, which is behind a `&` reference
--> $DIR/issue-128381-mutate-through-option-inspect.rs:12:9
|
LL | a.a += 123;
| ^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be written

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0594`.
6 changes: 2 additions & 4 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut-2.rs:30:45
--> $DIR/issue-62387-suggest-iter-mut-2.rs:30:56
|
LL | vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
| --------- ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut Container`
| ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you may want to use `iter_mut` here
|
Expand Down
12 changes: 4 additions & 8 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut.rs:18:27
--> $DIR/issue-62387-suggest-iter-mut.rs:18:31
|
LL | v.iter().for_each(|a| a.double());
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut A`
| ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you may want to use `iter_mut` here
|
LL | v.iter_mut().for_each(|a| a.double());
| ++++

error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut.rs:25:39
--> $DIR/issue-62387-suggest-iter-mut.rs:25:43
|
LL | v.iter().rev().rev().for_each(|a| a.double());
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut A`
| ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you may want to use `iter_mut` here
|
Expand Down
6 changes: 2 additions & 4 deletions tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ LL | <Option<&mut dyn FnMut()> as Clone>::clone(&cb).map(|cb| cb());
| ++++++++++++++++++++++++++++++++++++++++++++ +

error[E0596]: cannot borrow `*cb` as mutable, as it is behind a `&` reference
--> $DIR/suggest-as-ref-on-mut-closure.rs:12:26
--> $DIR/suggest-as-ref-on-mut-closure.rs:12:30
|
LL | cb.as_ref().map(|cb| cb());
| -- ^^ `cb` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut &mut dyn FnMut()`
| ^^ `cb` is a `&` reference, so the data it refers to cannot be borrowed as mutable
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this a valid suggestion? Also check the other tests you updated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggestion "consider changing this binding's type to be: &mut &mut dyn FnMut()" is not actionable because cb is a closure parameter whose type is dictated by the map method signature. The user cannot change |cb| to |cb: &mut &mut dyn FnMut()| because as_ref().map() provides &&mut dyn FnMut(), not &mut &mut dyn FnMut().

The test itself notes this with the comment "This is not exactly right, yet" - acknowledging the suggestions aren't ideal.

Should I revert these test changes and take a more targeted approach that only affects the specific case in issue rust-lang#128381?


error: aborting due to 2 previous errors

Expand Down