Skip to content

Commit f13f37f

Browse files
committed
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.
2 parents 0827378 + 3a2a3ae commit f13f37f

File tree

11 files changed

+137
-55
lines changed

11 files changed

+137
-55
lines changed

compiler/rustc_ast_ir/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ impl Mutability {
5151
}
5252
}
5353

54+
/// Returns `"const"` or `"mut"` depending on the mutability.
55+
pub fn ptr_str(self) -> &'static str {
56+
match self {
57+
Mutability::Not => "const",
58+
Mutability::Mut => "mut",
59+
}
60+
}
61+
5462
/// Returns `""` (empty string) or `"mutably "` depending on the mutability.
5563
pub fn mutably_str(self) -> &'static str {
5664
match self {

compiler/rustc_hir_typeck/src/method/suggest.rs

+37-9
Original file line numberDiff line numberDiff line change
@@ -529,16 +529,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
529529
Applicability::MachineApplicable,
530530
);
531531
}
532-
if let ty::RawPtr(_, _) = &rcvr_ty.kind() {
533-
err.note(
534-
"try using `<*const T>::as_ref()` to get a reference to the \
535-
type behind the pointer: https://doc.rust-lang.org/std/\
536-
primitive.pointer.html#method.as_ref",
537-
);
538-
err.note(
539-
"using `<*const T>::as_ref()` on a pointer which is unaligned or points \
540-
to invalid or uninitialized memory is undefined behavior",
532+
533+
// on pointers, check if the method would exist on a reference
534+
if let SelfSource::MethodCall(rcvr_expr) = source
535+
&& let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind()
536+
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
537+
item_name,
538+
Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl),
539+
self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)),
540+
ProbeScope::TraitsInScope,
541+
None,
542+
)
543+
&& let ty::Ref(_, _, sugg_mutbl) = *pick.self_ty.kind()
544+
&& (sugg_mutbl.is_not() || ptr_mutbl.is_mut())
545+
{
546+
let (method, method_anchor) = match sugg_mutbl {
547+
Mutability::Not => {
548+
let method_anchor = match ptr_mutbl {
549+
Mutability::Not => "as_ref",
550+
Mutability::Mut => "as_ref-1",
551+
};
552+
("as_ref", method_anchor)
553+
}
554+
Mutability::Mut => ("as_mut", "as_mut"),
555+
};
556+
err.span_note(
557+
tcx.def_span(pick.item.def_id),
558+
format!("the method `{item_name}` exists on the type `{ty}`", ty = pick.self_ty),
541559
);
560+
let mut_str = ptr_mutbl.ptr_str();
561+
err.note(format!(
562+
"you might want to use the unsafe method `<*{mut_str} T>::{method}` to get \
563+
an optional reference to the value behind the pointer"
564+
));
565+
err.note(format!(
566+
"read the documentation for `<*{mut_str} T>::{method}` and ensure you satisfy its \
567+
safety preconditions before calling it to avoid undefined behavior: \
568+
https://doc.rust-lang.org/std/primitive.pointer.html#method.{method_anchor}"
569+
));
542570
}
543571

544572
let mut ty_span = match rcvr_ty.kind() {

compiler/rustc_middle/src/mir/pretty.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -978,12 +978,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
978978
CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
979979

980980
AddressOf(mutability, ref place) => {
981-
let kind_str = match mutability {
982-
Mutability::Mut => "mut",
983-
Mutability::Not => "const",
984-
};
985-
986-
write!(fmt, "&raw {kind_str} {place:?}")
981+
write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())
987982
}
988983

