Suggest type for overflowing bin/hex-literals

This commit is contained in:
flip1995 2018-02-22 15:53:22 +01:00
parent 0ff9872b22
commit e822e62ee8
No known key found for this signature in database
GPG Key ID: 6757AB26F72F0084
3 changed files with 274 additions and 25 deletions

View File

@ -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<String> {
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<ty::TypeVariants<'a>> {
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
}
}
}

View File

@ -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 <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.
// 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
}

View File

@ -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`