@@ -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,122 @@ 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 ,
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
+
433
556
fn check_item_structural_records( cx: ty:: ctxt, it: @ast:: item) {
434
557
let visit = item_stopping_visitor( visit:: mk_simple_visitor( @{
435
558
visit_expr: fn @( e: @ast:: expr) {
0 commit comments