From a779e054a91ac3bd8f5f1e8a898a2bb1affe3126 Mon Sep 17 00:00:00 2001 From: lapla Date: Fri, 17 Apr 2026 19:22:10 +0900 Subject: [PATCH] Fix ICE in borrowck mutability suggestion with multi-byte ref sigil --- .../src/diagnostics/mutability_errors.rs | 17 +++++++++--- tests/crashes/139089.rs | 2 -- ...k-assign-to-andmut-in-aliasable-loc.stderr | 2 +- ...orrow-mut-base-ptr-in-aliasable-loc.stderr | 2 +- tests/ui/borrowck/mutability-errors.stderr | 20 ++++++++------ ...tability-suggestion-fullwidth-ampersand.rs | 9 +++++++ ...lity-suggestion-fullwidth-ampersand.stderr | 27 +++++++++++++++++++ 7 files changed, 64 insertions(+), 15 deletions(-) delete mode 100644 tests/crashes/139089.rs create mode 100644 tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs create mode 100644 tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a0e53248c904..1d90b5dbcd10 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -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. diff --git a/tests/crashes/139089.rs b/tests/crashes/139089.rs deleted file mode 100644 index 3326aa6ad984..000000000000 --- a/tests/crashes/139089.rs +++ /dev/null @@ -1,2 +0,0 @@ -//@ known-bug: #139089 -pub fn foo3(x: &Vec) { x.push(0); } diff --git a/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr b/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr index 62d456c5510a..84c689d81c9c 100644 --- a/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr +++ b/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr @@ -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 diff --git a/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr b/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr index 7e516fe89b40..11607159eb01 100644 --- a/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr +++ b/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr @@ -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 diff --git a/tests/ui/borrowck/mutability-errors.stderr b/tests/ui/borrowck/mutability-errors.stderr index 18d8e6eb1a6e..34adf33a99c2 100644 --- a/tests/ui/borrowck/mutability-errors.stderr +++ b/tests/ui/borrowck/mutability-errors.stderr @@ -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 diff --git a/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs new file mode 100644 index 000000000000..a9bdd381307e --- /dev/null +++ b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs @@ -0,0 +1,9 @@ +// Regression test for https://github.com/rust-lang/rust/issues/139089 + +fn foo(x: &Vec) { + //~^ ERROR unknown start of token + x.push(0); + //~^ ERROR cannot borrow `*x` as mutable +} + +fn main() {} diff --git a/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr new file mode 100644 index 000000000000..e6712f5da9a8 --- /dev/null +++ b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr @@ -0,0 +1,27 @@ +error: unknown start of token: \u{ff06} + --> $DIR/mutability-suggestion-fullwidth-ampersand.rs:3:11 + | +LL | fn foo(x: &Vec) { + | ^^ + | +help: Unicode character '&' (Fullwidth Ampersand) looks like '&' (Ampersand), but it is not + | +LL - fn foo(x: &Vec) { +LL + fn foo(x: &Vec) { + | + +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) { +LL + fn foo(x: &mut Vec) { + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`.