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