Rollup merge of #140094 - Kivooeo:raw-pointer-assignment-suggestion, r=compiler-errors

Improve diagnostics for pointer arithmetic += and -= (fixes #137391)

**Description**:

This PR improves the diagnostic message for cases where a binary assignment operation like `ptr += offset` or `ptr -= offset` is attempted on `*mut T`. These operations are not allowed, and the compiler previously suggested calling `.add()` or `.wrapping_add()`, which is misleading if not assigned.

This PR updates the diagnostics to suggest assigning the result of `.wrapping_add()` or `.wrapping_sub()` back to the pointer, e.g.:

**Examples**

For this code
```rust
let mut arr = [0u8; 10];
let mut ptr = arr.as_mut_ptr();

ptr += 2;
```
it will say:
```rust
10 |     ptr += 2;
   |     ---^^^^^
   |     |
   |     cannot use `+=` on type `*mut u8`
   |
help: consider replacing `ptr += offset` with `ptr = ptr.wrapping_add(offset)` or `ptr.add(offset)`
   |
10 -     ptr += 2;
10 +     ptr = ptr.wrapping_add(2);
```

**Related issue**: #137391
cc `@nabijaczleweli` for context (issue author)
This commit is contained in:
Chris Denton 2025-04-22 01:22:13 +00:00 committed by GitHub
commit 8f42ac0043
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 122 additions and 1 deletions

View file

@ -234,7 +234,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// us do better coercions than we would be able to do otherwise,
// particularly for things like `String + &String`.
let rhs_ty_var = self.next_ty_var(rhs_expr.span);
let result = self.lookup_op_method(
(lhs_expr, lhs_ty),
Some((rhs_expr, rhs_ty_var)),
@ -698,6 +697,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
let lhs_name_str = match lhs_expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => {
path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
}
_ => self
.tcx
.sess
.source_map()
.span_to_snippet(lhs_expr.span)
.unwrap_or("_".to_string()),
};
if op.span().can_be_used_for_suggestions() {
match op {
Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
{
err.multipart_suggestion(
"consider using `add` or `wrapping_add` to do pointer arithmetic",
vec![
(lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
(
lhs_expr.span.between(rhs_expr.span),
".wrapping_add(".to_owned(),
),
(rhs_expr.span.shrink_to_hi(), ")".to_owned()),
],
Applicability::MaybeIncorrect,
);
}
Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => {
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
err.multipart_suggestion(
"consider using `sub` or `wrapping_sub` to do pointer arithmetic",
vec![
(lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
(
lhs_expr.span.between(rhs_expr.span),
".wrapping_sub(".to_owned(),
),
(rhs_expr.span.shrink_to_hi(), ")".to_owned()),
],
Applicability::MaybeIncorrect,
);
}
}
_ => {}
}
}
let reported = err.emit();
Ty::new_error(self.tcx, reported)
}

View file

@ -0,0 +1,20 @@
//@ run-rustfix
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
fn test_add_assign_raw_pointer() {
let mut arr = [0u8; 10];
let mut _ptr = arr.as_mut_ptr();
_ptr = _ptr.wrapping_add(2); //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368]
}
fn test_sub_assign_raw_pointer() {
let mut arr = [0u8; 10];
let mut _ptr = arr.as_mut_ptr();
_ptr = _ptr.wrapping_sub(2); //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368]
}
fn main() {}

View file

@ -0,0 +1,20 @@
//@ run-rustfix
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
fn test_add_assign_raw_pointer() {
let mut arr = [0u8; 10];
let mut _ptr = arr.as_mut_ptr();
_ptr += 2; //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368]
}
fn test_sub_assign_raw_pointer() {
let mut arr = [0u8; 10];
let mut _ptr = arr.as_mut_ptr();
_ptr -= 2; //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368]
}
fn main() {}

View file

@ -0,0 +1,31 @@
error[E0368]: binary assignment operation `+=` cannot be applied to type `*mut u8`
--> $DIR/pointer-arith-assign.rs:10:5
|
LL | _ptr += 2;
| ----^^^^^
| |
| cannot use `+=` on type `*mut u8`
|
help: consider using `add` or `wrapping_add` to do pointer arithmetic
|
LL - _ptr += 2;
LL + _ptr = _ptr.wrapping_add(2);
|
error[E0368]: binary assignment operation `-=` cannot be applied to type `*mut u8`
--> $DIR/pointer-arith-assign.rs:17:5
|
LL | _ptr -= 2;
| ----^^^^^
| |
| cannot use `-=` on type `*mut u8`
|
help: consider using `sub` or `wrapping_sub` to do pointer arithmetic
|
LL - _ptr -= 2;
LL + _ptr = _ptr.wrapping_sub(2);
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0368`.