Auto merge of #123007 - kadiwa4:suggest_convert_ptr_to_mut_ref, r=estebank

Rework ptr-to-ref conversion suggestion for method calls

If we have a value `z` of type `*const u8` and try to call `z.to_string()`, the upstream compiler will show you a note suggesting to call `<*const u8>::as_ref` first.

This PR extends that:
- The note will only be shown when the method would exist on the corresponding reference type
- It can now suggest any of `<*const u8>::as_ref`, `<*mut u8>::as_ref` and `<*mut u8>::as_mut`, depending on what the method needs.

I didn't introduce a `help` message because that's not a good idea with `unsafe` functions (and you'd also need to unwrap the `Option<&_>` somehow).
People should check the safety requirements.

For the simplest case
```rust
fn main() {
    let x = 8u8;
    let z: *const u8 = &x;
    // issue #21596
    println!("{}", z.to_string()); //~ ERROR E0599
}
```
the output changes like this:
```diff
 error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
   --> $DIR/suggest-convert-ptr-to-ref.rs:5:22
    |
 LL |     println!("{}", z.to_string());
    |                      ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
    |
-   = note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
-   = note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior
+note: the method `to_string` exists on the type `&u8`
+  --> $SRC_DIR/alloc/src/string.rs:LL:COL
+   = note: try using the unsafe method `<*const T>::as_ref` to get an optional reference to the value behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
    = note: the following trait bounds were not satisfied:
            `*const u8: std::fmt::Display`
            which is required by `*const u8: ToString`
```

I removed the separate note about the safety requirements because it was incomplete and the linked doc page already has the information you need.

Fixes #83695, but that's more of a side effect. The upstream compiler already suggests the right method name here.
This commit is contained in:
bors 2024-04-11 04:41:39 +00:00
commit f13f37fd7b
11 changed files with 137 additions and 55 deletions

View File

@ -51,6 +51,14 @@ pub fn ref_prefix_str(self) -> &'static str {
}
}
/// Returns `"const"` or `"mut"` depending on the mutability.
pub fn ptr_str(self) -> &'static str {
match self {
Mutability::Not => "const",
Mutability::Mut => "mut",
}
}
/// Returns `""` (empty string) or `"mutably "` depending on the mutability.
pub fn mutably_str(self) -> &'static str {
match self {

View File

@ -529,16 +529,44 @@ pub fn report_no_match_method_error(
Applicability::MachineApplicable,
);
}
if let ty::RawPtr(_, _) = &rcvr_ty.kind() {
err.note(
"try using `<*const T>::as_ref()` to get a reference to the \
type behind the pointer: https://doc.rust-lang.org/std/\
primitive.pointer.html#method.as_ref",
);
err.note(
"using `<*const T>::as_ref()` on a pointer which is unaligned or points \
to invalid or uninitialized memory is undefined behavior",
// on pointers, check if the method would exist on a reference
if let SelfSource::MethodCall(rcvr_expr) = source
&& let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind()
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
item_name,
Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl),
self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)),
ProbeScope::TraitsInScope,
None,
)
&& let ty::Ref(_, _, sugg_mutbl) = *pick.self_ty.kind()
&& (sugg_mutbl.is_not() || ptr_mutbl.is_mut())
{
let (method, method_anchor) = match sugg_mutbl {
Mutability::Not => {
let method_anchor = match ptr_mutbl {
Mutability::Not => "as_ref",
Mutability::Mut => "as_ref-1",
};
("as_ref", method_anchor)
}
Mutability::Mut => ("as_mut", "as_mut"),
};
err.span_note(
tcx.def_span(pick.item.def_id),
format!("the method `{item_name}` exists on the type `{ty}`", ty = pick.self_ty),
);
let mut_str = ptr_mutbl.ptr_str();
err.note(format!(
"you might want to use the unsafe method `<*{mut_str} T>::{method}` to get \
an optional reference to the value behind the pointer"
));
err.note(format!(
"read the documentation for `<*{mut_str} T>::{method}` and ensure you satisfy its \
safety preconditions before calling it to avoid undefined behavior: \
https://doc.rust-lang.org/std/primitive.pointer.html#method.{method_anchor}"
));
}
let mut ty_span = match rcvr_ty.kind() {

View File

@ -978,12 +978,7 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
AddressOf(mutability, ref place) => {
let kind_str = match mutability {
Mutability::Mut => "mut",
Mutability::Not => "const",
};
write!(fmt, "&raw {kind_str} {place:?}")
write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())
}
Aggregate(ref kind, ref places) => {

View File

@ -671,13 +671,7 @@ fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
p!("(", print(ty), ") is ", write("{pat:?}"))
}
ty::RawPtr(ty, mutbl) => {
p!(write(
"*{} ",
match mutbl {
hir::Mutability::Mut => "mut",
hir::Mutability::Not => "const",
}
));
p!(write("*{} ", mutbl.ptr_str()));
p!(print(ty))
}
ty::Ref(r, ty, mutbl) => {

View File

@ -373,17 +373,8 @@ fn fmt<Infcx: InferCtxtLike<Interner = I>>(
Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)),
Slice(t) => write!(f, "[{:?}]", &this.wrap(t)),
RawPtr(ty, mutbl) => {
match mutbl {
Mutability::Mut => write!(f, "*mut "),
Mutability::Not => write!(f, "*const "),
}?;
write!(f, "{:?}", &this.wrap(ty))
}
Ref(r, t, m) => match m {
Mutability::Mut => write!(f, "&{:?} mut {:?}", &this.wrap(r), &this.wrap(t)),
Mutability::Not => write!(f, "&{:?} {:?}", &this.wrap(r), &this.wrap(t)),
},
RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), this.wrap(ty)),
Ref(r, t, m) => write!(f, "&{:?} {}{:?}", this.wrap(r), m.prefix_str(), this.wrap(t)),
FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(),
FnPtr(s) => write!(f, "{:?}", &this.wrap(s)),
Dynamic(p, r, repr) => match repr {

View File

@ -1791,7 +1791,6 @@ ui/issues/issue-2150.rs
ui/issues/issue-2151.rs
ui/issues/issue-21546.rs
ui/issues/issue-21554.rs
ui/issues/issue-21596.rs
ui/issues/issue-21600.rs
ui/issues/issue-21622.rs
ui/issues/issue-21634.rs

View File

@ -17,7 +17,7 @@
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1722;
const ISSUES_ENTRY_LIMIT: usize = 1720;
const ROOT_ENTRY_LIMIT: usize = 859;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[

View File

@ -1,5 +0,0 @@
fn main() {
let x = 8u8;
let z: *const u8 = &x;
println!("{}", z.to_string()); //~ ERROR E0599
}

View File

@ -1,15 +0,0 @@
error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
--> $DIR/issue-21596.rs:4:22
|
LL | println!("{}", z.to_string());
| ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
|
= note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
= note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior
= note: the following trait bounds were not satisfied:
`*const u8: std::fmt::Display`
which is required by `*const u8: ToString`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0599`.

View File

@ -0,0 +1,17 @@
fn main() {
let mut x = 8u8;
let z: *const u8 = &x;
// issue #21596
println!("{}", z.to_string()); //~ ERROR E0599
let t: *mut u8 = &mut x;
println!("{}", t.to_string()); //~ ERROR E0599
t.make_ascii_lowercase(); //~ ERROR E0599
// suggest `as_mut` simply because the name is similar
let _ = t.as_mut_ref(); //~ ERROR E0599
let _ = t.as_ref_mut(); //~ ERROR E0599
// no ptr-to-ref suggestion
z.make_ascii_lowercase(); //~ ERROR E0599
}

View File

@ -0,0 +1,70 @@
error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
--> $DIR/suggest-convert-ptr-to-ref.rs:5:22
|
LL | println!("{}", z.to_string());
| ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
|
note: the method `to_string` exists on the type `&u8`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
= note: you might want to use the unsafe method `<*const T>::as_ref` to get an optional reference to the value behind the pointer
= note: read the documentation for `<*const T>::as_ref` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
= note: the following trait bounds were not satisfied:
`*const u8: std::fmt::Display`
which is required by `*const u8: ToString`
error[E0599]: `*mut u8` doesn't implement `std::fmt::Display`
--> $DIR/suggest-convert-ptr-to-ref.rs:8:22
|
LL | println!("{}", t.to_string());
| ^^^^^^^^^ `*mut u8` cannot be formatted with the default formatter
|
note: the method `to_string` exists on the type `&&mut u8`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
= note: you might want to use the unsafe method `<*mut T>::as_ref` to get an optional reference to the value behind the pointer
= note: read the documentation for `<*mut T>::as_ref` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref-1
= note: the following trait bounds were not satisfied:
`*mut u8: std::fmt::Display`
which is required by `*mut u8: ToString`
error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*mut u8` in the current scope
--> $DIR/suggest-convert-ptr-to-ref.rs:9:7
|
LL | t.make_ascii_lowercase();
| ^^^^^^^^^^^^^^^^^^^^ method not found in `*mut u8`
|
note: the method `make_ascii_lowercase` exists on the type `&mut u8`
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
= note: you might want to use the unsafe method `<*mut T>::as_mut` to get an optional reference to the value behind the pointer
= note: read the documentation for `<*mut T>::as_mut` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_mut
error[E0599]: no method named `as_mut_ref` found for raw pointer `*mut u8` in the current scope
--> $DIR/suggest-convert-ptr-to-ref.rs:12:15
|
LL | let _ = t.as_mut_ref();
| ^^^^^^^^^^
|
help: there is a method `as_mut` with a similar name
|
LL | let _ = t.as_mut();
| ~~~~~~
error[E0599]: no method named `as_ref_mut` found for raw pointer `*mut u8` in the current scope
--> $DIR/suggest-convert-ptr-to-ref.rs:13:15
|
LL | let _ = t.as_ref_mut();
| ^^^^^^^^^^
|
help: there is a method `as_mut` with a similar name
|
LL | let _ = t.as_mut();
| ~~~~~~
error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*const u8` in the current scope
--> $DIR/suggest-convert-ptr-to-ref.rs:16:7
|
LL | z.make_ascii_lowercase();
| ^^^^^^^^^^^^^^^^^^^^ method not found in `*const u8`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0599`.