From ecb593379c3ab075940a3fbf2e980ec5260b6ca4 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Wed, 20 May 2020 15:59:57 +0200 Subject: [PATCH] refactor check_for_cast --- src/librustc_typeck/check/demand.rs | 396 +++++++++---------- src/test/ui/discrim/discrim-ill-typed.stderr | 40 ++ src/test/ui/issues/issue-8761.stderr | 10 + src/test/ui/numeric/const-scope.stderr | 10 + src/test/ui/repeat_count.rs | 3 + src/test/ui/repeat_count.stderr | 15 +- 6 files changed, 265 insertions(+), 209 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 9694ce9450c27..6e93f2273ab86 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -16,6 +16,8 @@ use rustc_span::Span; use super::method::probe; +use std::fmt; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_coerce_suggestions( &self, @@ -689,16 +691,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool { - if self.tcx.hir().is_const_context(expr.hir_id) { - // Shouldn't suggest `.into()` on `const`s. - // FIXME(estebank): modify once we decide to suggest `as` casts - return false; - } if self.tcx.sess.source_map().is_imported(expr.span) { // Ignore if span is from within a macro. return false; } + let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) { + src + } else { + return false; + }; + // If casting this expression to a given numeric type would be appropriate in case of a type // mismatch. // @@ -727,6 +730,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { String::new() }; + if let hir::ExprKind::Call(path, args) = &expr.kind { if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) = (&path.kind, args.len()) @@ -768,222 +772,200 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty, expected_ty, ); - let needs_paren = expr.precedence().order() < (PREC_POSTFIX as i8); - - if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - let cast_suggestion = format!( - "{}{}{}{} as {}", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - expected_ty, - ); - let try_into_suggestion = format!( - "{}{}{}{}.try_into().unwrap()", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - ); - let into_suggestion = format!( - "{}{}{}{}.into()", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - ); - let suffix_suggestion = format!( - "{}{}{}{}", - if needs_paren { "(" } else { "" }, - if let (ty::Int(_) | ty::Uint(_), ty::Float(_)) = - (&expected_ty.kind, &checked_ty.kind,) - { - // Remove fractional part from literal, for example `42.0f32` into `42` - let src = src.trim_end_matches(&checked_ty.to_string()); - src.split('.').next().unwrap() + let with_opt_paren: fn(&dyn fmt::Display) -> String = + if expr.precedence().order() < PREC_POSTFIX { + |s| format!("({})", s) + } else { + |s| s.to_string() + }; + + let cast_suggestion = format!("{}{} as {}", prefix, with_opt_paren(&src), expected_ty); + let try_into_suggestion = format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src)); + let into_suggestion = format!("{}{}.into()", prefix, with_opt_paren(&src)); + let suffix_suggestion = with_opt_paren(&format_args!( + "{}{}", + if matches!( + (&expected_ty.kind, &checked_ty.kind), + (ty::Int(_) | ty::Uint(_), ty::Float(_)) + ) { + // Remove fractional part from literal, for example `42.0f32` into `42` + let src = src.trim_end_matches(&checked_ty.to_string()); + src.split('.').next().unwrap() + } else { + src.trim_end_matches(&checked_ty.to_string()) + }, + expected_ty, + )); + let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { + if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false } + }; + + let is_const_context = self.tcx.hir().is_const_context(expr.hir_id); + let suggest_to_change_suffix_or_into = + |err: &mut DiagnosticBuilder<'_>, is_fallible: bool| { + let msg = if literal_is_ty_suffixed(expr) { + &lit_msg + } else if is_const_context { + // Do not recommend `into` or `try_into` in const contexts. + return; + } else if is_fallible { + &try_msg } else { - src.trim_end_matches(&checked_ty.to_string()) - }, - expected_ty, - if needs_paren { ")" } else { "" }, - ); - let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { - if let hir::ExprKind::Lit(lit) = &expr.kind { - lit.node.is_suffixed() + &msg + }; + let suggestion = if literal_is_ty_suffixed(expr) { + suffix_suggestion.clone() + } else if is_fallible { + try_into_suggestion } else { - false - } + into_suggestion.clone() + }; + err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable); }; - let suggest_to_change_suffix_or_into = - |err: &mut DiagnosticBuilder<'_>, is_fallible: bool| { + match (&expected_ty.kind, &checked_ty.kind) { + (&ty::Int(ref exp), &ty::Int(ref found)) => { + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if exp < found => true, + (None, Some(8 | 16)) => false, + (None, _) | (_, None) => true, + _ => false, + }; + suggest_to_change_suffix_or_into(err, is_fallible); + true + } + (&ty::Uint(ref exp), &ty::Uint(ref found)) => { + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if exp < found => true, + (None, Some(8 | 16)) => false, + (None, _) | (_, None) => true, + _ => false, + }; + suggest_to_change_suffix_or_into(err, is_fallible); + true + } + (&ty::Int(exp), &ty::Uint(found)) => { + let is_fallible = match (exp.bit_width(), found.bit_width()) { + (Some(exp), Some(found)) if found < exp => false, + (None, Some(8)) => false, + _ => true, + }; + suggest_to_change_suffix_or_into(err, is_fallible); + true + } + (&ty::Uint(_), &ty::Int(_)) => { + suggest_to_change_suffix_or_into(err, true); + true + } + (&ty::Float(ref exp), &ty::Float(ref found)) => { + if found.bit_width() < exp.bit_width() { + suggest_to_change_suffix_or_into(err, false); + } else if literal_is_ty_suffixed(expr) { err.span_suggestion( expr.span, - if literal_is_ty_suffixed(expr) { - &lit_msg - } else if is_fallible { - &try_msg - } else { - &msg - }, - if literal_is_ty_suffixed(expr) { - suffix_suggestion.clone() - } else if is_fallible { - try_into_suggestion - } else { - into_suggestion.clone() - }, + &lit_msg, + suffix_suggestion, Applicability::MachineApplicable, ); - }; - - match (&expected_ty.kind, &checked_ty.kind) { - (&ty::Int(ref exp), &ty::Int(ref found)) => { - let is_fallible = match (exp.bit_width(), found.bit_width()) { - (Some(exp), Some(found)) if exp < found => true, - (None, Some(8 | 16)) => false, - (None, _) | (_, None) => true, - _ => false, - }; - suggest_to_change_suffix_or_into(err, is_fallible); - true - } - (&ty::Uint(ref exp), &ty::Uint(ref found)) => { - let is_fallible = match (exp.bit_width(), found.bit_width()) { - (Some(exp), Some(found)) if exp < found => true, - (None, Some(8 | 16)) => false, - (None, _) | (_, None) => true, - _ => false, - }; - suggest_to_change_suffix_or_into(err, is_fallible); - true - } - (&ty::Int(exp), &ty::Uint(found)) => { - let is_fallible = match (exp.bit_width(), found.bit_width()) { - (Some(exp), Some(found)) if found < exp => false, - (None, Some(8)) => false, - _ => true, - }; - suggest_to_change_suffix_or_into(err, is_fallible); - true - } - (&ty::Uint(_), &ty::Int(_)) => { - suggest_to_change_suffix_or_into(err, true); - true - } - (&ty::Float(ref exp), &ty::Float(ref found)) => { - if found.bit_width() < exp.bit_width() { - suggest_to_change_suffix_or_into(err, false); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `f64` to `f32` - err.span_suggestion( - expr.span, - &format!("{}, producing the closest possible value", cast_msg), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + } else if can_cast { + // Missing try_into implementation for `f64` to `f32` + err.span_suggestion( + expr.span, + &format!("{}, producing the closest possible value", cast_msg), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { - if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `{float}` to `{integer}` - err.span_suggestion( - expr.span, - &format!("{}, rounding the float towards zero", msg), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + true + } + (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { + if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else if can_cast { + // Missing try_into implementation for `{float}` to `{integer}` + err.span_suggestion( + expr.span, + &format!("{}, rounding the float towards zero", msg), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Float(ref exp), &ty::Uint(ref found)) => { - // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer", - msg, - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer, + true + } + (&ty::Float(ref exp), &ty::Uint(ref found)) => { + // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` + if exp.bit_width() > found.bit_width().unwrap_or(256) { + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer", + msg, + ), + into_suggestion, + Applicability::MachineApplicable, + ); + } else if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else { + // Missing try_into implementation for `{integer}` to `{float}` + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer, rounded if necessary", - cast_msg, - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + cast_msg, + ), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Float(ref exp), &ty::Int(ref found)) => { - // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer", - &msg, - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer, \ - rounded if necessary", - &msg, - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + true + } + (&ty::Float(ref exp), &ty::Int(ref found)) => { + // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` + if exp.bit_width() > found.bit_width().unwrap_or(256) { + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer", + &msg, + ), + into_suggestion, + Applicability::MachineApplicable, + ); + } else if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else { + // Missing try_into implementation for `{integer}` to `{float}` + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer, \ + rounded if necessary", + &msg, + ), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - _ => false, + true } - } else { - false + _ => false, } } } diff --git a/src/test/ui/discrim/discrim-ill-typed.stderr b/src/test/ui/discrim/discrim-ill-typed.stderr index b0e11ee5a6e60..7b9f086151a84 100644 --- a/src/test/ui/discrim/discrim-ill-typed.stderr +++ b/src/test/ui/discrim/discrim-ill-typed.stderr @@ -3,48 +3,88 @@ error[E0308]: mismatched types | LL | OhNo = 0_u8, | ^^^^ expected `i8`, found `u8` + | +help: change the type of the numeric literal from `u8` to `i8` + | +LL | OhNo = 0_i8, + | ^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:30:16 | LL | OhNo = 0_i8, | ^^^^ expected `u8`, found `i8` + | +help: change the type of the numeric literal from `i8` to `u8` + | +LL | OhNo = 0_u8, + | ^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:43:16 | LL | OhNo = 0_u16, | ^^^^^ expected `i16`, found `u16` + | +help: change the type of the numeric literal from `u16` to `i16` + | +LL | OhNo = 0_i16, + | ^^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:56:16 | LL | OhNo = 0_i16, | ^^^^^ expected `u16`, found `i16` + | +help: change the type of the numeric literal from `i16` to `u16` + | +LL | OhNo = 0_u16, + | ^^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:69:16 | LL | OhNo = 0_u32, | ^^^^^ expected `i32`, found `u32` + | +help: change the type of the numeric literal from `u32` to `i32` + | +LL | OhNo = 0_i32, + | ^^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:82:16 | LL | OhNo = 0_i32, | ^^^^^ expected `u32`, found `i32` + | +help: change the type of the numeric literal from `i32` to `u32` + | +LL | OhNo = 0_u32, + | ^^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:95:16 | LL | OhNo = 0_u64, | ^^^^^ expected `i64`, found `u64` + | +help: change the type of the numeric literal from `u64` to `i64` + | +LL | OhNo = 0_i64, + | ^^^^^ error[E0308]: mismatched types --> $DIR/discrim-ill-typed.rs:108:16 | LL | OhNo = 0_i64, | ^^^^^ expected `u64`, found `i64` + | +help: change the type of the numeric literal from `i64` to `u64` + | +LL | OhNo = 0_u64, + | ^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/issues/issue-8761.stderr b/src/test/ui/issues/issue-8761.stderr index 6ab74a9e98940..836520a28ef34 100644 --- a/src/test/ui/issues/issue-8761.stderr +++ b/src/test/ui/issues/issue-8761.stderr @@ -3,12 +3,22 @@ error[E0308]: mismatched types | LL | A = 1i64, | ^^^^ expected `isize`, found `i64` + | +help: change the type of the numeric literal from `i64` to `isize` + | +LL | A = 1isize, + | ^^^^^^ error[E0308]: mismatched types --> $DIR/issue-8761.rs:5:9 | LL | B = 2u8 | ^^^ expected `isize`, found `u8` + | +help: change the type of the numeric literal from `u8` to `isize` + | +LL | B = 2isize + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/numeric/const-scope.stderr b/src/test/ui/numeric/const-scope.stderr index 6e1990e3a7222..d7f18e19b41bd 100644 --- a/src/test/ui/numeric/const-scope.stderr +++ b/src/test/ui/numeric/const-scope.stderr @@ -3,6 +3,11 @@ error[E0308]: mismatched types | LL | const C: i32 = 1i8; | ^^^ expected `i32`, found `i8` + | +help: change the type of the numeric literal from `i8` to `i32` + | +LL | const C: i32 = 1i32; + | ^^^^ error[E0308]: mismatched types --> $DIR/const-scope.rs:2:15 @@ -17,6 +22,11 @@ LL | let c: i32 = 1i8; | --- ^^^ expected `i32`, found `i8` | | | expected due to this + | +help: change the type of the numeric literal from `i8` to `i32` + | +LL | let c: i32 = 1i32; + | ^^^^ error[E0308]: mismatched types --> $DIR/const-scope.rs:6:17 diff --git a/src/test/ui/repeat_count.rs b/src/test/ui/repeat_count.rs index aca7af144a934..7e30491f0bdbc 100644 --- a/src/test/ui/repeat_count.rs +++ b/src/test/ui/repeat_count.rs @@ -22,6 +22,9 @@ fn main() { let f = [0_usize; -1_isize]; //~^ ERROR mismatched types //~| expected `usize`, found `isize` + let f = [0; 4u8]; + //~^ ERROR mismatched types + //~| expected `usize`, found `u8` struct G { g: (), } diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat_count.stderr index 963319892d4ca..6a081e23d9d37 100644 --- a/src/test/ui/repeat_count.stderr +++ b/src/test/ui/repeat_count.stderr @@ -29,7 +29,7 @@ LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:28:17 + --> $DIR/repeat_count.rs:31:17 | LL | let g = [0; G { g: () }]; | ^^^^^^^^^^^ expected `usize`, found struct `main::G` @@ -46,7 +46,18 @@ error[E0308]: mismatched types LL | let f = [0_usize; -1_isize]; | ^^^^^^^^ expected `usize`, found `isize` -error: aborting due to 8 previous errors +error[E0308]: mismatched types + --> $DIR/repeat_count.rs:25:17 + | +LL | let f = [0; 4u8]; + | ^^^ expected `usize`, found `u8` + | +help: change the type of the numeric literal from `u8` to `usize` + | +LL | let f = [0; 4usize]; + | ^^^^^^ + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0435. For more information about an error, try `rustc --explain E0308`.