989984
Aggregate(ref kind, ref places) => {

compiler/rustc_middle/src/ty/print/pretty.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -671,13 +671,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
671671
p!("(", print(ty), ") is ", write("{pat:?}"))
672672
}
673673
ty::RawPtr(ty, mutbl) => {
674-
p!(write(
675-
"*{} ",
676-
match mutbl {
677-
hir::Mutability::Mut => "mut",
678-
hir::Mutability::Not => "const",
679-
}
680-
));
674+
p!(write("*{} ", mutbl.ptr_str()));
681675
p!(print(ty))
682676
}
683677
ty::Ref(r, ty, mutbl) => {

compiler/rustc_type_ir/src/ty_kind.rs

+2-11
Original file line numberDiff line numberDiff line change
@@ -373,17 +373,8 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> {
373373
Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
374374
Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)),
375375
Slice(t) => write!(f, "[{:?}]", &this.wrap(t)),
376-
RawPtr(ty, mutbl) => {
377-
match mutbl {
378-
Mutability::Mut => write!(f, "*mut "),
379-
Mutability::Not => write!(f, "*const "),
380-
}?;
381-
write!(f, "{:?}", &this.wrap(ty))
382-
}
383-
Ref(r, t, m) => match m {
384-
Mutability::Mut => write!(f, "&{:?} mut {:?}", &this.wrap(r), &this.wrap(t)),
385-
Mutability::Not => write!(f, "&{:?} {:?}", &this.wrap(r), &this.wrap(t)),
386-
},
376+
RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), this.wrap(ty)),
377+
Ref(r, t, m) => write!(f, "&{:?} {}{:?}", this.wrap(r), m.prefix_str(), this.wrap(t)),
387378
FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(),
388379
FnPtr(s) => write!(f, "{:?}", &this.wrap(s)),
389380
Dynamic(p, r, repr) => match repr {

src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,6 @@ ui/issues/issue-2150.rs
17911791
ui/issues/issue-2151.rs
17921792
ui/issues/issue-21546.rs
17931793
ui/issues/issue-21554.rs
1794-
ui/issues/issue-21596.rs
17951794
ui/issues/issue-21600.rs
17961795
ui/issues/issue-21622.rs
17971796
ui/issues/issue-21634.rs

src/tools/tidy/src/ui_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::path::{Path, PathBuf};
1717
const ENTRY_LIMIT: usize = 900;
1818
// FIXME: The following limits should be reduced eventually.
1919

20-
const ISSUES_ENTRY_LIMIT: usize = 1722;
20+
const ISSUES_ENTRY_LIMIT: usize = 1720;
2121
const ROOT_ENTRY_LIMIT: usize = 859;
2222

2323
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[

tests/ui/issues/issue-21596.rs

-5
This file was deleted.

tests/ui/issues/issue-21596.stderr

-15
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fn main() {
2+
let mut x = 8u8;
3+
let z: *const u8 = &x;
4+
// issue #21596
5+
println!("{}", z.to_string()); //~ ERROR E0599
6+
7+
let t: *mut u8 = &mut x;
8+
println!("{}", t.to_string()); //~ ERROR E0599
9+
t.make_ascii_lowercase(); //~ ERROR E0599
10+
11+
// suggest `as_mut` simply because the name is similar
12+
let _ = t.as_mut_ref(); //~ ERROR E0599
13+
let _ = t.as_ref_mut(); //~ ERROR E0599
14+
15+
// no ptr-to-ref suggestion
16+
z.make_ascii_lowercase(); //~ ERROR E0599
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
2+
--> $DIR/suggest-convert-ptr-to-ref.rs:5:22
3+
|
4+
LL | println!("{}", z.to_string());
5+
| ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
6+
|
7+
note: the method `to_string` exists on the type `&u8`
8+
--> $SRC_DIR/alloc/src/string.rs:LL:COL
9+
= note: you might want to use the unsafe method `<*const T>::as_ref` to get an optional reference to the value behind the pointer
10+
= 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
11+
= note: the following trait bounds were not satisfied:
12+
`*const u8: std::fmt::Display`
13+
which is required by `*const u8: ToString`
14+
15+
error[E0599]: `*mut u8` doesn't implement `std::fmt::Display`
16+
--> $DIR/suggest-convert-ptr-to-ref.rs:8:22
17+
|
18+
LL | println!("{}", t.to_string());
19+
| ^^^^^^^^^ `*mut u8` cannot be formatted with the default formatter
20+
|
21+
note: the method `to_string` exists on the type `&&mut u8`
22+
--> $SRC_DIR/alloc/src/string.rs:LL:COL
23+
= note: you might want to use the unsafe method `<*mut T>::as_ref` to get an optional reference to the value behind the pointer
24+
= 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
25+
= note: the following trait bounds were not satisfied:
26+
`*mut u8: std::fmt::Display`
27+
which is required by `*mut u8: ToString`
28+
29+
error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*mut u8` in the current scope
30+
--> $DIR/suggest-convert-ptr-to-ref.rs:9:7
31+
|
32+
LL | t.make_ascii_lowercase();
33+
| ^^^^^^^^^^^^^^^^^^^^ method not found in `*mut u8`
34+
|
35+
note: the method `make_ascii_lowercase` exists on the type `&mut u8`
36+
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
37+
= note: you might want to use the unsafe method `<*mut T>::as_mut` to get an optional reference to the value behind the pointer
38+
= 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
39+
40+
error[E0599]: no method named `as_mut_ref` found for raw pointer `*mut u8` in the current scope
41+
--> $DIR/suggest-convert-ptr-to-ref.rs:12:15
42+
|
43+
LL | let _ = t.as_mut_ref();
44+
| ^^^^^^^^^^
45+
|
46+
help: there is a method `as_mut` with a similar name
47+
|
48+
LL | let _ = t.as_mut();
49+
| ~~~~~~
50+
51+
error[E0599]: no method named `as_ref_mut` found for raw pointer `*mut u8` in the current scope
52+
--> $DIR/suggest-convert-ptr-to-ref.rs:13:15
53+
|
54+
LL | let _ = t.as_ref_mut();
55+
| ^^^^^^^^^^
56+
|
57+
help: there is a method `as_mut` with a similar name
58+
|
59+
LL | let _ = t.as_mut();
60+
| ~~~~~~
61+
62+
error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*const u8` in the current scope
63+
--> $DIR/suggest-convert-ptr-to-ref.rs:16:7
64+
|
65+
LL | z.make_ascii_lowercase();
66+
| ^^^^^^^^^^^^^^^^^^^^ method not found in `*const u8`
67+
68+
error: aborting due to 6 previous errors
69+
70+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)