about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-06-06 19:39:48 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-06-06 19:53:12 +0000
commit24dcfaf71ffe08a0b5acfcc5b5b4d2419e79b847 (patch)
tree530b96a7a6e54d7803a91c01d02749a6afeeb753
parentdf8102fe5f24f28a918660b0cd918d7331c3896e (diff)
downloadrust-24dcfaf71ffe08a0b5acfcc5b5b4d2419e79b847.tar.gz
rust-24dcfaf71ffe08a0b5acfcc5b5b4d2419e79b847.zip
Make cast suggestions verbose
```
error[E0604]: only `u8` can be cast as `char`, not `u32`
  --> $DIR/E0604.rs:2:5
   |
LL |     1u32 as char;
   |     ^^^^^^^^^^^^ invalid cast
   |
help: try `char::from_u32` instead
   |
LL -     1u32 as char;
LL +     char::from_u32(1u32);
   |
```

```
error[E0620]: cast to unsized type: `&[u8]` as `[char]`
  --> $DIR/cast-to-slice.rs:6:5
   |
LL |     arr as [char];
   |     ^^^^^^^^^^^^^
   |
help: try casting to a reference instead
   |
LL |     arr as &[char];
   |            +
```

```
error[E0620]: cast to unsized type: `Box<{integer}>` as `dyn Send`
  --> $DIR/cast-to-unsized-trait-object-suggestion.rs:3:5
   |
LL |     Box::new(1) as dyn Send;
   |     ^^^^^^^^^^^^^^^^^^^^^^^
   |
help: you can cast to a `Box` instead
   |
LL |     Box::new(1) as Box<dyn Send>;
   |                    ++++        +
```
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs105
-rw-r--r--tests/ui/cast/cast-to-slice.stderr18
-rw-r--r--tests/ui/cast/cast-to-unsized-trait-object-suggestion.stderr18
-rw-r--r--tests/ui/coercion/issue-73886.stderr3
-rw-r--r--tests/ui/coercion/non-primitive-cast-135412.stderr6
-rw-r--r--tests/ui/error-codes/E0604.stderr11
-rw-r--r--tests/ui/error-codes/E0620.stderr9
-rw-r--r--tests/ui/error-emitter/error-festival.stderr11
-rw-r--r--tests/ui/issues/issue-16048.stderr8
-rw-r--r--tests/ui/issues/issue-17441.stderr18
-rw-r--r--tests/ui/mismatched_types/cast-rfc0401.stderr11
-rw-r--r--tests/ui/nonscalar-cast.stderr8
-rw-r--r--tests/ui/tag-variant-cast-non-nullary.stderr8
13 files changed, 122 insertions, 112 deletions
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index e144a6ab599..3884691cbbd 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -380,16 +380,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 err.span_label(self.span, "invalid cast");
                 if self.expr_ty.is_numeric() {
                     if self.expr_ty == fcx.tcx.types.u32 {
-                        match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
-                            Ok(snippet) => err.span_suggestion(
-                                self.span,
-                                "try `char::from_u32` instead",
-                                format!("char::from_u32({snippet})"),
-                                Applicability::MachineApplicable,
-                            ),
-
-                            Err(_) => err.span_help(self.span, "try `char::from_u32` instead"),
-                        };
+                        err.multipart_suggestion(
+                            "try `char::from_u32` instead",
+                            vec![
+                                (self.expr_span.shrink_to_lo(), "char::from_u32(".to_string()),
+                                (self.expr_span.shrink_to_hi().to(self.cast_span), ")".to_string()),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
                     } else if self.expr_ty == fcx.tcx.types.i8 {
                         err.span_help(self.span, "try casting from `u8` instead");
                     } else {
@@ -494,11 +492,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     self.cast_ty.kind(),
                     ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..)
                 ) {
-                    let mut label = true;
                     // Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion:
-                    if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span)
-                        && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From)
-                    {
+                    if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) {
                         let ty = fcx.resolve_vars_if_possible(self.cast_ty);
                         let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
                         if fcx
@@ -506,26 +501,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env)
                             .must_apply_modulo_regions()
                         {
-                            label = false;
-                            if let ty::Adt(def, args) = self.cast_ty.kind() {
-                                err.span_suggestion_verbose(
-                                    self.span,
-                                    "consider using the `From` trait instead",
-                                    format!(
-                                        "{}::from({})",
-                                        fcx.tcx.value_path_str_with_args(def.did(), args),
-                                        snippet
-                                    ),
-                                    Applicability::MaybeIncorrect,
-                                );
+                            let to_ty = if let ty::Adt(def, args) = self.cast_ty.kind() {
+                                fcx.tcx.value_path_str_with_args(def.did(), args)
                             } else {
-                                err.span_suggestion(
-                                    self.span,
-                                    "consider using the `From` trait instead",
-                                    format!("{}::from({})", self.cast_ty, snippet),
-                                    Applicability::MaybeIncorrect,
-                                );
+                                self.cast_ty.to_string()
                             };
+                            err.multipart_suggestion(
+                                "consider using the `From` trait instead",
+                                vec![
+                                    (self.expr_span.shrink_to_lo(), format!("{to_ty}::from(")),
+                                    (
+                                        self.expr_span.shrink_to_hi().to(self.cast_span),
+                                        ")".to_string(),
+                                    ),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
                         }
                     }
 
@@ -548,11 +539,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         )
                     };
 
