From 20627c7430afd700cf0b462c372d9086c7c51f0a Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Sun, 27 Oct 2013 19:59:58 +0100 Subject: [PATCH] Check inferred integer literals for overflows, closes #4220 --- doc/po/ja/tutorial-tasks.md.po | 2 +- doc/po/tutorial-tasks.md.pot | 2 +- doc/tutorial-tasks.md | 2 +- src/librustc/middle/lint.rs | 70 ++++++++++++++++++++- src/test/compile-fail/lint-type-limits.rs | 2 + src/test/compile-fail/lint-type-overflow.rs | 50 +++++++++++++++ src/test/compile-fail/oversized-literal.rs | 4 +- 7 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 src/test/compile-fail/lint-type-overflow.rs diff --git a/doc/po/ja/tutorial-tasks.md.po b/doc/po/ja/tutorial-tasks.md.po index e0cf7467db90b..9cea63f0d0b5d 100644 --- a/doc/po/ja/tutorial-tasks.md.po +++ b/doc/po/ja/tutorial-tasks.md.po @@ -529,7 +529,7 @@ msgid "" "The basic example below illustrates this.\n" "~~~\n" "# fn make_a_sandwich() {};\n" -"fn fib(n: uint) -> uint {\n" +"fn fib(n: u64) -> u64 {\n" " // lengthy computation returning an uint\n" " 12586269025\n" "}\n" diff --git a/doc/po/tutorial-tasks.md.pot b/doc/po/tutorial-tasks.md.pot index f4dde0cb4e3cf..483cde9d7d016 100644 --- a/doc/po/tutorial-tasks.md.pot +++ b/doc/po/tutorial-tasks.md.pot @@ -529,7 +529,7 @@ msgid "" "The basic example below illustrates this.\n" "~~~\n" "# fn make_a_sandwich() {};\n" -"fn fib(n: uint) -> uint {\n" +"fn fib(n: u64) -> u64 {\n" " // lengthy computation returning an uint\n" " 12586269025\n" "}\n" diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md index 4983a5af3e5ab..ef78484c15198 100644 --- a/doc/tutorial-tasks.md +++ b/doc/tutorial-tasks.md @@ -273,7 +273,7 @@ later. The basic example below illustrates this. ~~~ # fn make_a_sandwich() {}; -fn fib(n: uint) -> uint { +fn fib(n: u64) -> u64 { // lengthy computation returning an uint 12586269025 } diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 21a9b1904dd04..497c9f166a4e2 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -72,6 +72,7 @@ pub enum lint { non_uppercase_statics, non_uppercase_pattern_statics, type_limits, + type_overflow, unused_unsafe, managed_heap_memory, @@ -219,6 +220,14 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ default: warn }), + ("type_overflow", + LintSpec { + lint: type_overflow, + desc: "literal out of range for its type", + default: warn + }), + + ("unused_unsafe", LintSpec { lint: unused_unsafe, @@ -321,6 +330,9 @@ struct Context { // levels, this stack keeps track of the previous lint levels of whatever // was modified. lint_stack: ~[(lint, level, LintSource)], + + // id of the last visited negated expression + negated_expr_id: ast::NodeId } impl Context { @@ -507,7 +519,48 @@ fn check_type_limits(cx: &Context, e: &ast::Expr) { cx.span_lint(type_limits, e.span, "comparison is useless due to type limits"); } - } + }, + ast::ExprLit(lit) => { + match ty::get(ty::expr_ty(cx.tcx, e)).sty { + ty::ty_int(t) => { + let int_type = if t == ast::ty_i { + cx.tcx.sess.targ_cfg.int_type + } else { t }; + let (min, max) = int_ty_range(int_type); + let mut lit_val: i64 = match lit.node { + ast::lit_int(v, _) => v, + ast::lit_uint(v, _) => v as i64, + ast::lit_int_unsuffixed(v) => v, + _ => fail!() + }; + if cx.negated_expr_id == e.id { + lit_val *= -1; + } + if lit_val < min || lit_val > max { + cx.span_lint(type_overflow, e.span, + "literal out of range for its type"); + } + }, + ty::ty_uint(t) => { + let uint_type = if t == ast::ty_u { + cx.tcx.sess.targ_cfg.uint_type + } else { t }; + let (min, max) = uint_ty_range(uint_type); + let lit_val: u64 = match lit.node { + ast::lit_int(v, _) => v as u64, + ast::lit_uint(v, _) => v, + ast::lit_int_unsuffixed(v) => v as u64, + _ => fail!() + }; + if lit_val < min || lit_val > max { + cx.span_lint(type_overflow, e.span, + "literal out of range for its type"); + } + }, + + _ => () + }; + }, _ => () }; @@ -1078,11 +1131,25 @@ impl Visitor<()> for Context { } fn visit_expr(&mut self, e: @ast::Expr, _: ()) { + match e.node { + ast::ExprUnary(_, ast::UnNeg, expr) => { + // propagate negation, if the negation itself isn't negated + if self.negated_expr_id != e.id { + self.negated_expr_id = expr.id; + } + }, + ast::ExprParen(expr) => if self.negated_expr_id == e.id { + self.negated_expr_id = expr.id + }, + _ => () + }; + check_while_true_expr(self, e); check_stability(self, e); check_unused_unsafe(self, e); check_unnecessary_allocation(self, e); check_heap_expr(self, e); + check_type_limits(self, e); visit::walk_expr(self, e, ()); @@ -1139,6 +1206,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) { cur: SmallIntMap::new(), tcx: tcx, lint_stack: ~[], + negated_expr_id: -1 }; // Install default lint levels, followed by the command line levels, and diff --git a/src/test/compile-fail/lint-type-limits.rs b/src/test/compile-fail/lint-type-limits.rs index 2eb794fd1c296..08714e3a044b4 100644 --- a/src/test/compile-fail/lint-type-limits.rs +++ b/src/test/compile-fail/lint-type-limits.rs @@ -24,11 +24,13 @@ fn bar() -> i8 { fn baz() -> bool { 128 > bar() //~ ERROR comparison is useless due to type limits + //~^ WARNING literal out of range for its type } fn qux() { let mut i = 1i8; while 200 != i { //~ ERROR comparison is useless due to type limits + //~^ WARNING literal out of range for its type i += 1; } } diff --git a/src/test/compile-fail/lint-type-overflow.rs b/src/test/compile-fail/lint-type-overflow.rs new file mode 100644 index 0000000000000..27cd2bae125c8 --- /dev/null +++ b/src/test/compile-fail/lint-type-overflow.rs @@ -0,0 +1,50 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// + +#[deny(type_overflow)]; + +fn test(x: i8) { + println!("x {}", x); +} + +#[allow(unused_variable)] +fn main() { + let x1: u8 = 255; // should be OK + let x1: u8 = 256; //~ error: literal out of range for its type + + let x1 = 255_u8; // should be OK + let x1 = 256_u8; //~ error: literal out of range for its type + + let x2: i8 = -128; // should be OK + let x1: i8 = 128; //~ error: literal out of range for its type + let x2: i8 = --128; //~ error: literal out of range for its type + + let x3: i8 = -129; //~ error: literal out of range for its type + let x3: i8 = -(129); //~ error: literal out of range for its type + let x3: i8 = -{129}; //~ error: literal out of range for its type + + test(1000); //~ error: literal out of range for its type + + let x = 128_i8; //~ error: literal out of range for its type + let x = 127_i8; + let x = -128_i8; + let x = -(128_i8); + let x = -129_i8; //~ error: literal out of range for its type + + let x: i32 = 2147483647; // should be OK + let x = 2147483647_i32; // should be OK + let x: i32 = 2147483648; //~ error: literal out of range for its type + let x = 2147483648_i32; //~ error: literal out of range for its type + let x: i32 = -2147483648; // should be OK + let x = -2147483648_i32; // should be OK + let x: i32 = -2147483649; //~ error: literal out of range for its type + let x = -2147483649_i32; //~ error: literal out of range for its type +} diff --git a/src/test/compile-fail/oversized-literal.rs b/src/test/compile-fail/oversized-literal.rs index 2e4ba8855bdb6..1c4316672f3fb 100644 --- a/src/test/compile-fail/oversized-literal.rs +++ b/src/test/compile-fail/oversized-literal.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:literal out of range +// compile-flags: -D type-overflow -fn main() { info!("{}", 300u8); } +fn main() { info!("{}", 300u8); } //~ error: literal out of range for its type