Skip to content

Commit aec1623

Browse files
committed
Suggest casting on numeric type error
1 parent bb345a0 commit aec1623

File tree

7 files changed

+1946
-4
lines changed

7 files changed

+1946
-4
lines changed

src/librustc/hir/mod.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,37 @@ impl fmt::Debug for Expr {
11731173
}
11741174
}
11751175

1176+
impl Expr {
1177+
1178+
/// If casting this expression to a given numeric type would be appropriate in case of a type
1179+
/// mismatch.
1180+
///
1181+
/// We want to minimize the amount of casting operations that are suggested, as it can be a
1182+
/// lossy operation with potentially bad side effects, so we only suggest when encountering an
1183+
/// expression that indicates that the original type couldn't be directly changed.
1184+
pub fn could_cast_in_type_mismatch(&self) -> bool {
1185+
match self.node {
1186+
ExprCall(..) |
1187+
ExprMethodCall(..) |
1188+
ExprBinary(..) |
1189+
ExprField(..) |
1190+
ExprTupField(..) |
1191+
ExprIndex(..) |
1192+
ExprPath(..) => true,
1193+
_ => false,
1194+
}
1195+
}
1196+
1197+
pub fn needs_parens_around_cast(&self) -> bool {
1198+
match self.node {
1199+
ExprBinary(..) |
1200+
ExprCast(..) |
1201+
ExprType(..) => true,
1202+
_ => false,
1203+
}
1204+
}
1205+
}
1206+
11761207
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
11771208
pub enum Expr_ {
11781209
/// A `box x` expression.

src/librustc_typeck/check/demand.rs

Lines changed: 172 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
137137

138138
if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) {
139139
err.span_suggestion(expr.span, msg, suggestion);
140-
} else {
140+
} else if !self.check_for_cast(&mut err, expr, expr_ty, expected) {
141141
let methods = self.get_conversion_methods(expected, checked_ty);
142142
if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
143143
let suggestions = iter::repeat(expr_text).zip(methods.iter())
@@ -287,8 +287,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
287287
// Maybe remove `&`?
288288
hir::ExprAddrOf(_, ref expr) => {
289289
if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
290-
return Some(("consider removing the borrow",
291-
code));
290+
return Some(("consider removing the borrow", code));
292291
}
293292
}
294293

