Skip to content

Commit 09e45bd

Browse files
authored
Rollup merge of rust-lang#48432 - flip1995:lit_diag, r=oli-obk
Suggest type for overflowing bin/hex-literals Fixes rust-lang#48073 For hexadecimal and binary literals, which overflow, it gives an additional note to the warning message, like in this [comment](rust-lang#48073 (comment)). 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
2 parents e2746d8 + fc33b25 commit 09e45bd

File tree

3 files changed

+238
-8
lines changed

3 files changed

+238
-8
lines changed

src/librustc_lint/types.rs

+147-8
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
150150

151151
// Detect literal value out of range [min, max] inclusive
152152
// avoiding use of -min to prevent overflow/panic
153-
if (negative && v > max + 1) ||
154-
(!negative && v > max) {
155-
cx.span_lint(OVERFLOWING_LITERALS,
156-
e.span,
157-
&format!("literal out of range for {:?}", t));
153+
if (negative && v > max + 1) || (!negative && v > max) {
154+
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
155+
report_bin_hex_error(
156+
cx,
157+
e,
158+
ty::TyInt(t),
159+
repr_str,
160+
v,
161+
negative,
162+
);
163+
return;
164+
}
165+
cx.span_lint(
166+
OVERFLOWING_LITERALS,
167+
e.span,
168+
&format!("literal out of range for {:?}", t),
169+
);
158170
return;
159171
}
160172
}
@@ -191,9 +203,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
191203
}
192204
}
193205
}
194-
cx.span_lint(OVERFLOWING_LITERALS,
195-
e.span,
196-
&format!("literal out of range for {:?}", t));
206+
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
207+
report_bin_hex_error(
208+
cx,
209+
e,
210+
ty::TyUint(t),
211+
repr_str,
212+
lit_val,
213+
false,
214+
);
215+
return;
216+
}
217+
cx.span_lint(
218+
OVERFLOWING_LITERALS,
219+
e.span,
220+
&format!("literal out of range for {:?}", t),
221+
);
197222
}
198223
}
199224
ty::TyFloat(t) => {
@@ -338,6 +363,120 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
338363
_ => false,
339364
}
340365
}
366+
367+
fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option<String> {
368+
let src = cx.sess().codemap().span_to_snippet(lit.span).ok()?;
369+
let firstch = src.chars().next()?;
370+
371+
if firstch == '0' {
372+
match src.chars().nth(1) {
373+
Some('x') | Some('b') => return Some(src),
374+
_ => return None,
375+
}
376+
}
377+
378+
None
379+
}
380+
381+
// This function finds the next fitting type and generates a suggestion string.
382+
// It searches for fitting types in the following way (`X < Y`):
383+
// - `iX`: if literal fits in `uX` => `uX`, else => `iY`
384+
// - `-iX` => `iY`
385+
// - `uX` => `uY`
386+
//
387+
// No suggestion for: `isize`, `usize`.
388+
fn get_type_suggestion<'a>(
389+
t: &ty::TypeVariants,
390+
val: u128,
391+
negative: bool,
392+
) -> Option<String> {
393+
use syntax::ast::IntTy::*;
394+
use syntax::ast::UintTy::*;
395+
macro_rules! find_fit {
396+
($ty:expr, $val:expr, $negative:expr,
397+
$($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
398+
{
399+
let _neg = if negative { 1 } else { 0 };
400+
match $ty {
401+
$($type => {
402+
$(if !negative && val <= uint_ty_range($utypes).1 {
403+
return Some(format!("{:?}", $utypes))
404+
})*
405+
$(if val <= int_ty_range($itypes).1 as u128 + _neg {
406+
return Some(format!("{:?}", $itypes))
407+
})*
408+
None
409+
},)*
410+
_ => None
411+
}
412+
}
413+
}
414+
}
415+
match t {
416+
&ty::TyInt(i) => find_fit!(i, val, negative,
417+
I8 => [U8] => [I16, I32, I64, I128],
418+
I16 => [U16] => [I32, I64, I128],
419+
I32 => [U32] => [I64, I128],
420+
I64 => [U64] => [I128],
421+
I128 => [U128] => []),
422+
&ty::TyUint(u) => find_fit!(u, val, negative,
423+
U8 => [U8, U16, U32, U64, U128] => [],
424+
U16 => [U16, U32, U64, U128] => [],
425+
U32 => [U32, U64, U128] => [],
426+
U64 => [U64, U128] => [],
427+
U128 => [U128] => []),
428+
_ => None,
429+
}
430+
}
431+
432+
fn report_bin_hex_error(
433+
cx: &LateContext,
434+
expr: &hir::Expr,
435+
ty: ty::TypeVariants,
436+
repr_str: String,
437+
val: u128,
438+
negative: bool,
439+
) {
440+
let (t, actually) = match ty {
441+
ty::TyInt(t) => {
442+
let bits = int_ty_bits(t, cx.sess().target.isize_ty);
443+
let actually = (val << (128 - bits)) as i128 >> (128 - bits);
444+
(format!("{:?}", t), actually.to_string())
445+
}
446+
ty::TyUint(t) => {
447+
let bits = uint_ty_bits(t, cx.sess().target.usize_ty);
448+
let actually = (val << (128 - bits)) >> (128 - bits);
449+
(format!("{:?}", t), actually.to_string())
450+
}
451+
_ => bug!(),
452+
};
453+
let mut err = cx.struct_span_lint(
454+
OVERFLOWING_LITERALS,
455+
expr.span,
456+
&format!("literal out of range for {}", t),
457+
);
458+
err.note(&format!(
459+
"the literal `{}` (decimal `{}`) does not fit into \
460+
an `{}` and will become `{}{}`",
461+
repr_str, val, t, actually, t
462+
));
463+
if let Some(sugg_ty) =
464+
get_type_suggestion(&cx.tables.node_id_to_type(expr.hir_id).sty, val, negative)
465+
{
466+
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
467+
let (sans_suffix, _) = repr_str.split_at(pos);
468+
err.span_suggestion(
469+
expr.span,
470+
&format!("consider using `{}` instead", sugg_ty),
471+
format!("{}{}", sans_suffix, sugg_ty),
472+
);
473+
} else {
474+
err.help(&format!("consider using `{}` instead", sugg_ty));
475+
}
476+
}
477+
478+
err.emit();
479+
}
341480
}
342481
}
343482

