1
Fork 0

Suggest casting on numeric type error

This commit is contained in:
Esteban Küber 2018-01-06 21:10:51 -08:00
parent bb345a0be3
commit aec16237e4
7 changed files with 1946 additions and 4 deletions

View file

@ -1173,6 +1173,37 @@ impl fmt::Debug for Expr {
}
}
impl Expr {
/// If casting this expression to a given numeric type would be appropriate in case of a type
/// mismatch.
///
/// We want to minimize the amount of casting operations that are suggested, as it can be a
/// lossy operation with potentially bad side effects, so we only suggest when encountering an
/// expression that indicates that the original type couldn't be directly changed.
pub fn could_cast_in_type_mismatch(&self) -> bool {
match self.node {
ExprCall(..) |
ExprMethodCall(..) |
ExprBinary(..) |
ExprField(..) |
ExprTupField(..) |
ExprIndex(..) |
ExprPath(..) => true,
_ => false,
}
}
pub fn needs_parens_around_cast(&self) -> bool {
match self.node {
ExprBinary(..) |
ExprCast(..) |
ExprType(..) => true,
_ => false,
}
}
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum Expr_ {
/// A `box x` expression.

View file

@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) {
err.span_suggestion(expr.span, msg, suggestion);
} else {
} else if !self.check_for_cast(&mut err, expr, expr_ty, expected) {
let methods = self.get_conversion_methods(expected, checked_ty);
if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
let suggestions = iter::repeat(expr_text).zip(methods.iter())
@ -287,8 +287,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Maybe remove `&`?
hir::ExprAddrOf(_, ref expr) => {
if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
return Some(("consider removing the borrow",
code));
return Some(("consider removing the borrow", code));
}
}
@ -303,7 +302,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
format!("*{}", code)));
}
}
},
}
}
}
None
@ -311,4 +310,173 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => None,
}
}
fn check_for_cast(&self,
err: &mut DiagnosticBuilder<'tcx>,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>)
-> bool {
let will_truncate = "will truncate the source value";
let depending_on_isize = "will truncate or zero-extend depending on the bit width of \
`isize`";
let depending_on_usize = "will truncate or zero-extend depending on the bit width of \
`usize`";
let will_sign_extend = "will sign-extend the source value";
let will_zero_extend = "will zero-extend the source value";
let needs_paren = expr.needs_parens_around_cast();
if let (Ok(src), true) = (self.tcx.sess.codemap().span_to_snippet(expr.span),
expr.could_cast_in_type_mismatch()) {
let msg = format!("you can cast an `{}` to `{}`", checked_ty, expected_ty);
let suggestion = format!("{}{} as {}{}",
if needs_paren { "(" } else { "" },
src,
if needs_paren { ")" } else { "" },
expected_ty);
match (&expected_ty.sty, &checked_ty.sty) {
(&ty::TyInt(ref exp), &ty::TyInt(ref found)) => {
match (found.bit_width(), exp.bit_width()) {
(Some(found), Some(exp)) if found > exp => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_truncate),
suggestion);
}
(None, _) | (_, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_isize),
suggestion);
}
_ => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_sign_extend),
suggestion);
}
}
true
}
(&ty::TyUint(ref exp), &ty::TyUint(ref found)) => {
match (found.bit_width(), exp.bit_width()) {
(Some(found), Some(exp)) if found > exp => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_truncate),
suggestion);
}
(None, _) | (_, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_usize),
suggestion);
}
_ => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_zero_extend),
suggestion);
}
}
true
}
(&ty::TyInt(ref exp), &ty::TyUint(ref found)) => {
match (found.bit_width(), exp.bit_width()) {
(Some(found), Some(exp)) if found > exp - 1 => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_truncate),
suggestion);
}
(None, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_truncate),
suggestion);
}
(None, _) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_isize),
suggestion);
}
(_, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_usize),
suggestion);
}
_ => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_zero_extend),
suggestion);
}
}
true
}
(&ty::TyUint(ref exp), &ty::TyInt(ref found)) => {
match (found.bit_width(), exp.bit_width()) {
(Some(found), Some(exp)) if found - 1 > exp => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_truncate),
suggestion);
}
(None, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_sign_extend),
suggestion);
}
(None, _) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_usize),
suggestion);
}
(_, None) => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, depending_on_isize),
suggestion);
}
_ => {
err.span_suggestion(expr.span,
&format!("{}, which {}", msg, will_sign_extend),
suggestion);
}
}
true
}
(&ty::TyFloat(ref exp), &ty::TyFloat(ref found)) => {
if found.bit_width() > exp.bit_width() {
err.span_suggestion(expr.span,
&format!("{}, producing the closest possible value",
msg),
suggestion);
err.warn("casting here will cause Undefined Behavior if the value is \
finite but larger or smaller than the largest or smallest \
finite value representable by `f32` (this is a bug and will be \
fixed)");
} else {
err.span_suggestion(expr.span,
&format!("{} in a lossless way",
msg),
suggestion);
}
true
}
(&ty::TyUint(_), &ty::TyFloat(_)) | (&ty::TyInt(_), &ty::TyFloat(_)) => {
err.span_suggestion(expr.span,
&format!("{}, rounding the float towards zero",
msg),
suggestion);
err.warn("casting here will cause Undefined Behavior if the rounded value \
cannot be represented by the target integer type, including `Inf` \
and `NaN` (this is a bug and will be fixed)");
true
}
(&ty::TyFloat(_), &ty::TyUint(_)) | (&ty::TyFloat(_), &ty::TyInt(_)) => {
err.span_suggestion(expr.span,
&format!("{}, producing the floating point representation \
of the integer, rounded if necessary",
msg),
suggestion);
true
}
_ => false,
}
} else {
false
}
}
}

