+// For `isize` & `usize`, be conservative with the warnings, so that the
+// warnings are consistent between 32- and 64-bit platforms.
+fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
+ match int_ty {
+ ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128),
+ ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128),
+ ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128),
+ ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128),
+ ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128),
+ ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()),
+ }
+}
+
+fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
+ match uint_ty {
+ ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128),
+ ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128),
+ ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128),
+ ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128),
+ ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128),
+ ast::UintTy::U128 => (u128::min_value(), u128::max_value()),
+ }
+}
+
+fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &ast::Lit) -> Option<String> {
+ let src = cx.sess().source_map().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
+}
+
+fn report_bin_hex_error(
+ cx: &LateContext<'_, '_>,
+ expr: &hir::Expr,
+ ty: attr::IntType,
+ repr_str: String,
+ val: u128,
+ negative: bool,
+) {
+ let size = layout::Integer::from_attr(&cx.tcx, ty).size();
+ let (t, actually) = match ty {
+ attr::IntType::SignedInt(t) => {
+ let actually = sign_extend(val, size) as i128;
+ (format!("{:?}", t), actually.to_string())
+ }
+ attr::IntType::UnsignedInt(t) => {
+ let actually = truncate(val, size);
+ (format!("{:?}", t), actually.to_string())
+ }
+ };
+ 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_type(expr.hir_id), 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),
+ Applicability::MachineApplicable
+ );
+ } else {
+ err.help(&format!("consider using `{}` instead", sugg_ty));
+ }
+ }
+
+ err.emit();
+}
+
+// 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<'_>,
+ 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.sty {
+ ty::Int(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::Uint(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 lint_int_literal<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ type_limits: &TypeLimits,
+ e: &'tcx hir::Expr,
+ lit: &ast::Lit,
+ t: ast::IntTy,
+ v: u128,
+) {
+ let int_type = if let ast::IntTy::Isize = t {
+ cx.sess().target.isize_ty
+ } else {
+ t
+ };
+
+ let (_, max) = int_ty_range(int_type);
+ let max = max as u128;
+ let negative = type_limits.negated_expr_id == e.hir_id;
+
+ // 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) {
+ if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
+ report_bin_hex_error(
+ cx,
+ e,
+ attr::IntType::SignedInt(t),
+ repr_str,
+ v,
+ negative,
+ );
+ return;
+ }
+
+ let par_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id);
+ if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(par_id) {
+ if let hir::ExprKind::Struct(..) = par_e.node {
+ if is_range_literal(cx.sess(), par_e)
+ && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t)
+ {
+ // The overflowing literal lint was overridden.
+ return;
+ }
+ }
+ }
+
+ cx.span_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ &format!("literal out of range for `{:?}`", t),
+ );
+ }
+}
+
+fn lint_uint_literal<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ e: &'tcx hir::Expr,
+ lit: &ast::Lit,
+ t: ast::UintTy,
+) {
+ let uint_type = if let ast::UintTy::Usize = t {
+ cx.sess().target.usize_ty
+ } else {
+ t
+ };
+ let (min, max) = uint_ty_range(uint_type);
+ let lit_val: u128 = match lit.node {
+ // _v is u8, within range by definition
+ ast::LitKind::Byte(_v) => return,
+ ast::LitKind::Int(v, _) => v,
+ _ => bug!(),
+ };
+ if lit_val < min || lit_val > max {
+ let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id);
+ if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(parent_id) {
+ match par_e.node {
+ hir::ExprKind::Cast(..) => {
+ if let ty::Char = cx.tables.expr_ty(par_e).sty {
+ let mut err = cx.struct_span_lint(
+ OVERFLOWING_LITERALS,
+ par_e.span,
+ "only `u8` can be cast into `char`",
+ );
+ err.span_suggestion(
+ par_e.span,
+ &"use a `char` literal instead",
+ format!("'\\u{{{:X}}}'", lit_val),
+ Applicability::MachineApplicable,
+ );
+ err.emit();
+ return;
+ }
+ }
+ hir::ExprKind::Struct(..)
+ if is_range_literal(cx.sess(), par_e) => {
+ if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
+ // The overflowing literal lint was overridden.
+ return;
+ }
+ }
+ _ => {}
+ }
+ }
+ if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
+ report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false);
+ return;
+ }
+ cx.span_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ &format!("literal out of range for `{:?}`", t),
+ );
+ }
+}
+
+fn lint_literal<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ type_limits: &TypeLimits,
+ e: &'tcx hir::Expr,
+ lit: &ast::Lit,
+) {
+ match cx.tables.node_type(e.hir_id).sty {
+ ty::Int(t) => {
+ match lit.node {
+ ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
+ ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => {
+ lint_int_literal(cx, type_limits, e, lit, t, v)
+ }
+ _ => bug!(),
+ };
+ }
+ ty::Uint(t) => {
+ lint_uint_literal(cx, e, lit, t)
+ }
+ ty::Float(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),
+ }
+ }
+ _ => bug!(),
+ };
+ if is_infinite == Ok(true) {
+ cx.span_lint(OVERFLOWING_LITERALS,
+ e.span,
+ &format!("literal out of range for `{:?}`", t));
+ }
+ }
+ _ => {}
+ }
+}
+