diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index df455a725c5ba..0366d1e5c24e9 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5400,3 +5400,65 @@ fn body_ids(bodies: &BTreeMap) -> Vec { body_ids.sort_by_key(|b| bodies[b].value.span); body_ids } + +/// Checks if the specified expression is a built-in range literal. +/// (See: `LoweringContext::lower_expr()`). +pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { + use hir::{Path, QPath, ExprKind, TyKind}; + + // Returns whether the given path represents a (desugared) range, + // either in std or core, i.e. has either a `::std::ops::Range` or + // `::core::ops::Range` prefix. + fn is_range_path(path: &Path) -> bool { + let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.as_str().to_string()).collect(); + let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); + + // "{{root}}" is the equivalent of `::` prefix in `Path`. + if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { + (*std_core == "std" || *std_core == "core") && range.starts_with("Range") + } else { + false + } + }; + + // Check whether a span corresponding to a range expression is a + // range literal, rather than an explicit struct or `new()` call. + fn is_lit(sess: &Session, span: &Span) -> bool { + let source_map = sess.source_map(); + let end_point = source_map.end_point(*span); + + if let Ok(end_string) = source_map.span_to_snippet(end_point) { + !(end_string.ends_with("}") || end_string.ends_with(")")) + } else { + false + } + }; + + match expr.node { + // All built-in range literals but `..=` and `..` desugar to `Struct`s. + ExprKind::Struct(ref qpath, _, _) => { + if let QPath::Resolved(None, ref path) = **qpath { + return is_range_path(&path) && is_lit(sess, &expr.span); + } + } + + // `..` desugars to its struct path. + ExprKind::Path(QPath::Resolved(None, ref path)) => { + return is_range_path(&path) && is_lit(sess, &expr.span); + } + + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Call(ref func, _) => { + if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node { + if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node { + let new_call = segment.ident.as_str() == "new"; + return is_range_path(&path) && is_lit(sess, &expr.span) && new_call; + } + } + } + + _ => {} + } + + false +} diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index d3223c6edb809..f4ebfd79fe1db 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] -use rustc::hir::Node; +use rustc::hir::{ExprKind, Node}; +use rustc::hir::lowering::is_range_literal; use rustc::ty::subst::SubstsRef; use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx}; @@ -57,6 +58,347 @@ impl TypeLimits { } } +/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint. +/// Returns `true` iff the lint was overridden. +fn lint_overflowing_range_endpoint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + lit: &ast::Lit, + lit_val: u128, + max: u128, + expr: &'tcx hir::Expr, + parent_expr: &'tcx hir::Expr, + ty: impl std::fmt::Debug, +) -> bool { + // We only want to handle exclusive (`..`) ranges, + // which are represented as `ExprKind::Struct`. + if let ExprKind::Struct(_, eps, _) = &parent_expr.node { + debug_assert_eq!(eps.len(), 2); + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + parent_expr.span, + &format!("range endpoint is out of range for `{:?}`", ty), + ); + if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { + use ast::{LitKind, LitIntType}; + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s), + LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s), + LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), + _ => bug!(), + }; + let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); + err.span_suggestion( + parent_expr.span, + &"use an inclusive range instead", + suggestion, + Applicability::MachineApplicable, + ); + err.emit(); + return true; + } + } + } + + false +} + +// 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 { + 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 { + 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)); + } + } + _ => {} + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { match e.node { @@ -73,118 +415,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { "comparison is useless due to type limits"); } } - hir::ExprKind::Lit(ref 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) => { - 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 = self.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; - } - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for {:?}", t), - ); - return; - } - } - _ => bug!(), - }; - } - ty::Uint(t) => { - 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(parent_expr) = cx.tcx.hir().get_by_hir_id(parent_id) { - if let hir::ExprKind::Cast(..) = parent_expr.node { - if let ty::Char = cx.tables.expr_ty(parent_expr).sty { - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - "only u8 can be cast into char"); - err.span_suggestion( - parent_expr.span, - &"use a char literal instead", - format!("'\\u{{{:X}}}'", lit_val), - Applicability::MachineApplicable - ); - err.emit(); - 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), - ); - } - } - 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)); - } - } - _ => (), - }; - } - _ => (), + hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), + _ => {} }; fn is_valid(binop: hir::BinOp, v: T, min: T, max: T) -> bool { @@ -209,30 +441,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { }) } - // 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 check_limits(cx: &LateContext<'_, '_>, binop: hir::BinOp, l: &hir::Expr, @@ -289,119 +497,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { _ => false, } } - - fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &ast::Lit) -> Option { - 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 - } - - // 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 { - 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 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(); - } } } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 689996ccb25a9..8ae66a96c763a 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -7,7 +7,7 @@ use syntax_pos::Span; use rustc::hir; use rustc::hir::def::Def; use rustc::hir::Node; -use rustc::hir::print; +use rustc::hir::{print, lowering::is_range_literal}; use rustc::ty::{self, Ty, AssociatedItem}; use rustc::ty::adjustment::AllowTwoPhase; use errors::{Applicability, DiagnosticBuilder}; @@ -380,7 +380,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, // parenthesize borrows of range literals (Issue #54505) - _ if self.is_range_literal(expr) => true, + _ if is_range_literal(self.tcx.sess, expr) => true, _ => false, }; let sugg_expr = if needs_parens { @@ -479,70 +479,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { None } - /// This function checks if the specified expression is a built-in range literal. - /// (See: `LoweringContext::lower_expr()` in `src/librustc/hir/lowering.rs`). - fn is_range_literal(&self, expr: &hir::Expr) -> bool { - use hir::{Path, QPath, ExprKind, TyKind}; - - // We support `::std::ops::Range` and `::core::ops::Range` prefixes - let is_range_path = |path: &Path| { - let mut segs = path.segments.iter() - .map(|seg| seg.ident.as_str()); - - if let (Some(root), Some(std_core), Some(ops), Some(range), None) = - (segs.next(), segs.next(), segs.next(), segs.next(), segs.next()) - { - // "{{root}}" is the equivalent of `::` prefix in Path - root == "{{root}}" && (std_core == "std" || std_core == "core") - && ops == "ops" && range.starts_with("Range") - } else { - false - } - }; - - let span_is_range_literal = |span: &Span| { - // Check whether a span corresponding to a range expression - // is a range literal, rather than an explicit struct or `new()` call. - let source_map = self.tcx.sess.source_map(); - let end_point = source_map.end_point(*span); - - if let Ok(end_string) = source_map.span_to_snippet(end_point) { - !(end_string.ends_with("}") || end_string.ends_with(")")) - } else { - false - } - }; - - match expr.node { - // All built-in range literals but `..=` and `..` desugar to Structs - ExprKind::Struct(ref qpath, _, _) => { - if let QPath::Resolved(None, ref path) = **qpath { - return is_range_path(&path) && span_is_range_literal(&expr.span); - } - } - // `..` desugars to its struct path - ExprKind::Path(QPath::Resolved(None, ref path)) => { - return is_range_path(&path) && span_is_range_literal(&expr.span); - } - - // `..=` desugars into `::std::ops::RangeInclusive::new(...)` - ExprKind::Call(ref func, _) => { - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node { - if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node { - let call_to_new = segment.ident.as_str() == "new"; - - return is_range_path(&path) && span_is_range_literal(&expr.span) - && call_to_new; - } - } - } - - _ => {} - } - - false - } - pub fn check_for_cast( &self, err: &mut DiagnosticBuilder<'tcx>, diff --git a/src/test/ui/cast_char.rs b/src/test/ui/cast_char.rs index 8c319af9c96e7..9634ed56f7b72 100644 --- a/src/test/ui/cast_char.rs +++ b/src/test/ui/cast_char.rs @@ -2,9 +2,9 @@ fn main() { const XYZ: char = 0x1F888 as char; - //~^ ERROR only u8 can be cast into char + //~^ ERROR only `u8` can be cast into `char` const XY: char = 129160 as char; - //~^ ERROR only u8 can be cast into char + //~^ ERROR only `u8` can be cast into `char` const ZYX: char = '\u{01F888}'; println!("{}", XYZ); } diff --git a/src/test/ui/cast_char.stderr b/src/test/ui/cast_char.stderr index f0c9b8988903b..37ef98bcb5067 100644 --- a/src/test/ui/cast_char.stderr +++ b/src/test/ui/cast_char.stderr @@ -1,8 +1,8 @@ -error: only u8 can be cast into char +error: only `u8` can be cast into `char` --> $DIR/cast_char.rs:4:23 | LL | const XYZ: char = 0x1F888 as char; - | ^^^^^^^^^^^^^^^ help: use a char literal instead: `'\u{1F888}'` + | ^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` | note: lint level defined here --> $DIR/cast_char.rs:1:9 @@ -10,11 +10,11 @@ note: lint level defined here LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: only u8 can be cast into char +error: only `u8` can be cast into `char` --> $DIR/cast_char.rs:6:22 | LL | const XY: char = 129160 as char; - | ^^^^^^^^^^^^^^ help: use a char literal instead: `'\u{1F888}'` + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` error: aborting due to 2 previous errors diff --git a/src/test/ui/enum/enum-discrim-too-small2.rs b/src/test/ui/enum/enum-discrim-too-small2.rs index af60564302599..85cd73d6f0855 100644 --- a/src/test/ui/enum/enum-discrim-too-small2.rs +++ b/src/test/ui/enum/enum-discrim-too-small2.rs @@ -5,28 +5,28 @@ enum Ei8 { Ai8 = 23, Bi8 = -23, - Ci8 = 223, //~ ERROR literal out of range for i8 + Ci8 = 223, //~ ERROR literal out of range for `i8` } #[repr(i16)] enum Ei16 { Ai16 = 23, Bi16 = -22333, - Ci16 = 55555, //~ ERROR literal out of range for i16 + Ci16 = 55555, //~ ERROR literal out of range for `i16` } #[repr(i32)] enum Ei32 { Ai32 = 23, Bi32 = -2_000_000_000, - Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32 + Ci32 = 3_000_000_000, //~ ERROR literal out of range for `i32` } #[repr(i64)] enum Ei64 { Ai64 = 23, Bi64 = -9223372036854775808, - Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64 + Ci64 = 9223372036854775809, //~ ERROR literal out of range for `i64` } // u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a diff --git a/src/test/ui/enum/enum-discrim-too-small2.stderr b/src/test/ui/enum/enum-discrim-too-small2.stderr index 6340f5a856ed3..f7220044ba42d 100644 --- a/src/test/ui/enum/enum-discrim-too-small2.stderr +++ b/src/test/ui/enum/enum-discrim-too-small2.stderr @@ -1,4 +1,4 @@ -error: literal out of range for i8 +error: literal out of range for `i8` --> $DIR/enum-discrim-too-small2.rs:8:11 | LL | Ci8 = 223, @@ -10,19 +10,19 @@ note: lint level defined here LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i16 +error: literal out of range for `i16` --> $DIR/enum-discrim-too-small2.rs:15:12 | LL | Ci16 = 55555, | ^^^^^ -error: literal out of range for i32 +error: literal out of range for `i32` --> $DIR/enum-discrim-too-small2.rs:22:12 | LL | Ci32 = 3_000_000_000, | ^^^^^^^^^^^^^ -error: literal out of range for i64 +error: literal out of range for `i64` --> $DIR/enum-discrim-too-small2.rs:29:12 | LL | Ci64 = 9223372036854775809, diff --git a/src/test/ui/lint/deny-overflowing-literals.rs b/src/test/ui/lint/deny-overflowing-literals.rs index ebd6654d39b1f..b887f66e94bd6 100644 --- a/src/test/ui/lint/deny-overflowing-literals.rs +++ b/src/test/ui/lint/deny-overflowing-literals.rs @@ -1,4 +1,4 @@ fn main() { let x: u8 = 256; - //~^ error: literal out of range for u8 + //~^ error: literal out of range for `u8` } diff --git a/src/test/ui/lint/deny-overflowing-literals.stderr b/src/test/ui/lint/deny-overflowing-literals.stderr index 7313dd0bfb5a7..1263a7bb7fd1b 100644 --- a/src/test/ui/lint/deny-overflowing-literals.stderr +++ b/src/test/ui/lint/deny-overflowing-literals.stderr @@ -1,4 +1,4 @@ -error: literal out of range for u8 +error: literal out of range for `u8` --> $DIR/deny-overflowing-literals.rs:2:17 | LL | let x: u8 = 256; diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.rs b/src/test/ui/lint/lint-range-endpoint-overflow.rs new file mode 100644 index 0000000000000..7034d56aa5d83 --- /dev/null +++ b/src/test/ui/lint/lint-range-endpoint-overflow.rs @@ -0,0 +1,17 @@ +#![deny(overflowing_literals)] + +fn main() { + let range_a = 0..256; //~ ERROR range endpoint is out of range for `u8` + let range_b = 0..=255; // ok + let range_c = 0..=256; //~ ERROR literal out of range for `u8` + let range_d = 256..5; //~ ERROR literal out of range for `u8` + let range_e = 0..257; //~ ERROR literal out of range for `u8` + let _range_f = 0..256u8; //~ ERROR range endpoint is out of range for `u8` + let _range_g = 0..128i8; //~ ERROR range endpoint is out of range for `i8` + + range_a.collect::>(); + range_b.collect::>(); + range_c.collect::>(); + range_d.collect::>(); + range_e.collect::>(); +} diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr new file mode 100644 index 0000000000000..939451d6bc022 --- /dev/null +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -0,0 +1,44 @@ +error: range endpoint is out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:4:19 + | +LL | let range_a = 0..256; + | ^^^^^^ help: use an inclusive range instead: `0..=255` + | +note: lint level defined here + --> $DIR/lint-range-endpoint-overflow.rs:1:9 + | +LL | #![deny(overflowing_literals)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:6:23 + | +LL | let range_c = 0..=256; + | ^^^ + +error: literal out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:7:19 + | +LL | let range_d = 256..5; + | ^^^ + +error: literal out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:8:22 + | +LL | let range_e = 0..257; + | ^^^ + +error: range endpoint is out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:9:20 + | +LL | let _range_f = 0..256u8; + | ^^^^^^^^ help: use an inclusive range instead: `0..=255u8` + +error: range endpoint is out of range for `i8` + --> $DIR/lint-range-endpoint-overflow.rs:10:20 + | +LL | let _range_g = 0..128i8; + | ^^^^^^^^ help: use an inclusive range instead: `0..=127i8` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/lint/lint-type-limits2.rs b/src/test/ui/lint/lint-type-limits2.rs index c4486e0676887..3f90119cd8954 100644 --- a/src/test/ui/lint/lint-type-limits2.rs +++ b/src/test/ui/lint/lint-type-limits2.rs @@ -11,5 +11,5 @@ fn bar() -> i8 { fn baz() -> bool { 128 > bar() //~ ERROR comparison is useless due to type limits - //~| WARN literal out of range for i8 + //~| WARN literal out of range for `i8` } diff --git a/src/test/ui/lint/lint-type-limits2.stderr b/src/test/ui/lint/lint-type-limits2.stderr index e7bc407422a12..0b3d292856707 100644 --- a/src/test/ui/lint/lint-type-limits2.stderr +++ b/src/test/ui/lint/lint-type-limits2.stderr @@ -6,7 +6,7 @@ LL | 128 > bar() | = note: requested on the command line with `-D unused-comparisons` -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-limits2.rs:13:5 | LL | 128 > bar() diff --git a/src/test/ui/lint/lint-type-limits3.rs b/src/test/ui/lint/lint-type-limits3.rs index a715c69f7849e..ceecf9ab30bb8 100644 --- a/src/test/ui/lint/lint-type-limits3.rs +++ b/src/test/ui/lint/lint-type-limits3.rs @@ -7,7 +7,7 @@ fn main() { } fn qux() { let mut i = 1i8; while 200 != i { //~ ERROR comparison is useless due to type limits - //~| WARN literal out of range for i8 + //~| WARN literal out of range for `i8` i += 1; } } diff --git a/src/test/ui/lint/lint-type-limits3.stderr b/src/test/ui/lint/lint-type-limits3.stderr index 742b6695c24f2..70cd9c859ecf3 100644 --- a/src/test/ui/lint/lint-type-limits3.stderr +++ b/src/test/ui/lint/lint-type-limits3.stderr @@ -6,7 +6,7 @@ LL | while 200 != i { | = note: requested on the command line with `-D unused-comparisons` -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-limits3.rs:9:11 | LL | while 200 != i { diff --git a/src/test/ui/lint/lint-type-overflow.rs b/src/test/ui/lint/lint-type-overflow.rs index 847cdf31dc788..9672da6d3586c 100644 --- a/src/test/ui/lint/lint-type-overflow.rs +++ b/src/test/ui/lint/lint-type-overflow.rs @@ -1,5 +1,3 @@ -// - #![deny(overflowing_literals)] fn test(x: i8) { @@ -9,39 +7,39 @@ fn test(x: i8) { #[allow(unused_variables)] fn main() { let x1: u8 = 255; // should be OK - let x1: u8 = 256; //~ error: literal out of range for u8 + let x1: u8 = 256; //~ error: literal out of range for `u8` let x1 = 255_u8; // should be OK - let x1 = 256_u8; //~ error: literal out of range for u8 + let x1 = 256_u8; //~ error: literal out of range for `u8` let x2: i8 = -128; // should be OK - let x1: i8 = 128; //~ error: literal out of range for i8 + let x1: i8 = 128; //~ error: literal out of range for `i8` - let x3: i8 = -129; //~ error: literal out of range for i8 - let x3: i8 = -(129); //~ error: literal out of range for i8 - let x3: i8 = -{129}; //~ error: literal out of range for i8 + let x3: i8 = -129; //~ error: literal out of range for `i8` + let x3: i8 = -(129); //~ error: literal out of range for `i8` + let x3: i8 = -{129}; //~ error: literal out of range for `i8` - test(1000); //~ error: literal out of range for i8 + test(1000); //~ error: literal out of range for `i8` - let x = 128_i8; //~ error: literal out of range for i8 + let x = 128_i8; //~ error: literal out of range for `i8` let x = 127_i8; let x = -128_i8; let x = -(128_i8); - let x = -129_i8; //~ error: literal out of range for i8 + let x = -129_i8; //~ error: literal out of range for `i8` let x: i32 = 2147483647; // should be OK let x = 2147483647_i32; // should be OK - let x: i32 = 2147483648; //~ error: literal out of range for i32 - let x = 2147483648_i32; //~ error: literal out of range for i32 + let x: i32 = 2147483648; //~ error: literal out of range for `i32` + let x = 2147483648_i32; //~ error: literal out of range for `i32` let x: i32 = -2147483648; // should be OK let x = -2147483648_i32; // should be OK - let x: i32 = -2147483649; //~ error: literal out of range for i32 - let x = -2147483649_i32; //~ error: literal out of range for i32 - let x = 2147483648; //~ error: literal out of range for i32 + let x: i32 = -2147483649; //~ error: literal out of range for `i32` + let x = -2147483649_i32; //~ error: literal out of range for `i32` + let x = 2147483648; //~ error: literal out of range for `i32` - let x = 9223372036854775808_i64; //~ error: literal out of range for i64 + let x = 9223372036854775808_i64; //~ error: literal out of range for `i64` let x = -9223372036854775808_i64; // should be OK - let x = 18446744073709551615_i64; //~ error: literal out of range for i64 - let x: i64 = -9223372036854775809; //~ error: literal out of range for i64 - let x = -9223372036854775809_i64; //~ error: literal out of range for i64 + let x = 18446744073709551615_i64; //~ error: literal out of range for `i64` + let x: i64 = -9223372036854775809; //~ error: literal out of range for `i64` + let x = -9223372036854775809_i64; //~ error: literal out of range for `i64` } diff --git a/src/test/ui/lint/lint-type-overflow.stderr b/src/test/ui/lint/lint-type-overflow.stderr index 9da007457aa83..6fcd9b58b2dc7 100644 --- a/src/test/ui/lint/lint-type-overflow.stderr +++ b/src/test/ui/lint/lint-type-overflow.stderr @@ -1,113 +1,113 @@ -error: literal out of range for u8 - --> $DIR/lint-type-overflow.rs:12:18 +error: literal out of range for `u8` + --> $DIR/lint-type-overflow.rs:10:18 | LL | let x1: u8 = 256; | ^^^ | note: lint level defined here - --> $DIR/lint-type-overflow.rs:3:9 + --> $DIR/lint-type-overflow.rs:1:9 | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for u8 - --> $DIR/lint-type-overflow.rs:15:14 +error: literal out of range for `u8` + --> $DIR/lint-type-overflow.rs:13:14 | LL | let x1 = 256_u8; | ^^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:18:18 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:16:18 | LL | let x1: i8 = 128; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:20:19 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:18:19 | LL | let x3: i8 = -129; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:21:19 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:19:19 | LL | let x3: i8 = -(129); | ^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:22:20 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:20:20 | LL | let x3: i8 = -{129}; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:24:10 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:22:10 | LL | test(1000); | ^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:26:13 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:24:13 | LL | let x = 128_i8; | ^^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:30:14 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:28:14 | LL | let x = -129_i8; | ^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:34:18 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:32:18 | LL | let x: i32 = 2147483648; | ^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:35:13 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:33:13 | LL | let x = 2147483648_i32; | ^^^^^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:38:19 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:36:19 | LL | let x: i32 = -2147483649; | ^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:39:14 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:37:14 | LL | let x = -2147483649_i32; | ^^^^^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:40:13 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:38:13 | LL | let x = 2147483648; | ^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:42:13 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:40:13 | LL | let x = 9223372036854775808_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:44:13 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:42:13 | LL | let x = 18446744073709551615_i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:45:19 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:43:19 | LL | let x: i64 = -9223372036854775809; | ^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:46:14 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:44:14 | LL | let x = -9223372036854775809_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr index 5255f6c75934d..c76e9e25d5a93 100644 --- a/src/test/ui/lint/lint-type-overflow2.stderr +++ b/src/test/ui/lint/lint-type-overflow2.stderr @@ -1,4 +1,4 @@ -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-overflow2.rs:9:20 | LL | let x2: i8 = --128; @@ -10,25 +10,25 @@ note: lint level defined here LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f32 +warning: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:11:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f32 +warning: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:12:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f64 +warning: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:13:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f64 +warning: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:14:14 | LL | let x = 1.7976931348623159e+308_f64; diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 5235c9851b53f..dabfb876fbb92 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -1,4 +1,4 @@ -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/type-overflow.rs:5:17 | LL | let error = 255i8;