-                    if label {
-                        err.span_label(self.span, msg);
-                    } else {
-                        err.note(msg);
-                    }
+                    err.span_label(self.span, msg);
 
                     if let Some(note) = note {
                         err.note(note);
@@ -654,38 +641,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         match self.expr_ty.kind() {
             ty::Ref(_, _, mt) => {
                 let mtstr = mt.prefix_str();
-                match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) {
-                    Ok(s) => {
-                        err.span_suggestion(
-                            self.cast_span,
-                            "try casting to a reference instead",
-                            format!("&{mtstr}{s}"),
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                    Err(_) => {
-                        let msg = format!("did you mean `&{mtstr}{tstr}`?");
-                        err.span_help(self.cast_span, msg);
-                    }
-                }
+                err.span_suggestion_verbose(
+                    self.cast_span.shrink_to_lo(),
+                    "try casting to a reference instead",
+                    format!("&{mtstr}"),
+                    Applicability::MachineApplicable,
+                );
             }
             ty::Adt(def, ..) if def.is_box() => {
-                match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) {
-                    Ok(s) => {
-                        err.span_suggestion(
-                            self.cast_span,
-                            "you can cast to a `Box` instead",
-                            format!("Box<{s}>"),
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                    Err(_) => {
-                        err.span_help(
-                            self.cast_span,
-                            format!("you might have meant `Box<{tstr}>`"),
-                        );
-                    }
-                }
+                err.multipart_suggestion(
+                    "you can cast to a `Box` instead",
+                    vec![
+                        (self.cast_span.shrink_to_lo(), "Box<".to_string()),
+                        (self.cast_span.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
             }
             _ => {
                 err.span_help(self.expr_span, "consider using a box or reference as appropriate");
diff --git a/tests/ui/cast/cast-to-slice.stderr b/tests/ui/cast/cast-to-slice.stderr
index 8f862c00014..1033f77ee0c 100644
--- a/tests/ui/cast/cast-to-slice.stderr
+++ b/tests/ui/cast/cast-to-slice.stderr
@@ -2,17 +2,23 @@ error[E0620]: cast to unsized type: `&[u8]` as `[char]`
   --> $DIR/cast-to-slice.rs:2:5
    |
 LL |     "example".as_bytes() as [char];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^------
-   |                             |
-   |                             help: try casting to a reference instead: `&[char]`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try casting to a reference instead
+   |
+LL |     "example".as_bytes() as &[char];
+   |                             +
 
 error[E0620]: cast to unsized type: `&[u8]` as `[char]`
   --> $DIR/cast-to-slice.rs:6:5
    |
 LL |     arr as [char];
-   |     ^^^^^^^------
-   |            |
-   |            help: try casting to a reference instead: `&[char]`
+   |     ^^^^^^^^^^^^^
+   |
+help: try casting to a reference instead
+   |
+LL |     arr as &[char];
+   |            +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/tests/ui/cast/cast-to-unsized-trait-object-suggestion.stderr
index 3b5b8ea69c1..cc96055abeb 100644
--- a/tests/ui/cast/cast-to-unsized-trait-object-suggestion.stderr
+++ b/tests/ui/cast/cast-to-unsized-trait-object-suggestion.stderr
@@ -2,17 +2,23 @@ error[E0620]: cast to unsized type: `&{integer}` as `dyn Send`
   --> $DIR/cast-to-unsized-trait-object-suggestion.rs:2:5
    |
 LL |     &1 as dyn Send;
-   |     ^^^^^^--------
-   |           |
-   |           help: try casting to a reference instead: `&dyn Send`
+   |     ^^^^^^^^^^^^^^
+   |
+help: try casting to a reference instead
+   |
+LL |     &1 as &dyn Send;
+   |           +
 
 error[E0620]: cast to unsized type: `Box<{integer}>` as `dyn Send`
   --> $DIR/cast-to-unsized-trait-object-suggestion.rs:3:5
    |
 LL |     Box::new(1) as dyn Send;
-   |     ^^^^^^^^^^^^^^^--------
-   |                    |
-   |                    help: you can cast to a `Box` instead: `Box<dyn Send>`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you can cast to a `Box` instead
+   |
+LL |     Box::new(1) as Box<dyn Send>;
+   |                    ++++        +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr
index a287aa29e11..891931d0bf8 100644
--- a/tests/ui/coercion/issue-73886.stderr
+++ b/tests/ui/coercion/issue-73886.stderr
@@ -10,9 +10,8 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>`
   --> $DIR/issue-73886.rs:4:13
    |
 LL |     let _ = 7u32 as Option<_>;
-   |             ^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 help: consider using the `From` trait instead
    |
 LL -     let _ = 7u32 as Option<_>;
diff --git a/tests/ui/coercion/non-primitive-cast-135412.stderr b/tests/ui/coercion/non-primitive-cast-135412.stderr
index 7e5861f83e9..e5e9ee13459 100644
--- a/tests/ui/coercion/non-primitive-cast-135412.stderr
+++ b/tests/ui/coercion/non-primitive-cast-135412.stderr
@@ -2,9 +2,8 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>`
   --> $DIR/non-primitive-cast-135412.rs:6:13
    |
 LL |     let _ = 7u32 as Option<_>;
-   |             ^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 help: consider using the `From` trait instead
    |
 LL -     let _ = 7u32 as Option<_>;
@@ -15,9 +14,8 @@ error[E0605]: non-primitive cast: `&'static str` as `Arc<str>`
   --> $DIR/non-primitive-cast-135412.rs:8:13
    |
 LL |     let _ = "String" as Arc<str>;
-   |             ^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 help: consider using the `From` trait instead
    |
 LL -     let _ = "String" as Arc<str>;
diff --git a/tests/ui/error-codes/E0604.stderr b/tests/ui/error-codes/E0604.stderr
index e91f74d6b3f..b949ed973cd 100644
--- a/tests/ui/error-codes/E0604.stderr
+++ b/tests/ui/error-codes/E0604.stderr
@@ -2,10 +2,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
   --> $DIR/E0604.rs:2:5
    |
 LL |     1u32 as char;
-   |     ^^^^^^^^^^^^
-   |     |
-   |     invalid cast
-   |     help: try `char::from_u32` instead: `char::from_u32(1u32)`
+   |     ^^^^^^^^^^^^ invalid cast
+   |
+help: try `char::from_u32` instead
+   |
+LL -     1u32 as char;
+LL +     char::from_u32(1u32);
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0620.stderr b/tests/ui/error-codes/E0620.stderr
index 644ba813c96..b2efe02f3aa 100644
--- a/tests/ui/error-codes/E0620.stderr
+++ b/tests/ui/error-codes/E0620.stderr
@@ -2,9 +2,12 @@ error[E0620]: cast to unsized type: `&[usize; 2]` as `[usize]`
   --> $DIR/E0620.rs:2:16
    |
 LL |     let _foo = &[1_usize, 2] as [usize];
-   |                ^^^^^^^^^^^^^^^^^-------
-   |                                 |
-   |                                 help: try casting to a reference instead: `&[usize]`
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try casting to a reference instead
+   |
+LL |     let _foo = &[1_usize, 2] as &[usize];
+   |                                 +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-emitter/error-festival.stderr b/tests/ui/error-emitter/error-festival.stderr
index be484bc8094..d2afae69c07 100644
--- a/tests/ui/error-emitter/error-festival.stderr
+++ b/tests/ui/error-emitter/error-festival.stderr
@@ -58,10 +58,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
   --> $DIR/error-festival.rs:27:5
    |
 LL |     0u32 as char;
-   |     ^^^^^^^^^^^^
-   |     |
-   |     invalid cast
-   |     help: try `char::from_u32` instead: `char::from_u32(0u32)`
+   |     ^^^^^^^^^^^^ invalid cast
+   |
+help: try `char::from_u32` instead
+   |
+LL -     0u32 as char;
+LL +     char::from_u32(0u32);
+   |
 
 error[E0605]: non-primitive cast: `u8` as `Vec<u8>`
   --> $DIR/error-festival.rs:31:5
diff --git a/tests/ui/issues/issue-16048.stderr b/tests/ui/issues/issue-16048.stderr
index 73610942d7a..f97f13152bc 100644
--- a/tests/ui/issues/issue-16048.stderr
+++ b/tests/ui/issues/issue-16048.stderr
@@ -11,9 +11,13 @@ error[E0605]: non-primitive cast: `Foo<'a>` as `T`
   --> $DIR/issue-16048.rs:24:16
    |
 LL |         return *self as T;
-   |                ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)`
+   |                ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+   |
+help: consider using the `From` trait instead
+   |
+LL -         return *self as T;
+LL +         return T::from(*self);
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/issues/issue-17441.stderr b/tests/ui/issues/issue-17441.stderr
index 29e50b91c7c..c96b13d62dc 100644
--- a/tests/ui/issues/issue-17441.stderr
+++ b/tests/ui/issues/issue-17441.stderr
@@ -2,17 +2,23 @@ error[E0620]: cast to unsized type: `&[usize; 2]` as `[usize]`
   --> $DIR/issue-17441.rs:2:16
    |
 LL |     let _foo = &[1_usize, 2] as [usize];
-   |                ^^^^^^^^^^^^^^^^^-------
-   |                                 |
-   |                                 help: try casting to a reference instead: `&[usize]`
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try casting to a reference instead
+   |
+LL |     let _foo = &[1_usize, 2] as &[usize];
+   |                                 +
 
 error[E0620]: cast to unsized type: `Box<usize>` as `dyn Debug`
   --> $DIR/issue-17441.rs:5:16
    |
 LL |     let _bar = Box::new(1_usize) as dyn std::fmt::Debug;
-   |                ^^^^^^^^^^^^^^^^^^^^^-------------------
-   |                                     |
-   |                                     help: you can cast to a `Box` instead: `Box<dyn std::fmt::Debug>`
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: you can cast to a `Box` instead
+   |
+LL |     let _bar = Box::new(1_usize) as Box<dyn std::fmt::Debug>;
+   |                                     ++++                   +
 
 error[E0620]: cast to unsized type: `usize` as `dyn Debug`
   --> $DIR/issue-17441.rs:8:16
diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr
index a4276395944..eb95d659840 100644
--- a/tests/ui/mismatched_types/cast-rfc0401.stderr
+++ b/tests/ui/mismatched_types/cast-rfc0401.stderr
@@ -104,10 +104,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
   --> $DIR/cast-rfc0401.rs:41:13
    |
 LL |     let _ = 0x61u32 as char;
-   |             ^^^^^^^^^^^^^^^
-   |             |
-   |             invalid cast
-   |             help: try `char::from_u32` instead: `char::from_u32(0x61u32)`
+   |             ^^^^^^^^^^^^^^^ invalid cast
+   |
+help: try `char::from_u32` instead
+   |
+LL -     let _ = 0x61u32 as char;
+LL +     let _ = char::from_u32(0x61u32);
+   |
 
 error[E0606]: casting `bool` as `f32` is invalid
   --> $DIR/cast-rfc0401.rs:43:13
diff --git a/tests/ui/nonscalar-cast.stderr b/tests/ui/nonscalar-cast.stderr
index 01b4a9a7ce0..834d4ea241c 100644
--- a/tests/ui/nonscalar-cast.stderr
+++ b/tests/ui/nonscalar-cast.stderr
@@ -2,9 +2,13 @@ error[E0605]: non-primitive cast: `Foo` as `isize`
   --> $DIR/nonscalar-cast.rs:15:20
    |
 LL |     println!("{}", Foo { x: 1 } as isize);
-   |                    ^^^^^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(Foo { x: 1 })`
+   |                    ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+   |
+help: consider using the `From` trait instead
+   |
+LL -     println!("{}", Foo { x: 1 } as isize);
+LL +     println!("{}", isize::from(Foo { x: 1 }));
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/tag-variant-cast-non-nullary.stderr b/tests/ui/tag-variant-cast-non-nullary.stderr
index 2e1dde27d0f..8ec1c5f11ec 100644
--- a/tests/ui/tag-variant-cast-non-nullary.stderr
+++ b/tests/ui/tag-variant-cast-non-nullary.stderr
@@ -2,10 +2,14 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize`
   --> $DIR/tag-variant-cast-non-nullary.rs:19:15
    |
 LL |     let val = v as isize;
-   |               ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)`
+   |               ^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
    |
-   = note: an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
    = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
+help: consider using the `From` trait instead
+   |
+LL -     let val = v as isize;
+LL +     let val = isize::from(v);
+   |
 
 error: aborting due to 1 previous error