Rollup merge of #48432 - flip1995:lit_diag, r=oli-obk
Suggest type for overflowing bin/hex-literals Fixes #48073 For hexadecimal and binary literals, which overflow, it gives an additional note to the warning message, like in this [comment](https://github.com/rust-lang/rust/issues/48073#issuecomment-365370113). Additionally it will suggest a type (`X < Y`): - `iX`: if literal fits in `uX` => `uX`, else => `iY` - `-iX` => `iY` - `uX` => `uY` Exceptions: `isize`, `usize`. I don't think you can make a good suggestion here. The programmer has to figure it out on it's own in this case. r? @oli-obk
This commit is contained in:
commit
813ac2c36b
@ -150,11 +150,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
|
|||||||
|
|
||||||
// Detect literal value out of range [min, max] inclusive
|
// Detect literal value out of range [min, max] inclusive
|
||||||
// avoiding use of -min to prevent overflow/panic
|
// avoiding use of -min to prevent overflow/panic
|
||||||
if (negative && v > max + 1) ||
|
if (negative && v > max + 1) || (!negative && v > max) {
|
||||||
(!negative && v > max) {
|
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
|
||||||
cx.span_lint(OVERFLOWING_LITERALS,
|
report_bin_hex_error(
|
||||||
e.span,
|
cx,
|
||||||
&format!("literal out of range for {:?}", t));
|
e,
|
||||||
|
ty::TyInt(t),
|
||||||
|
repr_str,
|
||||||
|
v,
|
||||||
|
negative,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cx.span_lint(
|
||||||
|
OVERFLOWING_LITERALS,
|
||||||
|
e.span,
|
||||||
|
&format!("literal out of range for {:?}", t),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,9 +203,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cx.span_lint(OVERFLOWING_LITERALS,
|
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
|
||||||
e.span,
|
report_bin_hex_error(
|
||||||
&format!("literal out of range for {:?}", t));
|
cx,
|
||||||
|
e,
|
||||||
|
ty::TyUint(t),
|
||||||
|
repr_str,
|
||||||
|
lit_val,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cx.span_lint(
|
||||||
|
OVERFLOWING_LITERALS,
|
||||||
|
e.span,
|
||||||
|
&format!("literal out of range for {:?}", t),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::TyFloat(t) => {
|
ty::TyFloat(t) => {
|
||||||
@ -338,6 +363,120 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option<String> {
|
||||||
|
let src = cx.sess().codemap().span_to_snippet(lit.span).ok()?;
|
||||||
|
let firstch = src.chars().next()?;
|
||||||
|
|
||||||
|
if firstch == '0' {
|
||||||
|
match src.chars().nth(1) {
|
||||||
|
Some('x') | Some('b') => return Some(src),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
) -> Option<String> {
|
||||||
|
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(format!("{:?}", $utypes))
|
||||||
|
})*
|
||||||
|
$(if val <= int_ty_range($itypes).1 as u128 + _neg {
|
||||||
|
return Some(format!("{:?}", $itypes))
|
||||||
|
})*
|
||||||
|
None
|
||||||
|
},)*
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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] => []),
|
||||||
|
_ => 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
src/test/ui/lint/type-overflow.rs
Normal file
33
src/test/ui/lint/type-overflow.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 = -0b1111_1111i8; //~WARNING literal out of range for i8
|
||||||
|
}
|
58
src/test/ui/lint/type-overflow.stderr
Normal file
58
src/test/ui/lint/type-overflow.stderr
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
warning: literal out of range for i8
|
||||||
|
--> $DIR/type-overflow.rs:16:17
|
||||||
|
|
|
||||||
|
LL | 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
|
||||||
|
|
|
||||||
|
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`
|
||||||
|
|
||||||
|
warning: literal out of range for i64
|
||||||
|
--> $DIR/type-overflow.rs:23:16
|
||||||
|
|
|
||||||
|
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`
|
||||||
|
|
||||||
|
warning: literal out of range for u32
|
||||||
|
--> $DIR/type-overflow.rs:25:16
|
||||||
|
|
|
||||||
|
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`
|
||||||
|
|
||||||
|
warning: literal out of range for i128
|
||||||
|
--> $DIR/type-overflow.rs:27:22
|
||||||
|
|
|
||||||
|
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`
|
||||||
|
= help: consider using `u128` instead
|
||||||
|
|
||||||
|
warning: literal out of range for i32
|
||||||
|
--> $DIR/type-overflow.rs:30:16
|
||||||
|
|
|
||||||
|
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`
|
||||||
|
= help: consider using `i128` instead
|
||||||
|
|
||||||
|
warning: literal out of range for i8
|
||||||
|
--> $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`
|
||||||
|
|
|
||||||
|
= note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8`
|
||||||
|
|
Loading…
Reference in New Issue
Block a user