From ad0bc66402eb528aedb1d14235fed9a70bcc0a34 Mon Sep 17 00:00:00 2001 From: "R.Chavignat" Date: Fri, 21 Aug 2015 03:03:37 +0200 Subject: [PATCH] Added support for isize/usize in the CastPass lint pass. Extracted the match that determines an integer types's size in a utility function and implemented support for usize/isize. Added a needed feature to the crate root. Added some tests to cover those cases, and a test I previously forgot. Silenced two errors signaled by dogfood.sh in unicode.rs. --- src/lib.rs | 2 +- src/types.rs | 33 ++++++++++++++++----------------- src/unicode.rs | 1 + tests/compile-fail/cast.rs | 30 ++++++++++++++++++++++++++++-- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bdb3cb3471a..9ea1efeed5f 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![feature(plugin_registrar, box_syntax)] #![feature(rustc_private, core, collections)] -#![feature(str_split_at)] +#![feature(str_split_at, num_bits_bytes)] #![allow(unknown_lints)] #[macro_use] diff --git a/src/types.rs b/src/types.rs index f9949a7b563..915ffb15fe5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -146,6 +146,18 @@ declare_lint!(pub CAST_SIGN_LOSS, Allow, declare_lint!(pub CAST_POSSIBLE_TRUNCATION, Allow, "casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, or `x as i32` where `x: f32`"); +/// Returns the size in bits of an integral type. +/// Will return 0 if the type is not an int or uint variant +fn int_ty_to_nbits(typ: &ty::TyS) -> usize { + let n = match &typ.sty { + &ty::TyInt(i) => 4 << (i as usize), + &ty::TyUint(u) => 4 << (u as usize), + _ => 0 + }; + // n == 4 is the usize/isize case + if n == 4 { ::std::usize::BITS } else { n } +} + impl LintPass for CastPass { fn get_lints(&self) -> LintArray { lint_array!(CAST_PRECISION_LOSS, @@ -159,18 +171,13 @@ impl LintPass for CastPass { if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx, expr.span) { match (cast_from.is_integral(), cast_to.is_integral()) { (true, false) => { - let from_nbits = match &cast_from.sty { - &ty::TyInt(i) => 4 << (i as usize), - &ty::TyUint(u) => 4 << (u as usize), - _ => 0 - }; + let from_nbits = int_ty_to_nbits(cast_from); let to_nbits : usize = match &cast_to.sty { &ty::TyFloat(ast::TyF32) => 32, &ty::TyFloat(ast::TyF64) => 64, _ => 0 }; - if from_nbits != 4 { - // Handle TyIs/TyUs separately (pointer size is arch dependant) + if from_nbits != 0 { if from_nbits >= to_nbits { span_lint(cx, CAST_PRECISION_LOSS, expr.span, &format!("converting from {0} to {1}, which causes a loss of precision \ @@ -192,16 +199,8 @@ impl LintPass for CastPass { span_lint(cx, CAST_SIGN_LOSS, expr.span, &format!("casting from {} to {} loses the sign of the value", cast_from, cast_to)); } - let from_nbits = match &cast_from.sty { - &ty::TyInt(i) => 4 << (i as usize), - &ty::TyUint(u) => 4 << (u as usize), - _ => 0 - }; - let to_nbits = match &cast_to.sty { - &ty::TyInt(i) => 4 << (i as usize), - &ty::TyUint(u) => 4 << (u as usize), - _ => 0 - }; + let from_nbits = int_ty_to_nbits(cast_from); + let to_nbits = int_ty_to_nbits(cast_to); if to_nbits < from_nbits || (!cast_from.is_signed() && cast_to.is_signed() && to_nbits <= from_nbits) { span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, diff --git a/src/unicode.rs b/src/unicode.rs index ab48fd1bef2..8a64f612666 100644 --- a/src/unicode.rs +++ b/src/unicode.rs @@ -40,6 +40,7 @@ fn check_str(cx: &Context, string: &str, span: Span) { } } +#[allow(cast_possible_truncation)] fn str_pos_lint(cx: &Context, lint: &'static Lint, span: Span, index: usize, msg: &str) { span_lint(cx, lint, Span { lo: span.lo + BytePos((1 + index) as u32), hi: span.lo + BytePos((1 + index) as u32), diff --git a/tests/compile-fail/cast.rs b/tests/compile-fail/cast.rs index af6e6089fbf..0fa402b3bf7 100644 --- a/tests/compile-fail/cast.rs +++ b/tests/compile-fail/cast.rs @@ -2,6 +2,7 @@ #![plugin(clippy)] #[deny(cast_precision_loss, cast_possible_truncation, cast_sign_loss)] +#[allow(dead_code)] fn main() { let i : i32 = 42; let u : u32 = 42; @@ -16,7 +17,7 @@ fn main() { (u as u64) as f64; //~ERROR converting from u64 to f64, which causes a loss of precision (u64 is 64 bits wide, but f64's mantissa is only 52 bits wide) i as f64; // Should not trigger the lint u as f64; // Should not trigger the lint - + // Test cast_possible_truncation f as i32; //~ERROR casting f32 to i32 may cause truncation of the value f as u32; //~ERROR casting f32 to u32 may cause truncation of the value @@ -25,7 +26,32 @@ fn main() { //~^ERROR casting from i32 to u8 loses the sign of the value (f as f64) as f32; //~ERROR casting f64 to f32 may cause truncation of the value i as i8; //~ERROR casting i32 to i8 may cause truncation of the value - + u as i32; //~ERROR casting u32 to i32 may cause truncation of the value + // Test cast_sign_loss i as u32; //~ERROR casting from i32 to u32 loses the sign of the value + + // Extra checks for usize/isize + let is : isize = -42; + is as usize; //~ERROR casting from isize to usize loses the sign of the value + is as i8; //~ERROR casting isize to i8 may cause truncation of the value + + // FIXME : enable these checks when we figure out a way to make compiletest deal with conditional compilation + /* + #[cfg(target_pointer_width = "64")] + fn check_64() { + let is : isize = -42; + let us : usize = 42; + is as f32; //ERROR converting from isize to f32, which causes a loss of precision (isize is 64 bits wide, but f32's mantissa is only 23 bits wide) + us as u32; //ERROR casting usize to u32 may cause truncation of the value + us as u64; // Should not trigger any lint + } + #[cfg(target_pointer_width = "32")] + fn check_32() { + let is : isize = -42; + let us : usize = 42; + is as f32; //ERROR converting from isize to f32, which causes a loss of precision (isize is 32 bits wide, but f32's mantissa is only 23 bits wide) + us as u32; // Should not trigger any lint + us as u64; // Should not trigger any lint + }*/ } \ No newline at end of file