]> git.lizzy.rs Git - rust.git/commitdiff
Suggest type for overflowing bin/hex-literals
authorflip1995 <uwdkn@student.kit.edu>
Thu, 22 Feb 2018 14:53:22 +0000 (15:53 +0100)
committerflip1995 <uwdkn@student.kit.edu>
Thu, 1 Mar 2018 00:34:25 +0000 (01:34 +0100)
src/librustc_lint/types.rs
src/test/ui/lint/type-overflow.rs [new file with mode: 0644]
src/test/ui/lint/type-overflow.stderr [new file with mode: 0644]

index ef9b3d38c637cc509cb1a74512b4e32096ba52e9..4fabb5bafbf89eb081e52573a3fed0ba3d34b79d 100644 (file)
@@ -150,11 +150,52 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
 
                                 // 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;
                                 }
                             }
@@ -180,37 +221,77 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
                                 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),
+                            );
                         }
                     }
                     _ => (),
@@ -338,6 +419,69 @@ fn is_comparison(binop: hir::BinOp) -> bool {
                 _ => 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
+        }
     }
 }
 
diff --git a/src/test/ui/lint/type-overflow.rs b/src/test/ui/lint/type-overflow.rs
new file mode 100644 (file)
index 0000000..e414f43
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// must-compile-successfully
+
+#![feature(i128_type)]
+
+fn main() {
+    let error = 255i8; //~WARNING literal out of range for i8
+
+    let ok = 0b1000_0001; // should be ok -> i32
+    let ok = 0b0111_1111i8; // should be ok -> 127i8
+
+    let fail = 0b1000_0001i8; //~WARNING literal out of range for i8
+
+    let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for i64
+
+    let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for u32
+
+    let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
+    //~^ WARNING literal out of range for i128
+
+    let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for i32
+
+    let fail: isize = 0x8000_0000_0000_0000; //~WARNING literal out of range for isize
+
+    let fail = -0b1111_1111i8; //~WARNING literal out of range for i8
+}
diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr
new file mode 100644 (file)
index 0000000..425f76d
--- /dev/null
@@ -0,0 +1,70 @@
+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`
+