From e822e62ee80a9108bfdb7d0952c85fab2146f569 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 22 Feb 2018 15:53:22 +0100 Subject: [PATCH 1/6] Suggest type for overflowing bin/hex-literals --- src/librustc_lint/types.rs | 194 ++++++++++++++++++++++---- src/test/ui/lint/type-overflow.rs | 35 +++++ src/test/ui/lint/type-overflow.stderr | 70 ++++++++++ 3 files changed, 274 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/lint/type-overflow.rs create mode 100644 src/test/ui/lint/type-overflow.stderr diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index ef9b3d38c637c..4fabb5bafbf89 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -150,11 +150,52 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { // Detect literal value out of range [min, max] inclusive // avoiding use of -min to prevent overflow/panic - if (negative && v > max + 1) || - (!negative && v > max) { - cx.span_lint(OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t)); + if (negative && v > max + 1) || (!negative && v > max) { + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + let bits = int_ty_bits(t, cx.sess().target.isize_ty); + let mut actually = v as i128; + if bits < 128 { + // v & 0b0..01..1, |1| = bits + let trimmed = v & ((1 << bits) - 1); + actually = if v & (1 << (bits - 1)) == 0 { + // positive + trimmed as i128 + } else { + // negative -> two's complement + (((-1 as i128 as u128) << bits) | trimmed) as i128 + }; + } + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t), + ); + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + an `{:?}` and will become `{}{:?}`.", + repr_str, v, t, actually, t + )); + let sugg_ty = get_fitting_type( + &cx.tables.node_id_to_type(e.hir_id).sty, + v, + negative, + ).map_or(String::new(), |ty| match ty { + ty::TyUint(t) => format!("Consider using `{:?}`", t), + ty::TyInt(t) => format!("Consider using `{:?}`", t), + _ => String::new(), + }); + if !sugg_ty.is_empty() { + err.help(&sugg_ty); + } + + err.emit(); + return; + } + cx.span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t), + ); return; } } @@ -180,37 +221,77 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if let hir::ExprCast(..) = parent_expr.node { if let ty::TyChar = cx.tables.expr_ty(parent_expr).sty { let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - "only u8 can be casted into char"); - err.span_suggestion(parent_expr.span, - &"use a char literal instead", - format!("'\\u{{{:X}}}'", lit_val)); + OVERFLOWING_LITERALS, + parent_expr.span, + "only u8 can be casted into char", + ); + err.span_suggestion( + parent_expr.span, + &"use a char literal instead", + format!("'\\u{{{:X}}}'", lit_val), + ); err.emit(); - return + return; } } } - cx.span_lint(OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t)); + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + let bits = uint_ty_bits(t, cx.sess().target.usize_ty); + // u128 cannot be greater than max -> compiler error + let actually = lit_val & ((1 << bits) - 1); + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t), + ); + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + an `{:?}` and will become `{}{:?}`.", + repr_str, lit_val, t, actually, t + )); + let sugg_ty = get_fitting_type( + &cx.tables.node_id_to_type(e.hir_id).sty, + lit_val, + false, + ).map_or( + String::new(), + |ty| { + if let ty::TyUint(t) = ty { + format!("Consider using `{:?}`", t) + } else { + String::new() + } + }, + ); + if !sugg_ty.is_empty() { + err.help(&sugg_ty); + } + + err.emit(); + return; + } + cx.span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t), + ); } } ty::TyFloat(t) => { let is_infinite = match lit.node { - ast::LitKind::Float(v, _) | - ast::LitKind::FloatUnsuffixed(v) => { - match t { - ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), - } - } + ast::LitKind::Float(v, _) | ast::LitKind::FloatUnsuffixed(v) => match t + { + ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), + ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), + }, _ => bug!(), }; if is_infinite == Ok(true) { - cx.span_lint(OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t)); + cx.span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t), + ); } } _ => (), @@ -338,6 +419,69 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { _ => false, } } + + fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option { + if let Some(src) = cx.sess().codemap().span_to_snippet(lit.span).ok() { + if let Some(firstch) = src.chars().next() { + if let Some(0) = char::to_digit(firstch, 10) { + if let Some(base) = src.chars().nth(1) { + if base == 'x' || base == 'b' { + return Some(src); + } + } + } + } + } + + None + } + + fn get_fitting_type<'a>( + t: &ty::TypeVariants, + val: u128, + negative: bool, + ) -> Option> { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + macro_rules! find_fit { + ($ty:expr, $val:expr, $negative:expr, + $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { + { + let _neg = if negative { 1 } else { 0 }; + match $ty { + $($type => { + $(if !negative && val <= uint_ty_range($utypes).1 { + return Some(ty::TyUint($utypes)) + })* + $(if val <= int_ty_range($itypes).1 as u128 + _neg { + return Some(ty::TyInt($itypes)) + })* + None + },)* + _ => None + } + } + } + } + if let &ty::TyInt(i) = t { + return find_fit!(i, val, negative, + I8 => [U8] => [I16, I32, I64, I128], + I16 => [U16] => [I32, I64, I128], + I32 => [U32] => [I64, I128], + I64 => [U64] => [I128], + I128 => [U128] => []); + } + if let &ty::TyUint(u) = t { + return find_fit!(u, val, negative, + U8 => [U8, U16, U32, U64, U128] => [], + U16 => [U16, U32, U64, U128] => [], + U32 => [U32, U64, U128] => [], + U64 => [U64, U128] => [], + U128 => [U128] => []); + } + + None + } } } diff --git a/src/test/ui/lint/type-overflow.rs b/src/test/ui/lint/type-overflow.rs new file mode 100644 index 0000000000000..e414f43b3ffd7 --- /dev/null +++ b/src/test/ui/lint/type-overflow.rs @@ -0,0 +1,35 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(i128_type)] + +fn main() { + let error = 255i8; //~WARNING literal out of range for i8 + + let ok = 0b1000_0001; // should be ok -> i32 + let ok = 0b0111_1111i8; // should be ok -> 127i8 + + let fail = 0b1000_0001i8; //~WARNING literal out of range for i8 + + let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64 + + let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32 + + let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; + //~^ WARNING literal out of range for i128 + + let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 + + let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize + + let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 +} diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr new file mode 100644 index 0000000000000..425f76da5cb4e --- /dev/null +++ b/src/test/ui/lint/type-overflow.stderr @@ -0,0 +1,70 @@ +warning: literal out of range for i8 + --> $DIR/type-overflow.rs:16:17 + | +16 | let error = 255i8; //~WARNING literal out of range for i8 + | ^^^^^ + | + = note: #[warn(overflowing_literals)] on by default + +warning: literal out of range for i8 + --> $DIR/type-overflow.rs:21:16 + | +21 | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8 + | ^^^^^^^^^^^^^ + | + = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8`. + = help: Consider using `u8` + +warning: literal out of range for i64 + --> $DIR/type-overflow.rs:23:16 + | +23 | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64 + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64`. + = help: Consider using `u64` + +warning: literal out of range for u32 + --> $DIR/type-overflow.rs:25:16 + | +25 | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32 + | ^^^^^^^^^^^^^^^^ + | + = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32`. + = help: Consider using `u64` + +warning: literal out of range for i128 + --> $DIR/type-overflow.rs:27:22 + | +27 | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128`. + = help: Consider using `u128` + +warning: literal out of range for i32 + --> $DIR/type-overflow.rs:30:16 + | +30 | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32`. + = help: Consider using `i128` + +warning: literal out of range for isize + --> $DIR/type-overflow.rs:32:23 + | +32 | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize`. + +warning: literal out of range for i8 + --> $DIR/type-overflow.rs:34:17 + | +34 | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 + | ^^^^^^^^^^^^^ + | + = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8`. + = help: Consider using `i16` + From 19c4771eeb5fc8fb18bb2e9a3f16ee474d8e67e3 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 22 Feb 2018 20:25:58 +0100 Subject: [PATCH 2/6] Implementing requested changes --- src/librustc_lint/types.rs | 141 +++++++++++++++---------------------- 1 file changed, 56 insertions(+), 85 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 4fabb5bafbf89..db9dfedc656ba 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -153,18 +153,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if (negative && v > max + 1) || (!negative && v > max) { if let Some(repr_str) = get_bin_hex_repr(cx, lit) { let bits = int_ty_bits(t, cx.sess().target.isize_ty); - let mut actually = v as i128; - if bits < 128 { - // v & 0b0..01..1, |1| = bits - let trimmed = v & ((1 << bits) - 1); - actually = if v & (1 << (bits - 1)) == 0 { - // positive - trimmed as i128 - } else { - // negative -> two's complement - (((-1 as i128 as u128) << bits) | trimmed) as i128 - }; - } + let actually = + ((v << (128 - bits)) as i128) >> (128 - bits); let mut err = cx.struct_span_lint( OVERFLOWING_LITERALS, e.span, @@ -175,15 +165,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { an `{:?}` and will become `{}{:?}`.", repr_str, v, t, actually, t )); - let sugg_ty = get_fitting_type( + let sugg_ty = get_type_suggestion( &cx.tables.node_id_to_type(e.hir_id).sty, v, negative, - ).map_or(String::new(), |ty| match ty { - ty::TyUint(t) => format!("Consider using `{:?}`", t), - ty::TyInt(t) => format!("Consider using `{:?}`", t), - _ => String::new(), - }); + ); if !sugg_ty.is_empty() { err.help(&sugg_ty); } @@ -221,24 +207,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if let hir::ExprCast(..) = parent_expr.node { if let ty::TyChar = cx.tables.expr_ty(parent_expr).sty { let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - "only u8 can be casted into char", - ); - err.span_suggestion( - parent_expr.span, - &"use a char literal instead", - format!("'\\u{{{:X}}}'", lit_val), - ); + OVERFLOWING_LITERALS, + parent_expr.span, + "only u8 can be casted into char"); + err.span_suggestion(parent_expr.span, + &"use a char literal instead", + format!("'\\u{{{:X}}}'", lit_val)); err.emit(); - return; + return } } } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { let bits = uint_ty_bits(t, cx.sess().target.usize_ty); - // u128 cannot be greater than max -> compiler error - let actually = lit_val & ((1 << bits) - 1); + let actually = (lit_val << (128 - bits)) >> (128 - bits); let mut err = cx.struct_span_lint( OVERFLOWING_LITERALS, e.span, @@ -249,19 +231,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { an `{:?}` and will become `{}{:?}`.", repr_str, lit_val, t, actually, t )); - let sugg_ty = get_fitting_type( + let sugg_ty = get_type_suggestion( &cx.tables.node_id_to_type(e.hir_id).sty, lit_val, false, - ).map_or( - String::new(), - |ty| { - if let ty::TyUint(t) = ty { - format!("Consider using `{:?}`", t) - } else { - String::new() - } - }, ); if !sugg_ty.is_empty() { err.help(&sugg_ty); @@ -279,19 +252,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } ty::TyFloat(t) => { let is_infinite = match lit.node { - ast::LitKind::Float(v, _) | ast::LitKind::FloatUnsuffixed(v) => match t - { - ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), - }, + ast::LitKind::Float(v, _) | + ast::LitKind::FloatUnsuffixed(v) => { + match t { + ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), + ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), + } + } _ => bug!(), }; if is_infinite == Ok(true) { - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t), - ); + cx.span_lint(OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for {:?}", t)); } } _ => (), @@ -421,26 +394,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option { - if let Some(src) = cx.sess().codemap().span_to_snippet(lit.span).ok() { - if let Some(firstch) = src.chars().next() { - if let Some(0) = char::to_digit(firstch, 10) { - if let Some(base) = src.chars().nth(1) { - if base == 'x' || base == 'b' { - return Some(src); - } - } - } + let src = cx.sess().codemap().span_to_snippet(lit.span).ok()?; + let firstch = src.chars().next()?; + + if let Some(0) = char::to_digit(firstch, 10) { + match src.chars().nth(1) { + Some('x') | Some('b') => return Some(src), + _ => return None, } } None } - fn get_fitting_type<'a>( - t: &ty::TypeVariants, - val: u128, - negative: bool, - ) -> Option> { + // This function finds the next fitting type and generates a suggestion string. + // It searches for fitting types in the following way (`X < Y`): + // - `iX`: if literal fits in `uX` => `uX`, else => `iY` + // - `-iX` => `iY` + // - `uX` => `uY` + // + // No suggestion for: `isize`, `usize`. + fn get_type_suggestion<'a>(t: &ty::TypeVariants, val: u128, negative: bool) -> String { use syntax::ast::IntTy::*; use syntax::ast::UintTy::*; macro_rules! find_fit { @@ -451,36 +425,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { match $ty { $($type => { $(if !negative && val <= uint_ty_range($utypes).1 { - return Some(ty::TyUint($utypes)) + return format!("Consider using `{:?}`", $utypes) })* $(if val <= int_ty_range($itypes).1 as u128 + _neg { - return Some(ty::TyInt($itypes)) + return format!("Consider using `{:?}`", $itypes) })* - None + String::new() },)* - _ => None + _ => String::new() } } } } - if let &ty::TyInt(i) = t { - return find_fit!(i, val, negative, - I8 => [U8] => [I16, I32, I64, I128], - I16 => [U16] => [I32, I64, I128], - I32 => [U32] => [I64, I128], - I64 => [U64] => [I128], - I128 => [U128] => []); - } - if let &ty::TyUint(u) = t { - return find_fit!(u, val, negative, - U8 => [U8, U16, U32, U64, U128] => [], - U16 => [U16, U32, U64, U128] => [], - U32 => [U32, U64, U128] => [], - U64 => [U64, U128] => [], - U128 => [U128] => []); + match t { + &ty::TyInt(i) => find_fit!(i, val, negative, + I8 => [U8] => [I16, I32, I64, I128], + I16 => [U16] => [I32, I64, I128], + I32 => [U32] => [I64, I128], + I64 => [U64] => [I128], + I128 => [U128] => []), + &ty::TyUint(u) => find_fit!(u, val, negative, + U8 => [U8, U16, U32, U64, U128] => [], + U16 => [U16, U32, U64, U128] => [], + U32 => [U32, U64, U128] => [], + U64 => [U64, U128] => [], + U128 => [U128] => []), + _ => String::new(), } - - None } } } From 5c706196449f679b163a8a5fbfb08d842db07e29 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 24 Feb 2018 16:40:51 +0100 Subject: [PATCH 3/6] Rewrite error reporting as requested --- src/librustc_lint/types.rs | 114 ++++++++++++++++---------- src/test/ui/lint/type-overflow.stderr | 30 +++---- 2 files changed, 82 insertions(+), 62 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index db9dfedc656ba..02aef271c37db 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -152,29 +152,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { // avoiding use of -min to prevent overflow/panic if (negative && v > max + 1) || (!negative && v > max) { if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - let bits = int_ty_bits(t, cx.sess().target.isize_ty); - let actually = - ((v << (128 - bits)) as i128) >> (128 - bits); - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t), - ); - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - an `{:?}` and will become `{}{:?}`.", - repr_str, v, t, actually, t - )); - let sugg_ty = get_type_suggestion( - &cx.tables.node_id_to_type(e.hir_id).sty, + report_bin_hex_error( + cx, + e, + ty::TyInt(t), + repr_str, v, negative, ); - if !sugg_ty.is_empty() { - err.help(&sugg_ty); - } - - err.emit(); return; } cx.span_lint( @@ -219,28 +204,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - let bits = uint_ty_bits(t, cx.sess().target.usize_ty); - let actually = (lit_val << (128 - bits)) >> (128 - bits); - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t), - ); - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - an `{:?}` and will become `{}{:?}`.", - repr_str, lit_val, t, actually, t - )); - let sugg_ty = get_type_suggestion( - &cx.tables.node_id_to_type(e.hir_id).sty, + report_bin_hex_error( + cx, + e, + ty::TyUint(t), + repr_str, lit_val, false, ); - if !sugg_ty.is_empty() { - err.help(&sugg_ty); - } - - err.emit(); return; } cx.span_lint( @@ -414,7 +385,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { // - `uX` => `uY` // // No suggestion for: `isize`, `usize`. - fn get_type_suggestion<'a>(t: &ty::TypeVariants, val: u128, negative: bool) -> String { + fn get_type_suggestion<'a>( + t: &ty::TypeVariants, + val: u128, + negative: bool, + ) -> Option { use syntax::ast::IntTy::*; use syntax::ast::UintTy::*; macro_rules! find_fit { @@ -425,14 +400,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { match $ty { $($type => { $(if !negative && val <= uint_ty_range($utypes).1 { - return format!("Consider using `{:?}`", $utypes) + return Some(format!("{:?}", $utypes)) })* $(if val <= int_ty_range($itypes).1 as u128 + _neg { - return format!("Consider using `{:?}`", $itypes) + return Some(format!("{:?}", $itypes)) })* - String::new() + None },)* - _ => String::new() + _ => None } } } @@ -450,8 +425,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { U32 => [U32, U64, U128] => [], U64 => [U64, U128] => [], U128 => [U128] => []), - _ => String::new(), + _ => None, + } + } + + fn report_bin_hex_error( + cx: &LateContext, + expr: &hir::Expr, + ty: ty::TypeVariants, + repr_str: String, + val: u128, + negative: bool, + ) { + let (t, actually) = match ty { + ty::TyInt(t) => { + let bits = int_ty_bits(t, cx.sess().target.isize_ty); + let actually = (val << (128 - bits)) as i128 >> (128 - bits); + (format!("{:?}", t), actually.to_string()) + } + ty::TyUint(t) => { + let bits = uint_ty_bits(t, cx.sess().target.usize_ty); + let actually = (val << (128 - bits)) >> (128 - bits); + (format!("{:?}", t), actually.to_string()) + } + _ => bug!(), + }; + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + expr.span, + &format!("literal out of range for {}", t), + ); + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + an `{}` and will become `{}{}`", + repr_str, val, t, actually, t + )); + if let Some(sugg_ty) = + get_type_suggestion(&cx.tables.node_id_to_type(expr.hir_id).sty, val, negative) + { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + err.span_suggestion( + expr.span, + &format!("consider using `{}` instead", sugg_ty), + format!("{}{}", sans_suffix, sugg_ty), + ); + } else { + err.help(&format!("consider using `{}` instead", sugg_ty)); + } } + + err.emit(); } } } diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 425f76da5cb4e..89718c7696a8d 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -10,28 +10,25 @@ warning: literal out of range for i8 --> $DIR/type-overflow.rs:21:16 | 21 | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8 - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider using `u8` instead: `0b1000_0001u8` | - = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8`. - = help: Consider using `u8` + = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8` warning: literal out of range for i64 --> $DIR/type-overflow.rs:23:16 | 23 | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64 - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x8000_0000_0000_0000u64` | - = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64`. - = help: Consider using `u64` + = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64` warning: literal out of range for u32 --> $DIR/type-overflow.rs:25:16 | 25 | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32 - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x1_FFFF_FFFFu64` | - = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32`. - = help: Consider using `u64` + = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32` warning: literal out of range for i128 --> $DIR/type-overflow.rs:27:22 @@ -39,8 +36,8 @@ warning: literal out of range for i128 27 | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128`. - = help: Consider using `u128` + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128` + = help: consider using `u128` instead warning: literal out of range for i32 --> $DIR/type-overflow.rs:30:16 @@ -48,8 +45,8 @@ warning: literal out of range for i32 30 | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 | ^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32`. - = help: Consider using `i128` + = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32` + = help: consider using `i128` instead warning: literal out of range for isize --> $DIR/type-overflow.rs:32:23 @@ -57,14 +54,13 @@ warning: literal out of range for isize 32 | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize | ^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize`. + = note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize` warning: literal out of range for i8 --> $DIR/type-overflow.rs:34:17 | 34 | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider using `i16` instead: `0b1111_1111i16` | - = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8`. - = help: Consider using `i16` + = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8` From f45f760f624b34a7fa70fe925303808d4713641a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 1 Mar 2018 01:49:36 +0100 Subject: [PATCH 4/6] Adapt stderr of UI test to PR #48449 --- src/test/ui/lint/type-overflow.stderr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 89718c7696a8d..4ede25b9d03e7 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -1,7 +1,7 @@ warning: literal out of range for i8 --> $DIR/type-overflow.rs:16:17 | -16 | let error = 255i8; //~WARNING literal out of range for i8 +LL | let error = 255i8; //~WARNING literal out of range for i8 | ^^^^^ | = note: #[warn(overflowing_literals)] on by default @@ -9,7 +9,7 @@ warning: literal out of range for i8 warning: literal out of range for i8 --> $DIR/type-overflow.rs:21:16 | -21 | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8 +LL | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8 | ^^^^^^^^^^^^^ help: consider using `u8` instead: `0b1000_0001u8` | = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8` @@ -17,7 +17,7 @@ warning: literal out of range for i8 warning: literal out of range for i64 --> $DIR/type-overflow.rs:23:16 | -23 | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64 +LL | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x8000_0000_0000_0000u64` | = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64` @@ -25,7 +25,7 @@ warning: literal out of range for i64 warning: literal out of range for u32 --> $DIR/type-overflow.rs:25:16 | -25 | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32 +LL | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32 | ^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x1_FFFF_FFFFu64` | = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32` @@ -33,7 +33,7 @@ warning: literal out of range for u32 warning: literal out of range for i128 --> $DIR/type-overflow.rs:27:22 | -27 | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; +LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128` @@ -42,7 +42,7 @@ warning: literal out of range for i128 warning: literal out of range for i32 --> $DIR/type-overflow.rs:30:16 | -30 | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 +LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 | ^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32` @@ -51,7 +51,7 @@ warning: literal out of range for i32 warning: literal out of range for isize --> $DIR/type-overflow.rs:32:23 | -32 | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize +LL | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize | ^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize` @@ -59,7 +59,7 @@ warning: literal out of range for isize warning: literal out of range for i8 --> $DIR/type-overflow.rs:34:17 | -34 | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 +LL | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 | ^^^^^^^^^^^^^ help: consider using `i16` instead: `0b1111_1111i16` | = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8` From 15ecf0694811cf9ab3273c14a97f6f05b22a9b5b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 2 Mar 2018 13:33:06 +0100 Subject: [PATCH 5/6] Remove isize test --- src/test/ui/lint/type-overflow.rs | 2 -- src/test/ui/lint/type-overflow.stderr | 10 +--------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/test/ui/lint/type-overflow.rs b/src/test/ui/lint/type-overflow.rs index e414f43b3ffd7..495989587e585 100644 --- a/src/test/ui/lint/type-overflow.rs +++ b/src/test/ui/lint/type-overflow.rs @@ -29,7 +29,5 @@ fn main() { let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32 - let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize - let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 } diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 4ede25b9d03e7..d3fcb1335e209 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -48,16 +48,8 @@ LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32` = help: consider using `i128` instead -warning: literal out of range for isize - --> $DIR/type-overflow.rs:32:23 - | -LL | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize` - warning: literal out of range for i8 - --> $DIR/type-overflow.rs:34:17 + --> $DIR/type-overflow.rs:32:17 | LL | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8 | ^^^^^^^^^^^^^ help: consider using `i16` instead: `0b1111_1111i16` From fc33b2567cac0ae453807f4118872ab81a16ddf7 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 3 Mar 2018 21:55:04 +0100 Subject: [PATCH 6/6] Improve getting literal representation --- src/librustc_lint/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 02aef271c37db..e778b2d386105 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -368,7 +368,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { let src = cx.sess().codemap().span_to_snippet(lit.span).ok()?; let firstch = src.chars().next()?; - if let Some(0) = char::to_digit(firstch, 10) { + if firstch == '0' { match src.chars().nth(1) { Some('x') | Some('b') => return Some(src), _ => return None,