Rollup merge of #155428 - lapla-cogito:issue_139089, r=mejrs

Fix ICE in borrowck mutability suggestion with multi-byte ref sigil

Fixes rust-lang/rust#139089

Similarly to rust-lang/rust#155068, this is another instance where span arithmetic did not account for multi-byte characters. (Note that the ampersand in the test is full-width)

This change also results in correcting some inappropriate suggestions.
This commit is contained in:
Jacob Pratt
2026-04-18 00:05:19 -04:00
committed by GitHub
7 changed files with 64 additions and 15 deletions
@@ -1410,9 +1410,20 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol)
(span, " mut".to_owned(), true)
// If there is already a binding, we modify it to be `mut`.
} else if binding_exists {
// Shrink the span to just after the `&` in `&variable`.
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
(span, "mut ".to_owned(), true)
// Replace the sigil with the mutable version. We may be dealing
// with parser recovery here and cannot assume the user actually
// typed `&` or `*const`, so we compute the prefix from the snippet.
let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) else {
return;
};
let (prefix_len, replacement) = if local_decl.ty.is_ref() {
(src.chars().next().map_or(0, char::len_utf8), "&mut ")
} else {
(src.find("const").map_or(1, |i| i + "const".len()), "*mut ")
};
let ws_len = src[prefix_len..].len() - src[prefix_len..].trim_start().len();
let span = span.with_hi(span.lo() + BytePos((prefix_len + ws_len) as u32));
(span, replacement.to_owned(), true)
} else {
// Otherwise, suggest that the user annotates the binding; We provide the
// type of the local.
-2
View File
@@ -1,2 +0,0 @@
//@ known-bug: #139089
pub fn foo3(x: Vec<u8>) { x.push(0); }
@@ -17,7 +17,7 @@ LL | *s.pointer += 1;
|
help: consider changing this to be a mutable reference
|
LL | fn c(s: &mut &mut S) {
LL | fn c(s: &mut &mut S) {
| +++
error: aborting due to 2 previous errors
@@ -27,7 +27,7 @@ LL | let x: &mut isize = &mut **t0;
|
help: consider changing this to be a mutable reference
|
LL | fn foo4(t0: &mut &mut isize) {
LL | fn foo4(t0: &mut &mut isize) {
| +++
error: aborting due to 3 previous errors
+12 -8
View File
@@ -74,8 +74,9 @@ LL | *x = (1,);
|
help: consider changing this to be a mutable pointer
|
LL | unsafe fn named_ptr(x: *mut const (i32,)) {
| +++
LL - unsafe fn named_ptr(x: *const (i32,)) {
LL + unsafe fn named_ptr(x: *mut (i32,)) {
|
error[E0594]: cannot assign to `x.0`, which is behind a `*const` pointer
--> $DIR/mutability-errors.rs:24:5
@@ -85,8 +86,9 @@ LL | (*x).0 = 1;
|
help: consider changing this to be a mutable pointer
|
LL | unsafe fn named_ptr(x: *mut const (i32,)) {
| +++
LL - unsafe fn named_ptr(x: *const (i32,)) {
LL + unsafe fn named_ptr(x: *mut (i32,)) {
|
error[E0596]: cannot borrow `*x` as mutable, as it is behind a `*const` pointer
--> $DIR/mutability-errors.rs:25:5
@@ -96,8 +98,9 @@ LL | &mut *x;
|
help: consider changing this to be a mutable pointer
|
LL | unsafe fn named_ptr(x: *mut const (i32,)) {
| +++
LL - unsafe fn named_ptr(x: *const (i32,)) {
LL + unsafe fn named_ptr(x: *mut (i32,)) {
|
error[E0596]: cannot borrow `x.0` as mutable, as it is behind a `*const` pointer
--> $DIR/mutability-errors.rs:26:5
@@ -107,8 +110,9 @@ LL | &mut (*x).0;
|
help: consider changing this to be a mutable pointer
|
LL | unsafe fn named_ptr(x: *mut const (i32,)) {
| +++
LL - unsafe fn named_ptr(x: *const (i32,)) {
LL + unsafe fn named_ptr(x: *mut (i32,)) {
|
error[E0594]: cannot assign to data in a `*const` pointer
--> $DIR/mutability-errors.rs:30:5
@@ -0,0 +1,9 @@
// Regression test for https://github.com/rust-lang/rust/issues/139089
fn foo(x: Vec<u8>) {
//~^ ERROR unknown start of token
x.push(0);
//~^ ERROR cannot borrow `*x` as mutable
}
fn main() {}
@@ -0,0 +1,27 @@
error: unknown start of token: \u{ff06}
--> $DIR/mutability-suggestion-fullwidth-ampersand.rs:3:11
|
LL | fn foo(x: Vec<u8>) {
| ^^
|
help: Unicode character '' (Fullwidth Ampersand) looks like '&' (Ampersand), but it is not
|
LL - fn foo(x: Vec<u8>) {
LL + fn foo(x: &Vec<u8>) {
|
error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
--> $DIR/mutability-suggestion-fullwidth-ampersand.rs:5:5
|
LL | x.push(0);
| ^ `x` is a `&` reference, so it cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
LL - fn foo(x: Vec<u8>) {
LL + fn foo(x: &mut Vec<u8>) {
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0596`.