Suggest type for overflowing bin/hex-literals
This commit is contained in:
parent
0ff9872b22
commit
e822e62ee8
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/test/ui/lint/type-overflow.rs
Normal file
35
src/test/ui/lint/type-overflow.rs
Normal 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
|
||||
}
|
70
src/test/ui/lint/type-overflow.stderr
Normal file
70
src/test/ui/lint/type-overflow.stderr
Normal 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`
|
||||
|
Loading…
Reference in New Issue
Block a user