// 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;
}
}
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),
+ );
}
}
_ => (),
_ => 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
+ }
}
}
--- /dev/null
+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`
+