@@ -303,12 +302,181 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
303302
format!("*{}", code)));
304303
}
305304
}
306-
},
305+
}
307306
}
308307
}
309308
None
310309
}
311310
_ => None,
312311
}
313312
}
313+
314+
fn check_for_cast(&self,
315+
err: &mut DiagnosticBuilder<'tcx>,
316+
expr: &hir::Expr,
317+
checked_ty: Ty<'tcx>,
318+
expected_ty: Ty<'tcx>)
319+
-> bool {
320+
let will_truncate = "will truncate the source value";
321+
let depending_on_isize = "will truncate or zero-extend depending on the bit width of \
322+
`isize`";
323+
let depending_on_usize = "will truncate or zero-extend depending on the bit width of \
324+
`usize`";
325+
let will_sign_extend = "will sign-extend the source value";
326+
let will_zero_extend = "will zero-extend the source value";
327+
328+
let needs_paren = expr.needs_parens_around_cast();
329+
330+
if let (Ok(src), true) = (self.tcx.sess.codemap().span_to_snippet(expr.span),
331+
expr.could_cast_in_type_mismatch()) {
332+
let msg = format!("you can cast an `{}` to `{}`", checked_ty, expected_ty);
333+
let suggestion = format!("{}{} as {}{}",
334+
if needs_paren { "(" } else { "" },
335+
src,
336+
if needs_paren { ")" } else { "" },
337+
expected_ty);
338+
339+
match (&expected_ty.sty, &checked_ty.sty) {
340+
(&ty::TyInt(ref exp), &ty::TyInt(ref found)) => {
341+
match (found.bit_width(), exp.bit_width()) {
342+
(Some(found), Some(exp)) if found > exp => {
343+
err.span_suggestion(expr.span,
344+
&format!("{}, which {}", msg, will_truncate),
345+
suggestion);
346+
}
347+
(None, _) | (_, None) => {
348+
err.span_suggestion(expr.span,
349+
&format!("{}, which {}", msg, depending_on_isize),
350+
suggestion);
351+
}
352+
_ => {
353+
err.span_suggestion(expr.span,
354+
&format!("{}, which {}", msg, will_sign_extend),
355+
suggestion);
356+
}
357+
}
358+
true
359+
}
360+
(&ty::TyUint(ref exp), &ty::TyUint(ref found)) => {
361+
match (found.bit_width(), exp.bit_width()) {
362+
(Some(found), Some(exp)) if found > exp => {
363+
err.span_suggestion(expr.span,
364+
&format!("{}, which {}", msg, will_truncate),
365+
suggestion);
366+
}
367+
(None, _) | (_, None) => {
368+
err.span_suggestion(expr.span,
369+
&format!("{}, which {}", msg, depending_on_usize),
370+
suggestion);
371+
}
372+
_ => {
373+
err.span_suggestion(expr.span,
374+
&format!("{}, which {}", msg, will_zero_extend),
375+
suggestion);
376+
}
377+
}
378+
true
379+
}
380+
(&ty::TyInt(ref exp), &ty::TyUint(ref found)) => {
381+
match (found.bit_width(), exp.bit_width()) {
382+
(Some(found), Some(exp)) if found > exp - 1 => {
383+
err.span_suggestion(expr.span,
384+
&format!("{}, which {}", msg, will_truncate),
385+
suggestion);
386+
}
387+
(None, None) => {
388+
err.span_suggestion(expr.span,
389+
&format!("{}, which {}", msg, will_truncate),
390+
suggestion);
391+
}
392+
(None, _) => {
393+
err.span_suggestion(expr.span,
394+
&format!("{}, which {}", msg, depending_on_isize),
395+
suggestion);
396+
}
397+
(_, None) => {
398+
err.span_suggestion(expr.span,
399+
&format!("{}, which {}", msg, depending_on_usize),
400+
suggestion);
401+
}
402+
_ => {
403+
err.span_suggestion(expr.span,
404+
&format!("{}, which {}", msg, will_zero_extend),
405+
suggestion);
406+
}
407+
}
408+
true
409+
}
410+
(&ty::TyUint(ref exp), &ty::TyInt(ref found)) => {
411+
match (found.bit_width(), exp.bit_width()) {
412+
(Some(found), Some(exp)) if found - 1 > exp => {
413+
err.span_suggestion(expr.span,
414+
&format!("{}, which {}", msg, will_truncate),
415+
suggestion);
416+
}
417+
(None, None) => {
418+
err.span_suggestion(expr.span,
419+
&format!("{}, which {}", msg, will_sign_extend),
420+
suggestion);
421+
}
422+
(None, _) => {
423+
err.span_suggestion(expr.span,
424+
&format!("{}, which {}", msg, depending_on_usize),
425+
suggestion);
426+
}
427+
(_, None) => {
428+
err.span_suggestion(expr.span,
429+
&format!("{}, which {}", msg, depending_on_isize),
430+
suggestion);
431+
}
432+
_ => {
433+
err.span_suggestion(expr.span,
434+
&format!("{}, which {}", msg, will_sign_extend),
435+
suggestion);
436+
}
437+
}
438+
true
439+
}
440+
(&ty::TyFloat(ref exp), &ty::TyFloat(ref found)) => {
441+
if found.bit_width() > exp.bit_width() {
442+
err.span_suggestion(expr.span,
443+
&format!("{}, producing the closest possible value",
444+
msg),
445+
suggestion);
446+
err.warn("casting here will cause Undefined Behavior if the value is \
447+
finite but larger or smaller than the largest or smallest \
448+
finite value representable by `f32` (this is a bug and will be \
449+
fixed)");
450+
} else {
451+
err.span_suggestion(expr.span,
452+
&format!("{} in a lossless way",
453+
msg),
454+
suggestion);
455+
}
456+
true
457+
}
458+
(&ty::TyUint(_), &ty::TyFloat(_)) | (&ty::TyInt(_), &ty::TyFloat(_)) => {
459+
err.span_suggestion(expr.span,
460+
&format!("{}, rounding the float towards zero",
461+
msg),
462+
suggestion);
463+
err.warn("casting here will cause Undefined Behavior if the rounded value \
464+
cannot be represented by the target integer type, including `Inf` \
465+
and `NaN` (this is a bug and will be fixed)");
466+
true
467+
}
468+
(&ty::TyFloat(_), &ty::TyUint(_)) | (&ty::TyFloat(_), &ty::TyInt(_)) => {
469+
err.span_suggestion(expr.span,
470+
&format!("{}, producing the floating point representation \
471+
of the integer, rounded if necessary",
472+
msg),
473+
suggestion);
474+
true
475+
}
476+
_ => false,
477+
}
478+
} else {
479+
false
480+
}
481+
}
314482
}

src/test/ui/mismatched_types/issue-26480.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ error[E0308]: mismatched types
66
...
77
37 | write!(hello);
88
| -------------- in this macro invocation
9+
help: you can cast an `usize` to `u64`, which will truncate or zero-extend depending on the bit width of `usize`
10+
|
11+
26 | ($arr.len() * size_of($arr[0]) as )u64); //~ ERROR mismatched types
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
913

1014
error[E0605]: non-primitive cast: `{integer}` as `()`
1115
--> $DIR/issue-26480.rs:32:19
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo() -> i32 {
12+
4
13+
}
14+
fn main() {
15+
let x: u32 = foo();
16+
let z: i32 = x + x;
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/numeric-cast-2.rs:15:18
3+
|
4+
15 | let x: u32 = foo();
5+
| ^^^^^ expected u32, found i32
6+
help: you can cast an `i32` to `u32`, which will sign-extend the source value
7+
|
8+
15 | let x: u32 = foo() as u32;
9+
| ^^^^^^^^^^^^
10+
11+
error[E0308]: mismatched types
12+
--> $DIR/numeric-cast-2.rs:16:18
13+
|
14+
16 | let z: i32 = x + x;
15+
| ^^^^^ expected i32, found u32
16+
help: you can cast an `u32` to `i32`, which will truncate the source value
17+
|
18+
16 | let z: i32 = (x + x as )i32;
19+
| ^^^^^^^^^^^^^^
20+
21+
error: aborting due to 2 previous errors
22+

0 commit comments

Comments
 (0)