Change:
Consider this program: The behaviour one might expect here is for the program to print "At most three" three times and then exiting. The actual program output is: The problem is that i is unsigned, so the comparison will always be true. With the addition of "" () this could easily be avoided.
Fixed.

Discussion:
deprecated_pattern, non_camel_case_types, structural_records, <ins> type_limits, </ins> managed_heap_memory, owned_heap_memory,

desc: ~"allow legacy modes", default: forbid}), <ins> (~"type_limits", @{lint: type_limits, desc: ~"comparisons made useless by limits of the types involved", default: warn}) </ins> /* FIXME(#3266)--make liveness warnings lintable (~"unused_variable", @{lint: unused_variable,

check_item_heap(cx, i); check_item_structural_records(cx, i); check_item_deprecated_modes(cx, i); <ins> check_item_type_limits(cx, i); </ins> } // Take a visitor, and modify it so that it will not proceed past subitems.

visit::visit_item(it, (), visit); } <ins> fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) { pure fn is_valid<T: cmp::Ord>(binop: ast::binop, v: T, min: T, max: T) -> bool { match binop { ast::lt => v <= max, ast::le => v < max, ast::gt => v >= min, ast::ge => v > min, ast::eq | ast::ne => v >= min && v <= max, _ => fail } } pure fn rev_binop(binop: ast::binop) -> ast::binop { match binop { ast::lt => ast::gt, ast::le => ast::ge, ast::gt => ast::lt, ast::ge => ast::le, _ => binop } } pure fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) { match int_ty { ast::ty_i =>    (int::min_value as i64, int::max_value as i64), ast::ty_char => (u32::min_value as i64, u32::max_value as i64), ast::ty_i8 =>   (i8::min_value  as i64, i8::max_value  as i64), ast::ty_i16 =>  (i16::min_value as i64, i16::max_value as i64), ast::ty_i32 =>  (i32::min_value as i64, i32::max_value as i64), ast::ty_i64 =>  (i64::min_value,        i64::max_value) } } pure fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) { match uint_ty { ast::ty_u =>   (uint::min_value as u64, uint::max_value as u64), ast::ty_u8 =>  (u8::min_value   as u64, u8::max_value   as u64), ast::ty_u16 => (u16::min_value  as u64, u16::max_value  as u64), ast::ty_u32 => (u32::min_value  as u64, u32::max_value  as u64), ast::ty_u64 => (u64::min_value,         u64::max_value) } } fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr, r: &ast::expr) -> bool { let (lit, expr, swap) = match (l.node, r.node) { (ast::expr_lit(_), _) => (l, r, true), (_, ast::expr_lit(_)) => (r, l, false), _ => return true }; // Normalize the binop so that the literal is always on the RHS in // the comparison let norm_binop = if (swap) { rev_binop(binop) } else { binop }; match ty::get(ty::expr_ty(cx, @*expr)).sty { ty::ty_int(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i64 = match lit.node { ast::expr_lit(@li) => match li.node { ast::lit_int(v, _) => v, ast::lit_uint(v, _) => v as i64, ast::lit_int_unsuffixed(v) => v, _ => return true }, _ => fail }; is_valid(norm_binop, lit_val, min, max) } ty::ty_uint(uint_ty) => { let (min, max): (u64, u64) = uint_ty_range(uint_ty); let lit_val: u64 = match lit.node { ast::expr_lit(@li) => match li.node { ast::lit_int(v, _) => v as u64, ast::lit_uint(v, _) => v, ast::lit_int_unsuffixed(v) => v as u64, _ => return true }, _ => fail }; is_valid(norm_binop, lit_val, min, max) } _ => true } } pure fn is_comparison(binop: ast::binop) -> bool { match binop { ast::eq | ast::lt | ast::le | ast::ne | ast::ge | ast::gt => true, _ => false } } let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ visit_expr: fn@(e: @ast::expr) { match e.node { ast::expr_binary(binop, @l, @r) => { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { cx.sess.span_lint( type_limits, e.id, it.id, e.span, ~"comparison is useless due to type limits"); } } _ => () } }, .. *visit::default_simple_visitor() })); visit::visit_item(it, (), visit); } </ins> fn check_item_structural_records(cx: ty::ctxt, it: @ast::item) { let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ visit_expr: fn@(e: @ast::expr) {

<ins> // compile-flags: -D type-limits fn main() { } fn foo() { let mut i = 100u; while i >= 0 { //~ ERROR comparison is useless due to type limits i -= 1; } } fn bar() -> i8 { return 123; } fn baz() -> bool { 128 > bar() //~ ERROR comparison is useless due to type limits } fn qux() { let mut i = 1i8; while 200 != i { //~ ERROR comparison is useless due to type limits i += 1; } } </ins>