Rollup merge of #60330 - varkor:range-endpoint-overflow-lint, r=estebank

Suggest using an inclusive range instead of an exclusive range when the endpoint overflows by 1

Fixes https://github.com/rust-lang/rust/issues/47213.
This commit is contained in:
Mazdak Farrokhzad 2019-05-01 08:29:11 +02:00 committed by GitHub
commit 6d78142bf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 551 additions and 399 deletions

View file

@ -5400,3 +5400,65 @@ fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
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
}

View file

@ -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<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));
}
}
_ => {}
}
}
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<T: cmp::PartialOrd>(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<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
}
// 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 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();
}
}
}

View file

@ -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>,

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -1,4 +1,4 @@
fn main() {
let x: u8 = 256;
//~^ error: literal out of range for u8
//~^ error: literal out of range for `u8`
}

View file

@ -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;

View file

@ -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::<Vec<u8>>();
range_b.collect::<Vec<u8>>();
range_c.collect::<Vec<u8>>();
range_d.collect::<Vec<u8>>();
range_e.collect::<Vec<u8>>();
}

View file

@ -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

View file

@ -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`
}

View file

@ -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()

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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`
}

View file

@ -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;
| ^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -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;

View file

@ -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;