src/test/ui/lint/type-overflow.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// must-compile-successfully
12+
13+
#![feature(i128_type)]
14+
15+
fn main() {
16+
let error = 255i8; //~WARNING literal out of range for i8
17+
18+
let ok = 0b1000_0001; // should be ok -> i32
19+
let ok = 0b0111_1111i8; // should be ok -> 127i8
20+
21+
let fail = 0b1000_0001i8; //~WARNING literal out of range for i8
22+
23+
let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64
24+
25+
let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32
26+
27+
let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
28+
//~^ WARNING literal out of range for i128
29+
30+
let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32
31+
32+
let fail = -0b1111_1111i8; //~WARNING literal out of range for i8
33+
}

src/test/ui/lint/type-overflow.stderr

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
warning: literal out of range for i8
2+
--> $DIR/type-overflow.rs:16:17
3+
|
4+
LL | let error = 255i8; //~WARNING literal out of range for i8
5+
| ^^^^^
6+
|
7+
= note: #[warn(overflowing_literals)] on by default
8+
9+
warning: literal out of range for i8
10+
--> $DIR/type-overflow.rs:21:16
11+
|
12+
LL | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8
13+
| ^^^^^^^^^^^^^ help: consider using `u8` instead: `0b1000_0001u8`
14+
|
15+
= note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8`
16+
17+
warning: literal out of range for i64
18+
--> $DIR/type-overflow.rs:23:16
19+
|
20+
LL | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x8000_0000_0000_0000u64`
22+
|
23+
= note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64`
24+
25+
warning: literal out of range for u32
26+
--> $DIR/type-overflow.rs:25:16
27+
|
28+
LL | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32
29+
| ^^^^^^^^^^^^^^^^ help: consider using `u64` instead: `0x1_FFFF_FFFFu64`
30+
|
31+
= note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32`
32+
33+
warning: literal out of range for i128
34+
--> $DIR/type-overflow.rs:27:22
35+
|
36+
LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
|
39+
= note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128`
40+
= help: consider using `u128` instead
41+
42+
warning: literal out of range for i32
43+
--> $DIR/type-overflow.rs:30:16
44+
|
45+
LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32
46+
| ^^^^^^^^^^^^^^^^^^^^^
47+
|
48+
= note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32`
49+
= help: consider using `i128` instead
50+
51+
warning: literal out of range for i8
52+
--> $DIR/type-overflow.rs:32:17
53+
|
54+
LL | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8
55+
| ^^^^^^^^^^^^^ help: consider using `i16` instead: `0b1111_1111i16`
56+
|
57+
= note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8`
58+

0 commit comments

Comments
 (0)