@@ -54,6 +54,7 @@ enum lint {
5454 deprecated_pattern,
5555 non_camel_case_types,
5656 structural_records,
57+ type_limits,
5758
5859 managed_heap_memory,
5960 owned_heap_memory,
@@ -186,6 +187,11 @@ fn get_lint_dict() -> lint_dict {
186187 desc: ~"allow legacy modes",
187188 default : forbid} ) ,
188189
190+ ( ~"type_limits",
191+ @{ lint: type_limits,
192+ desc: ~"comparisons made useless by limits of the types involved",
193+ default : warn} )
194+
189195 /* FIXME(#3266)--make liveness warnings lintable
190196 (~"unused_variable",
191197 @{lint: unused_variable,
@@ -397,6 +403,7 @@ fn check_item(i: @ast::item, cx: ty::ctxt) {
397403 check_item_heap( cx, i) ;
398404 check_item_structural_records( cx, i) ;
399405 check_item_deprecated_modes( cx, i) ;
406+ check_item_type_limits( cx, i) ;
400407}
401408
402409// Take a visitor, and modify it so that it will not proceed past subitems.
@@ -430,6 +437,122 @@ fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
430437 visit:: visit_item( it, ( ) , visit) ;
431438}
432439
440+ fn check_item_type_limits( cx: ty:: ctxt, it: @ast:: item) {
441+ pure fn is_valid<T : cmp:: Ord >( binop: ast:: binop, v: T ,
442+ min: T , max: T ) -> bool {
443+ match binop {
444+ ast:: lt => v <= max,
445+ ast:: le => v < max,
446+ ast:: gt => v >= min,
447+ ast:: ge => v > min,
448+ ast:: eq | ast:: ne => v >= min && v <= max,
449+ _ => fail
450+ }
451+ }
452+
453+ pure fn rev_binop( binop: ast:: binop) -> ast:: binop {
454+ match binop {
455+ ast:: lt => ast:: gt,
456+ ast:: le => ast:: ge,
457+ ast:: gt => ast:: lt,
458+ ast:: ge => ast:: le,
459+ _ => binop
460+ }
461+ }
462+
463+ pure fn int_ty_range( int_ty: ast:: int_ty) -> ( i64 , i64 ) {
464+ match int_ty {
465+ ast:: ty_i => ( int:: min_value as i64 , int:: max_value as i64 ) ,
466+ ast:: ty_char => ( u32 :: min_value as i64 , u32 :: max_value as i64 ) ,
467+ ast:: ty_i8 => ( i8 :: min_value as i64 , i8 :: max_value as i64 ) ,
468+ ast:: ty_i16 => ( i16 :: min_value as i64 , i16 :: max_value as i64 ) ,
469+ ast:: ty_i32 => ( i32 :: min_value as i64 , i32 :: max_value as i64 ) ,
470+ ast:: ty_i64 => ( i64 :: min_value, i64 :: max_value)
471+ }
472+ }
473+
474+ pure fn uint_ty_range( uint_ty: ast:: uint_ty) -> ( u64 , u64 ) {
475+ match uint_ty {
476+ ast:: ty_u => ( uint:: min_value as u64 , uint:: max_value as u64 ) ,
477+ ast:: ty_u8 => ( u8 :: min_value as u64 , u8 :: max_value as u64 ) ,
478+ ast:: ty_u16 => ( u16 :: min_value as u64 , u16 :: max_value as u64 ) ,
479+ ast:: ty_u32 => ( u32 :: min_value as u64 , u32 :: max_value as u64 ) ,
480+ ast:: ty_u64 => ( u64 :: min_value, u64 :: max_value)
481+ }
482+ }
483+
484+ fn check_limits( cx: ty:: ctxt, binop: ast:: binop, l: & ast:: expr,
485+ r: & ast:: expr) -> bool {
486+ let ( lit, expr, swap) = match ( l. node, r. node) {
487+ ( ast:: expr_lit( _) , _) => ( l, r, true) ,
488+ ( _, ast:: expr_lit( _) ) => ( r, l, false) ,
489+ _ => return true
490+ } ;
491+ // Normalize the binop so that the literal is always on the RHS in
492+ // the comparison
493+ let norm_binop = if ( swap) {
494+ rev_binop( binop)
495+ } else {
496+ binop
497+ } ;
498+ match ty:: get( ty:: expr_ty( cx, @* expr) ) . sty {
499+ ty:: ty_int( int_ty) => {
500+ let ( min, max) = int_ty_range( int_ty) ;
501+ let lit_val: i64 = match lit. node {
502+ ast:: expr_lit( @li) => match li. node {
503+ ast:: lit_int( v, _) => v,
504+ ast:: lit_uint( v, _) => v as i64 ,
505+ ast:: lit_int_unsuffixed( v) => v,
506+ _ => return true
507+ } ,
508+ _ => fail
509+ } ;
510+ is_valid( norm_binop, lit_val, min, max)
511+ }
512+ ty:: ty_uint( uint_ty) => {
513+ let ( min, max) : ( u64 , u64 ) = uint_ty_range( uint_ty) ;
514+ let lit_val: u64 = match lit. node {
515+ ast:: expr_lit( @li) => match li. node {
516+ ast:: lit_int( v, _) => v as u64 ,
517+ ast:: lit_uint( v, _) => v,
518+ ast:: lit_int_unsuffixed( v) => v as u64 ,
519+ _ => return true
520+ } ,
521+ _ => fail
522+ } ;
523+ is_valid( norm_binop, lit_val, min, max)
524+ }
525+ _ => true
526+ }
527+ }
528+
529+ pure fn is_comparison( binop: ast:: binop) -> bool {
530+ match binop {
531+ ast:: eq | ast:: lt | ast:: le |
532+ ast:: ne | ast:: ge | ast:: gt => true,
533+ _ => false
534+ }
535+ }
536+
537+ let visit = item_stopping_visitor( visit:: mk_simple_visitor( @{
538+ visit_expr: fn @( e: @ast:: expr) {
539+ match e. node {
540+ ast:: expr_binary( binop, @l, @r) => {
541+ if is_comparison( binop)
542+ && !check_limits( cx, binop, & l, & r) {
543+ cx. sess. span_lint(
544+ type_limits, e. id, it. id, e. span,
545+ ~"comparison is useless due to type limits") ;
546+ }
547+ }
548+ _ => ( )
549+ }
550+ } ,
551+ .. * visit:: default_simple_visitor( )
552+ } ) ) ;
553+ visit:: visit_item( it, ( ) , visit) ;
554+ }
555+
433556fn check_item_structural_records( cx: ty:: ctxt, it: @ast:: item) {
434557 let visit = item_stopping_visitor( visit:: mk_simple_visitor( @{
435558 visit_expr: fn @( e: @ast:: expr) {
0 commit comments