Skip to content

Commit e822e62

Browse files
committed
Suggest type for overflowing bin/hex-literals
1 parent 0ff9872 commit e822e62

File tree

3 files changed

+274
-25
lines changed

3 files changed

+274
-25
lines changed

src/librustc_lint/types.rs

Lines changed: 169 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,52 @@ 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+
let bits = int_ty_bits(t, cx.sess().target.isize_ty);
156+
let mut actually = v as i128;
157+
if bits < 128 {
158+
// v & 0b0..01..1, |1| = bits
159+
let trimmed = v & ((1 << bits) - 1);
160+
actually = if v & (1 << (bits - 1)) == 0 {
161+
// positive
162+
trimmed as i128
163+
} else {
164+
// negative -> two's complement
165+
(((-1 as i128 as u128) << bits) | trimmed) as i128
166+
};
167+
}
168+
let mut err = cx.struct_span_lint(
169+
OVERFLOWING_LITERALS,
170+
e.span,
171+
&format!("literal out of range for {:?}", t),
172+
);
173+
err.note(&format!(
174+
"the literal `{}` (decimal `{}`) does not fit into \
175+
an `{:?}` and will become `{}{:?}`.",
176+
repr_str, v, t, actually, t
177+
));
178+
let sugg_ty = get_fitting_type(
179+
&cx.tables.node_id_to_type(e.hir_id).sty,
180+
v,
181+
negative,
182+
).map_or(String::new(), |ty| match ty {
183+
ty::TyUint(t) => format!("Consider using `{:?}`", t),
184+
ty::TyInt(t) => format!("Consider using `{:?}`", t),
185+
_ => String::new(),
186+
});
187+
if !sugg_ty.is_empty() {
188+
err.help(&sugg_ty);
189+
}
190+
191+
err.emit();
192+
return;
193+
}
194+
cx.span_lint(
195+
OVERFLOWING_LITERALS,
196+
e.span,
197+
&format!("literal out of range for {:?}", t),
198+
);
158199
return;
159200
}
160201
}
@@ -180,37 +221,77 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
180221
if let hir::ExprCast(..) = parent_expr.node {
181222
if let ty::TyChar = cx.tables.expr_ty(parent_expr).sty {
182223
let mut err = cx.struct_span_lint(
183-
OVERFLOWING_LITERALS,
184-
parent_expr.span,
185-
"only u8 can be casted into char");
186-
err.span_suggestion(parent_expr.span,
187-
&"use a char literal instead",
188-
format!("'\\u{{{:X}}}'", lit_val));
224+
OVERFLOWING_LITERALS,
225+
parent_expr.span,
226+
"only u8 can be casted into char",
227+
);
228+
err.span_suggestion(
229+
parent_expr.span,
230+
&"use a char literal instead",
231+
format!("'\\u{{{:X}}}'", lit_val),
232+
);
189233
err.emit();
190-
return
234+
return;
191235
}
192236
}
193237
}
194-
cx.span_lint(OVERFLOWING_LITERALS,
195-
e.span,
196-
&format!("literal out of range for {:?}", t));
238+
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
239+
let bits = uint_ty_bits(t, cx.sess().target.usize_ty);
240+
// u128 cannot be greater than max -> compiler error
241+
let actually = lit_val & ((1 << bits) - 1);
242+
let mut err = cx.struct_span_lint(
243+
OVERFLOWING_LITERALS,
244+
e.span,
245+
&format!("literal out of range for {:?}", t),
246+
);
247+
err.note(&format!(
248+
"the literal `{}` (decimal `{}`) does not fit into \
249+
an `{:?}` and will become `{}{:?}`.",
250+
repr_str, lit_val, t, actually, t
251+
));
252+
let sugg_ty = get_fitting_type(
253+
&cx.tables.node_id_to_type(e.hir_id).sty,
254+
lit_val,
255+
false,
256+
).map_or(
257+
String::new(),
258+
|ty| {
259+
if let ty::TyUint(t) = ty {
260+
format!("Consider using `{:?}`", t)
261+
} else {
262+
String::new()
263+
}
264+
},
265+
);
266+
if !sugg_ty.is_empty() {
267+
err.help(&sugg_ty);
268+
}
269+
270+
err.emit();
271+
return;
272+
}
273+
cx.span_lint(
274+
OVERFLOWING_LITERALS,
275+
e.span,
276+
&format!("literal out of range for {:?}", t),
277+
);
197278
}
198279
}
199280
ty::TyFloat(t) => {
200281
let is_infinite = match lit.node {
201-
ast::LitKind::Float(v, _) |
202-
ast::LitKind::FloatUnsuffixed(v) => {
203-
match t {
204-
ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
205-
ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
206-
}
207-
}
282+
ast::LitKind::Float(v, _) | ast::LitKind::FloatUnsuffixed(v) => match t
283+
{
284+
ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
285+
ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
286+
},
208287
_ => bug!(),
209288
};
210289
if is_infinite == Ok(true) {
211-
cx.span_lint(OVERFLOWING_LITERALS,
212-
e.span,
213-
&format!("literal out of range for {:?}", t));
290+
cx.span_lint(
291+
OVERFLOWING_LITERALS,
292+
e.span,
293+
&format!("literal out of range for {:?}", t),
294+
);
214295
}
215296
}
216297
_ => (),
@@ -338,6 +419,69 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
338419
_ => false,
339420
}
340421
}
422+
423+
fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option<String> {
424+
if let Some(src) = cx.sess().codemap().span_to_snippet(lit.span).ok() {
425+
if let Some(firstch) = src.chars().next() {
426+
if let Some(0) = char::to_digit(firstch, 10) {
427+
if let Some(base) = src.chars().nth(1) {
428+
if base == 'x' || base == 'b' {
429+
return Some(src);
430+
}
431+
}
432+
}
433+
}
434+
}
435+
436+
None
437+
}
438+
439+
fn get_fitting_type<'a>(
440+
t: &ty::TypeVariants,
441+
val: u128,
442+
negative: bool,
443+
) -> Option<ty::TypeVariants<'a>> {
444+
use syntax::ast::IntTy::*;
445+
use syntax::ast::UintTy::*;
446+
macro_rules! find_fit {
447+
($ty:expr, $val:expr, $negative:expr,
448+
$($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
449+
{
450+
let _neg = if negative { 1 } else { 0 };
451+
match $ty {
452+
$($type => {
453+
$(if !negative && val <= uint_ty_range($utypes).1 {
454+
return Some(ty::TyUint($utypes))
455+
})*
456+
$(if val <= int_ty_range($itypes).1 as u128 + _neg {
457+
return Some(ty::TyInt($itypes))
458+
})*
459+
None
460+
},)*
461+
_ => None
462+
}
463+
}
464+
}
465+
}
466+
if let &ty::TyInt(i) = t {
467+
return find_fit!(i, val, negative,
468+
I8 => [U8] => [I16, I32, I64, I128],
469+
I16 => [U16] => [I32, I64, I128],
470+
I32 => [U32] => [I64, I128],
471+
I64 => [U64] => [I128],
472+
I128 => [U128] => []);
473+
}
474+
if let &ty::TyUint(u) = t {
475+
return find_fit!(u, val, negative,
476+
U8 => [U8, U16, U32, U64, U128] => [],
477+
U16 => [U16, U32, U64, U128] => [],
478+
U32 => [U32, U64, U128] => [],
479+
U64 => [U64, U128] => [],
480+
U128 => [U128] => []);
481+
}
482+
483+
None
484+
}
341485
}
342486
}
343487

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize
33+
34+
let fail = -0b1111_1111i8; //~WARNING literal out of range for i8
35+
}

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
warning: literal out of range for i8
2+
--> $DIR/type-overflow.rs:16:17
3+
|
4+
16 | 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+
21 | let fail = 0b1000_0001i8; //~WARNING literal out of range for i8
13+
| ^^^^^^^^^^^^^
14+
|
15+
= note: the literal `0b1000_0001i8` (decimal `129`) does not fit into an `i8` and will become `-127i8`.
16+
= help: Consider using `u8`
17+
18+
warning: literal out of range for i64
19+
--> $DIR/type-overflow.rs:23:16
20+
|
21+
23 | let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into an `i64` and will become `-9223372036854775808i64`.
25+
= help: Consider using `u64`
26+
27+
warning: literal out of range for u32
28+
--> $DIR/type-overflow.rs:25:16
29+
|
30+
25 | let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32
31+
| ^^^^^^^^^^^^^^^^
32+
|
33+
= note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into an `u32` and will become `4294967295u32`.
34+
= help: Consider using `u64`
35+
36+
warning: literal out of range for i128
37+
--> $DIR/type-overflow.rs:27:22
38+
|
39+
27 | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
|
42+
= note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into an `i128` and will become `-170141183460469231731687303715884105728i128`.
43+
= help: Consider using `u128`
44+
45+
warning: literal out of range for i32
46+
--> $DIR/type-overflow.rs:30:16
47+
|
48+
30 | let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32
49+
| ^^^^^^^^^^^^^^^^^^^^^
50+
|
51+
= note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into an `i32` and will become `-2i32`.
52+
= help: Consider using `i128`
53+
54+
warning: literal out of range for isize
55+
--> $DIR/type-overflow.rs:32:23
56+
|
57+
32 | let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize
58+
| ^^^^^^^^^^^^^^^^^^^^^
59+
|
60+
= note: the literal `0x8000_0000_0000_0000` (decimal `9223372036854775808`) does not fit into an `isize` and will become `-9223372036854775808isize`.
61+
62+
warning: literal out of range for i8
63+
--> $DIR/type-overflow.rs:34:17
64+
|
65+
34 | let fail = -0b1111_1111i8; //~WARNING literal out of range for i8
66+
| ^^^^^^^^^^^^^
67+
|
68+
= note: the literal `0b1111_1111i8` (decimal `255`) does not fit into an `i8` and will become `-1i8`.
69+
= help: Consider using `i16`
70+

0 commit comments

Comments
 (0)