View file

@ -6,6 +6,10 @@ error[E0308]: mismatched types
...
37 | write!(hello);
| -------------- in this macro invocation
help: you can cast an `usize` to `u64`, which will truncate or zero-extend depending on the bit width of `usize`
|
26 | ($arr.len() * size_of($arr[0]) as )u64); //~ ERROR mismatched types
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0605]: non-primitive cast: `{integer}` as `()`
--> $DIR/issue-26480.rs:32:19

View file

@ -0,0 +1,17 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo() -> i32 {
4
}
fn main() {
let x: u32 = foo();
let z: i32 = x + x;
}

View file

@ -0,0 +1,22 @@
error[E0308]: mismatched types
--> $DIR/numeric-cast-2.rs:15:18
|
15 | let x: u32 = foo();
| ^^^^^ expected u32, found i32
help: you can cast an `i32` to `u32`, which will sign-extend the source value
|
15 | let x: u32 = foo() as u32;
| ^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/numeric-cast-2.rs:16:18
|
16 | let z: i32 = x + x;
| ^^^^^ expected i32, found u32
help: you can cast an `u32` to `i32`, which will truncate the source value
|
16 | let z: i32 = (x + x as )i32;
| ^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,336 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo<N>(_x: N) {}
fn main() {
let x_usize: usize = 1;
let x_u64: u64 = 2;
let x_u32: u32 = 3;
let x_u16: u16 = 4;
let x_u8: u8 = 5;
let x_isize: isize = 6;
let x_i64: i64 = 7;
let x_i32: i32 = 8;
let x_i16: i16 = 9;
let x_i8: i8 = 10;
let x_f64: f64 = 11.0;
let x_f32: f32 = 12.0;
foo::<usize>(x_usize);
foo::<usize>(x_u64);
//~^ ERROR mismatched types
foo::<usize>(x_u32);
//~^ ERROR mismatched types
foo::<usize>(x_u16);
//~^ ERROR mismatched types
foo::<usize>(x_u8);
//~^ ERROR mismatched types
foo::<usize>(x_isize);
//~^ ERROR mismatched types
foo::<usize>(x_i64);
//~^ ERROR mismatched types
foo::<usize>(x_i32);
//~^ ERROR mismatched types
foo::<usize>(x_i16);
//~^ ERROR mismatched types
foo::<usize>(x_i8);
//~^ ERROR mismatched types
foo::<usize>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<usize>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<isize>(x_usize);
//~^ ERROR mismatched types
foo::<isize>(x_u64);
//~^ ERROR mismatched types
foo::<isize>(x_u32);
//~^ ERROR mismatched types
foo::<isize>(x_u16);
//~^ ERROR mismatched types
foo::<isize>(x_u8);
//~^ ERROR mismatched types
foo::<isize>(x_isize);
foo::<isize>(x_i64);
//~^ ERROR mismatched types
foo::<isize>(x_i32);
//~^ ERROR mismatched types
foo::<isize>(x_i16);
//~^ ERROR mismatched types
foo::<isize>(x_i8);
//~^ ERROR mismatched types
foo::<isize>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<isize>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u64>(x_usize);
//~^ ERROR mismatched types
foo::<u64>(x_u64);
foo::<u64>(x_u32);
//~^ ERROR mismatched types
foo::<u64>(x_u16);
//~^ ERROR mismatched types
foo::<u64>(x_u8);
//~^ ERROR mismatched types
foo::<u64>(x_isize);
//~^ ERROR mismatched types
foo::<u64>(x_i64);
//~^ ERROR mismatched types
foo::<u64>(x_i32);
//~^ ERROR mismatched types
foo::<u64>(x_i16);
//~^ ERROR mismatched types
foo::<u64>(x_i8);
//~^ ERROR mismatched types
foo::<u64>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u64>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i64>(x_usize);
//~^ ERROR mismatched types
foo::<i64>(x_u64);
//~^ ERROR mismatched types
foo::<i64>(x_u32);
//~^ ERROR mismatched types
foo::<i64>(x_u16);
//~^ ERROR mismatched types
foo::<i64>(x_u8);
//~^ ERROR mismatched types
foo::<i64>(x_isize);
//~^ ERROR mismatched types
foo::<i64>(x_i64);
foo::<i64>(x_i32);
//~^ ERROR mismatched types
foo::<i64>(x_i16);
//~^ ERROR mismatched types
foo::<i64>(x_i8);
//~^ ERROR mismatched types
foo::<i64>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i64>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u32>(x_usize);
//~^ ERROR mismatched types
foo::<u32>(x_u64);
//~^ ERROR mismatched types
foo::<u32>(x_u32);
foo::<u32>(x_u16);
//~^ ERROR mismatched types
foo::<u32>(x_u8);
//~^ ERROR mismatched types
foo::<u32>(x_isize);
//~^ ERROR mismatched types
foo::<u32>(x_i64);
//~^ ERROR mismatched types
foo::<u32>(x_i32);
//~^ ERROR mismatched types
foo::<u32>(x_i16);
//~^ ERROR mismatched types
foo::<u32>(x_i8);
//~^ ERROR mismatched types
foo::<u32>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u32>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i32>(x_usize);
//~^ ERROR mismatched types
foo::<i32>(x_u64);
//~^ ERROR mismatched types
foo::<i32>(x_u32);
//~^ ERROR mismatched types
foo::<i32>(x_u16);
//~^ ERROR mismatched types
foo::<i32>(x_u8);
//~^ ERROR mismatched types
foo::<i32>(x_isize);
//~^ ERROR mismatched types
foo::<i32>(x_i64);
//~^ ERROR mismatched types
foo::<i32>(x_i32);
foo::<i32>(x_i16);
//~^ ERROR mismatched types
foo::<i32>(x_i8);
//~^ ERROR mismatched types
foo::<i32>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i32>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u16>(x_usize);
//~^ ERROR mismatched types
foo::<u16>(x_u64);
//~^ ERROR mismatched types
foo::<u16>(x_u32);
//~^ ERROR mismatched types
foo::<u16>(x_u16);
foo::<u16>(x_u8);
//~^ ERROR mismatched types
foo::<u16>(x_isize);
//~^ ERROR mismatched types
foo::<u16>(x_i64);
//~^ ERROR mismatched types
foo::<u16>(x_i32);
//~^ ERROR mismatched types
foo::<u16>(x_i16);
//~^ ERROR mismatched types
foo::<u16>(x_i8);
//~^ ERROR mismatched types
foo::<u16>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u16>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i16>(x_usize);
//~^ ERROR mismatched types
foo::<i16>(x_u64);
//~^ ERROR mismatched types
foo::<i16>(x_u32);
//~^ ERROR mismatched types
foo::<i16>(x_u16);
//~^ ERROR mismatched types
foo::<i16>(x_u8);
//~^ ERROR mismatched types
foo::<i16>(x_isize);
//~^ ERROR mismatched types
foo::<i16>(x_i64);
//~^ ERROR mismatched types
foo::<i16>(x_i32);
//~^ ERROR mismatched types
foo::<i16>(x_i16);
foo::<i16>(x_i8);
//~^ ERROR mismatched types
foo::<i16>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i16>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u8>(x_usize);
//~^ ERROR mismatched types
foo::<u8>(x_u64);
//~^ ERROR mismatched types
foo::<u8>(x_u32);
//~^ ERROR mismatched types
foo::<u8>(x_u16);
//~^ ERROR mismatched types
foo::<u8>(x_u8);
foo::<u8>(x_isize);
//~^ ERROR mismatched types
foo::<u8>(x_i64);
//~^ ERROR mismatched types
foo::<u8>(x_i32);
//~^ ERROR mismatched types
foo::<u8>(x_i16);
//~^ ERROR mismatched types
foo::<u8>(x_i8);
//~^ ERROR mismatched types
foo::<u8>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<u8>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i8>(x_usize);
//~^ ERROR mismatched types
foo::<i8>(x_u64);
//~^ ERROR mismatched types
foo::<i8>(x_u32);
//~^ ERROR mismatched types
foo::<i8>(x_u16);
//~^ ERROR mismatched types
foo::<i8>(x_u8);
//~^ ERROR mismatched types
foo::<i8>(x_isize);
//~^ ERROR mismatched types
foo::<i8>(x_i64);
//~^ ERROR mismatched types
foo::<i8>(x_i32);
//~^ ERROR mismatched types
foo::<i8>(x_i16);
//~^ ERROR mismatched types
foo::<i8>(x_i8);
foo::<i8>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<i8>(x_f32);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<f64>(x_usize);
//~^ ERROR mismatched types
foo::<f64>(x_u64);
//~^ ERROR mismatched types
foo::<f64>(x_u32);
//~^ ERROR mismatched types
foo::<f64>(x_u16);
//~^ ERROR mismatched types
foo::<f64>(x_u8);
//~^ ERROR mismatched types
foo::<f64>(x_isize);
//~^ ERROR mismatched types
foo::<f64>(x_i64);
//~^ ERROR mismatched types
foo::<f64>(x_i32);
//~^ ERROR mismatched types
foo::<f64>(x_i16);
//~^ ERROR mismatched types
foo::<f64>(x_i8);
//~^ ERROR mismatched types
foo::<f64>(x_f64);
foo::<f64>(x_f32);
//~^ ERROR mismatched types
foo::<f32>(x_usize);
//~^ ERROR mismatched types
foo::<f32>(x_u64);
//~^ ERROR mismatched types
foo::<f32>(x_u32);
//~^ ERROR mismatched types
foo::<f32>(x_u16);
//~^ ERROR mismatched types
foo::<f32>(x_u8);
//~^ ERROR mismatched types
foo::<f32>(x_isize);
//~^ ERROR mismatched types
foo::<f32>(x_i64);
//~^ ERROR mismatched types
foo::<f32>(x_i32);
//~^ ERROR mismatched types
foo::<f32>(x_i16);
//~^ ERROR mismatched types
foo::<f32>(x_i8);
//~^ ERROR mismatched types
foo::<f32>(x_f64);
//~^ ERROR mismatched types
//~| WARN casting here will cause Undefined Behavior
foo::<f32>(x_f32);
}

File diff suppressed because it is too large Load diff