diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index e144a6ab5999a..e17cfc15a43d2 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -380,20 +380,21 @@ 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( + "consider using `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"); + err.span_help(self.span, "consider casting from `u8` instead"); } else { - err.span_help(self.span, "try `char::from_u32` instead (via a `u32`)"); + err.span_help( + self.span, + "consider using `char::from_u32` instead (via a `u32`)", + ); }; } err.emit(); @@ -494,11 +495,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.cast_ty.kind(), ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) ) { - let mut label = true; // Check `impl From 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 +504,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 +542,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 +644,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(), + "consider 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 8f862c0001401..382ccc3d10c91 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: consider 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: consider 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 3b5b8ea69c195..2803c3380d7ab 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: consider 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` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you can cast to a `Box` instead + | +LL | Box::new(1) as Box; + | ++++ + error: aborting due to 2 previous errors diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr index a287aa29e1183..891931d0bf89f 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 7e5861f83e9c1..e5e9ee134597d 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` --> $DIR/non-primitive-cast-135412.rs:8:13 | LL | let _ = "String" as Arc; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ 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; diff --git a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr index 1a0832b8ba02b..b996370ea3dfd 100644 --- a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -23,7 +23,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8` LL | : [u32; 5i8 as char as usize] | ^^^^^^^^^^^ invalid cast | -help: try casting from `u8` instead +help: consider casting from `u8` instead --> $DIR/const-eval-overflow-4b.rs:24:13 | LL | : [u32; 5i8 as char as usize] diff --git a/tests/ui/error-codes/E0604.stderr b/tests/ui/error-codes/E0604.stderr index e91f74d6b3fd6..67bbb25958f12 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: consider using `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 644ba813c9608..696407c6f7e0d 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: consider 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 be484bc8094fe..6c661eb17a87b 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: consider using `char::from_u32` instead + | +LL - 0u32 as char; +LL + char::from_u32(0u32); + | error[E0605]: non-primitive cast: `u8` as `Vec` --> $DIR/error-festival.rs:31:5 diff --git a/tests/ui/issues/issue-16048.stderr b/tests/ui/issues/issue-16048.stderr index 73610942d7a7e..f97f13152bc83 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 29e50b91c7c6f..96aad879e24d2 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: consider casting to a reference instead + | +LL | let _foo = &[1_usize, 2] as &[usize]; + | + error[E0620]: cast to unsized type: `Box` 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` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you can cast to a `Box` instead + | +LL | let _bar = Box::new(1_usize) as Box; + | ++++ + 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 a427639594415..a188b7791fdc8 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: consider using `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 01b4a9a7ce026..834d4ea241c12 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 2e1dde27d0f98..8ec1c5f11ec27 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