Skip to content

Commit

Permalink
Tweak "non-primitive cast" error
Browse files Browse the repository at this point in the history
- Suggest borrowing expression if it would allow cast to work.
- Suggest using `<Type>::from(<expr>)` when appropriate.
- Minor tweak to `;` typo suggestion.

Partily address #47136.
  • Loading branch information
estebank committed Jun 15, 2020
1 parent bb86748 commit e857696
Show file tree
Hide file tree
Showing 35 changed files with 204 additions and 121 deletions.
1 change: 1 addition & 0 deletions src/libcore/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ pub trait Into<T>: Sized {
/// [`Into`]: trait.Into.html
/// [`from`]: trait.From.html#tymethod.from
/// [book]: ../../book/ch09-00-error-handling.html
#[rustc_diagnostic_item = "from_trait"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(on(
all(_Self = "&str", T = "std::string::String"),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@ impl<'a> Parser<'a> {
self.bump();
let sp = self.prev_token.span;
self.struct_span_err(sp, &msg)
.span_suggestion(sp, "change this to `;`", ";".to_string(), appl)
.span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl)
.emit();
return Ok(());
} else if self.look_ahead(0, |t| {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_span/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ symbols! {
from_method,
from_ok,
from_usize,
from_trait,
fundamental,
future,
Future,
Expand Down
107 changes: 88 additions & 19 deletions src/librustc_typeck/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
use rustc_session::lint;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
Expand Down Expand Up @@ -333,23 +334,87 @@ impl<'a, 'tcx> CastCheck<'tcx> {
"only `u8` can be cast as `char`, not `{}`",
self.expr_ty
)
.span_label(self.span, "invalid cast")
.emit();
}
CastError::NonScalar => {
type_error_struct!(
let mut err = type_error_struct!(
fcx.tcx.sess,
self.span,
self.expr_ty,
E0605,
"non-primitive cast: `{}` as `{}`",
self.expr_ty,
fcx.ty_to_string(self.cast_ty)
)
.note(
"an `as` expression can only be used to convert between \
primitive types. Consider using the `From` trait",
)
.emit();
);
let mut sugg = None;
if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind {
if fcx
.try_coerce(
self.expr,
fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
self.cast_ty,
AllowTwoPhase::No,
)
.is_ok()
{
sugg = Some(format!("&{}", mutbl.prefix_str()));
}
}
if let Some(sugg) = sugg {
err.span_label(self.span, "invalid cast");
err.span_suggestion_verbose(
self.expr.span.shrink_to_lo(),
"borrow the value for the cast to be valid",
sugg,
Applicability::MachineApplicable,
);
} else if !matches!(
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) {
if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) {
let ty = fcx.resolve_vars_if_possible(&self.cast_ty);
// Erase regions to avoid panic in `prove_value` when calling
// `type_implements_trait`.
let ty = fcx.tcx.erase_regions(&ty);
let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty);
let expr_ty = fcx.tcx.erase_regions(&expr_ty);
let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
// Check for infer types because cases like `Option<{integer}>` would
// panic otherwise.
if !expr_ty.has_infer_types()
&& fcx.tcx.type_implements_trait((
from_trait,
ty,
ty_params,
fcx.param_env,
))
{
label = false;
err.span_suggestion(
self.span,
"consider using the `From` trait instead",
format!("{}::from({})", self.cast_ty, snippet),
Applicability::MaybeIncorrect,
);
}
}
}
let msg = "an `as` expression can only be used to convert between primitive \
types or to coerce to a specific trait object";
if label {
err.span_label(self.span, msg);
} else {
err.note(msg);
}
} else {
err.span_label(self.span, "invalid cast");
}
err.emit();
}
CastError::SizedUnsizedCast => {
use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic};
Expand All @@ -370,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
};
let mut err = struct_span_err!(
fcx.tcx.sess,
self.span,
if unknown_cast_to { self.cast_span } else { self.span },
E0641,
"cannot cast {} a pointer of an unknown kind",
if unknown_cast_to { "to" } else { "from" }
);
err.note(
"the type information given here is insufficient to check whether \
the pointer cast is valid",
);
if unknown_cast_to {
err.span_suggestion_short(
self.cast_span,
"consider giving more type information",
String::new(),
Applicability::Unspecified,
err.span_label(self.cast_span, "needs more type information");
err.note(
"the type information given here is insufficient to check whether \
the pointer cast is valid",
);
} else {
err.span_label(
self.span,
"the type information given here is insufficient to check whether \
the pointer cast is valid",
);
}
err.emit();
Expand Down Expand Up @@ -438,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
Ok(s) => {
err.span_suggestion(
self.cast_span,
"try casting to a `Box` instead",
"you can cast to a `Box` instead",
format!("Box<{}>", s),
Applicability::MachineApplicable,
);
}
Err(_) => {
err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr));
err.span_help(
self.cast_span,
&format!("you might have meant `Box<{}>`", tstr),
);
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/cast/cast-from-nil.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32`
--> $DIR/cast-from-nil.rs:2:21
|
LL | fn main() { let u = (assert!(true) as u32); }
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error: aborting due to previous error

Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/cast/cast-to-bare-fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize
--> $DIR/cast-to-bare-fn.rs:5:13
|
LL | let x = foo as extern "C" fn() -> isize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)`
--> $DIR/cast-to-bare-fn.rs:7:13
|
LL | let y = v as extern "Rust" fn(isize) -> (isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

error: aborting due to 2 previous errors

Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/cast/cast-to-nil.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()`
--> $DIR/cast-to-nil.rs:2:21
|
LL | fn main() { let u = 0u32 as (); }
| ^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma
LL | Box::new(1) as dyn Send;
| ^^^^^^^^^^^^^^^--------
| |
| help: try casting to a `Box` instead: `Box<dyn Send>`
| help: you can cast to a `Box` instead: `Box<dyn Send>`

error: aborting due to 2 previous errors

Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/closures/closure-no-fn-3.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b
--> $DIR/closure-no-fn-3.rs:6:27
|
LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8;
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

error: aborting due to previous error

Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/coercion/coerce-to-bang-cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `i32` as `!`
--> $DIR/coerce-to-bang-cast.rs:6:13
|
LL | let y = {return; 22} as !;
| ^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0605]: non-primitive cast: `i32` as `!`
--> $DIR/coerce-to-bang-cast.rs:11:13
|
LL | let y = 22 as !;
| ^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^ 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8`
--> $DIR/const-eval-overflow-4b.rs:25:13
|
LL | : [u32; 5i8 as char as usize]
| ^^^^^^^^^^^
| ^^^^^^^^^^^ invalid cast

error: aborting due to 3 previous errors

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/error-codes/E0604.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
--> $DIR/E0604.rs:2:5
|
LL | 1u32 as char;
| ^^^^^^^^^^^^
| ^^^^^^^^^^^^ invalid cast

error: aborting due to previous error

Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/error-codes/E0605.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec<u8>`
--> $DIR/E0605.rs:3:5
|
LL | x as Vec<u8>;
| ^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0605]: non-primitive cast: `*const u8` as `&u8`
--> $DIR/E0605.rs:6:5
|
LL | v as &u8;
| ^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^ 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

Expand Down
6 changes: 2 additions & 4 deletions src/test/ui/error-festival.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32`
--> $DIR/error-festival.rs:25:5
|
LL | 0u32 as char;
| ^^^^^^^^^^^^
| ^^^^^^^^^^^^ invalid cast

error[E0605]: non-primitive cast: `u8` as `std::vec::Vec<u8>`
--> $DIR/error-festival.rs:29:5
|
LL | x as Vec<u8>;
| ^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0054]: cannot cast as `bool`
--> $DIR/error-festival.rs:33:24
Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/fat-ptr-cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize`
--> $DIR/fat-ptr-cast.rs:14:5
|
LL | b as usize;
| ^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0606]: casting `*const [i32]` as `usize` is invalid
--> $DIR/fat-ptr-cast.rs:15:5
Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/issues/issue-10991.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize`
--> $DIR/issue-10991.rs:3:14
|
LL | let _t = nil as usize;
| ^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
| ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error: aborting due to previous error

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-16048.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> {
}

