Skip to content

Commit 23e8ad4

Browse files
committed
fix: rhs_span to rhs_span_new
1 parent c69e1a0 commit 23e8ad4

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1689,13 +1689,22 @@ fn suggest_ampmut<'tcx>(
16891689
// If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
16901690
// to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
16911691
// suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
1692+
//
1693+
// Note: we must use `rhs_span_new` (the span of the *inner* assignment) here, not
1694+
// `rhs_span` (the span of the outer re-borrow). Although current MIR lowering always
1695+
// assigns both statements the same source span, using the correct span is important
1696+
// for correctness: `rhs_str_new` is later paired with `rhs_span_new` (line below),
1697+
// so the snippet text must correspond to the inner span. If MIR lowering ever changes
1698+
// to produce different spans, using the wrong one would cause the `&` offset calculation
1699+
// in the suggestion below to point to the wrong source location.
1700+
// Regression test: `tests/ui/borrowck/suggest-ampmut-usertype-ascription.rs`
16921701
if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
16931702
&& let [user_ty_proj] = user_ty_projs.contents.as_slice()
16941703
&& user_ty_proj.projs.is_empty()
16951704
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
16961705
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
16971706
&& let rhs_span_new = rhs_stmt_new.source_info.span
1698-
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
1707+
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new)
16991708
{
17001709
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
17011710
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Regression test for the `suggest_ampmut` usertype ascription code path in borrowck.
2+
//
3+
// When a binding has a user type annotation like `let x: &T = &expr`, MIR creates
4+
// two statements: an inner borrow `_tmp = &expr` and an outer re-borrow `_x = &(*_tmp)`.
5+
// The `suggest_ampmut` function must pierce through the re-borrow to find the inner
6+
// assignment and use its span/snippet to produce a correct `&mut` suggestion.
7+
//
8+
// Previously there was a copy-paste bug where `span_to_snippet` was called with the
9+
// outer span (`rhs_span`) instead of the inner span (`rhs_span_new`). This is a latent
10+
// bug: current MIR lowering always assigns both statements the same source span, so the
11+
// diagnostic output is identical either way. However, the fix is semantically correct
12+
// and would prevent incorrect suggestions if MIR lowering ever produces different spans.
13+
//
14+
// This test covers the various patterns that exercise the usertype ascription path
15+
// to guard against regressions.
16+
17+
fn main() {
18+
// Basic usertype ascription: `let x: &T = &y`
19+
// Exercises the inner-assignment pierce-through and suggests `&mut` on the expression.
20+
let y = 0i32;
21+
let x: &i32 = &y;
22+
//~^ HELP consider changing this to be a mutable reference
23+
*x = 1;
24+
//~^ ERROR cannot assign to `*x`, which is behind a `&` reference
25+
//~| NOTE `x` is a `&` reference, so it cannot be written to
26+
27+
// Usertype ascription with `&mut` on the RHS: the inner rvalue is a mutable borrow,
28+
// so `suggest_ampmut` returns `ChangeBinding` and the caller suggests changing the type.
29+
let mut v = Vec::new();
30+
let w: &Vec<i32> = &mut v;
31+
//~^ HELP consider changing this binding's type
32+
w.push(1);
33+
//~^ ERROR cannot borrow `*w` as mutable, as it is behind a `&` reference
34+
//~| NOTE `w` is a `&` reference, so it cannot be borrowed as mutable
35+
36+
// Usertype ascription with parenthesized expression.
37+
let a = 0i32;
38+
let b: &i32 = &(a);
39+
//~^ HELP consider changing this to be a mutable reference
40+
*b = 2;
41+
//~^ ERROR cannot assign to `*b`, which is behind a `&` reference
42+
//~| NOTE `b` is a `&` reference, so it cannot be written to
43+
44+
// Usertype ascription inside a block expression.
45+
let c = 0i32;
46+
let d: &i32 = { &c };
47+
//~^ HELP consider changing this to be a mutable reference
48+
*d = 3;
49+
//~^ ERROR cannot assign to `*d`, which is behind a `&` reference
50+
//~| NOTE `d` is a `&` reference, so it cannot be written to
51+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
error[E0594]: cannot assign to `*x`, which is behind a `&` reference
2+
--> $DIR/suggest-ampmut-usertype-ascription.rs:23:5
3+
|
4+
LL | *x = 1;
5+
| ^^^^^^ `x` is a `&` reference, so it cannot be written to
6+
|
7+
help: consider changing this to be a mutable reference
8+
|
9+
LL | let x: &i32 = &mut y;
10+
| +++
11+
12+
error[E0596]: cannot borrow `*w` as mutable, as it is behind a `&` reference
13+
--> $DIR/suggest-ampmut-usertype-ascription.rs:32:5
14+
|
15+
LL | w.push(1);
16+
| ^ `w` is a `&` reference, so it cannot be borrowed as mutable
17+
|
18+
help: consider changing this binding's type
19+
|
20+
LL | let w: &mut Vec<i32> = &mut v;
21+
| +++
22+
23+
error[E0594]: cannot assign to `*b`, which is behind a `&` reference
24+
--> $DIR/suggest-ampmut-usertype-ascription.rs:40:5
25+
|
26+
LL | *b = 2;
27+
| ^^^^^^ `b` is a `&` reference, so it cannot be written to
28+
|
29+
help: consider changing this to be a mutable reference
30+
|
31+
LL | let b: &i32 = &mut (a);
32+
| +++
33+
34+
error[E0594]: cannot assign to `*d`, which is behind a `&` reference
35+
--> $DIR/suggest-ampmut-usertype-ascription.rs:48:5
36+
|
37+
LL | *d = 3;
38+
| ^^^^^^ `d` is a `&` reference, so it cannot be written to
39+
|
40+
help: consider changing this to be a mutable reference
41+
|
42+
LL | let d: &i32 = { &mut c };
43+
| +++
44+
45+
error: aborting due to 4 previous errors
46+
47+
Some errors have detailed explanations: E0594, E0596.
48+
For more information about an error, try `rustc --explain E0594`.

0 commit comments

Comments
 (0)