impl<'a> NoLifetime for Foo<'a> {
fn get<'p, T : Test<'a>>(&self) -> T {
fn get<'p, T: Test<'a> + From<Foo<'a>>>(&self) -> T {
//~^ ERROR E0195
//~| NOTE lifetimes do not match method in trait
return *self as T;
//~^ ERROR non-primitive cast: `Foo<'a>` as `T`
//~| NOTE an `as` expression can only be used to convert between primitive types.
//~| NOTE an `as` expression can only be used to convert between primitive types
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/issues/issue-16048.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra
LL | fn get<'p, T : Test<'p>>(&self) -> T;
| ------------------ lifetimes in impl do not match this method in trait
...
LL | fn get<'p, T : Test<'a>>(&self) -> T {
| ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
LL | fn get<'p, T: Test<'a> + From<Foo<'a>>>(&self) -> T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait

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)`
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
= 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

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-17441.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<usize>` as `dyn std::fmt::D
LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug;
| ^^^^^^^^^^^^^^^^^^^^^-------------------
| |
| help: try casting to a `Box` instead: `Box<dyn std::fmt::Debug>`
| help: you can cast to a `Box` instead: `Box<dyn std::fmt::Debug>`

error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug`
--> $DIR/issue-17441.rs:8:16
Expand Down
7 changes: 5 additions & 2 deletions src/test/ui/issues/issue-22289.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)`
--> $DIR/issue-22289.rs:2:5
|
LL | 0 as &dyn std::any::Any;
| ^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
help: borrow the value for the cast to be valid
|
LL | &0 as &dyn std::any::Any;
| ^

error: aborting due to previous error

Expand Down
Loading

0 comments on commit e857696

Please sign in to comment.