]> git.lizzy.rs Git - rust.git/blob - src/types.rs
Use configuration in the `TYPE_COMPLEXITY` lint
[rust.git] / src / types.rs
1 use reexport::*;
2 use rustc::lint::*;
3 use rustc::middle::const_eval;
4 use rustc::middle::ty;
5 use rustc_front::hir::*;
6 use rustc_front::intravisit::{FnKind, Visitor, walk_ty};
7 use rustc_front::util::{is_comparison_binop, binop_to_string};
8 use syntax::ast::{IntTy, UintTy, FloatTy};
9 use syntax::codemap::Span;
10 use utils::*;
11
12 /// Handles all the linting of funky types
13 #[allow(missing_copy_implementations)]
14 pub struct TypePass;
15
16 /// **What it does:** This lint checks for use of `Box<Vec<_>>` anywhere in the code.
17 ///
18 /// **Why is this bad?** `Vec` already keeps its contents in a separate area on the heap. So if you `Box` it, you just add another level of indirection without any benefit whatsoever.
19 ///
20 /// **Known problems:** None
21 ///
22 /// **Example:** `struct X { values: Box<Vec<Foo>> }`
23 declare_lint! {
24     pub BOX_VEC, Warn,
25     "usage of `Box<Vec<T>>`, vector elements are already on the heap"
26 }
27
28 /// **What it does:** This lint checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).
29 ///
30 /// **Why is this bad?** Gankro says:
31 ///
32 /// >The TL;DR of `LinkedList` is that it's built on a massive amount of pointers and indirection. It wastes memory, it has terrible cache locality, and is all-around slow. `RingBuf`, while "only" amortized for push/pop, should be faster in the general case for almost every possible workload, and isn't even amortized at all if you can predict the capacity you need.
33 /// >
34 /// > `LinkedList`s are only really good if you're doing a lot of merging or splitting of lists. This is because they can just mangle some pointers instead of actually copying the data. Even if you're doing a lot of insertion in the middle of the list, `RingBuf` can still be better because of how expensive it is to seek to the middle of a `LinkedList`.
35 ///
36 /// **Known problems:** False positives – the instances where using a `LinkedList` makes sense are few and far between, but they can still happen.
37 ///
38 /// **Example:** `let x = LinkedList::new();`
39 declare_lint! {
40     pub LINKEDLIST, Warn,
41     "usage of LinkedList, usually a vector is faster, or a more specialized data \
42      structure like a VecDeque"
43 }
44
45 impl LintPass for TypePass {
46     fn get_lints(&self) -> LintArray {
47         lint_array!(BOX_VEC, LINKEDLIST)
48     }
49 }
50
51 impl LateLintPass for TypePass {
52     fn check_ty(&mut self, cx: &LateContext, ast_ty: &Ty) {
53         if in_macro(cx, ast_ty.span) {
54             return;
55         }
56         if let Some(ty) = cx.tcx.ast_ty_to_ty_cache.borrow().get(&ast_ty.id) {
57             if let ty::TyBox(ref inner) = ty.sty {
58                 if match_type(cx, inner, &VEC_PATH) {
59                     span_help_and_lint(cx,
60                                        BOX_VEC,
61                                        ast_ty.span,
62                                        "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
63                                        "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.");
64                 }
65             } else if match_type(cx, ty, &LL_PATH) {
66                 span_help_and_lint(cx,
67                                    LINKEDLIST,
68                                    ast_ty.span,
69                                    "I see you're using a LinkedList! Perhaps you meant some other data structure?",
70                                    "a VecDeque might work");
71             }
72         }
73     }
74 }
75
76 #[allow(missing_copy_implementations)]
77 pub struct LetPass;
78
79 /// **What it does:** This lint checks for binding a unit value.
80 ///
81 /// **Why is this bad?** A unit value cannot usefully be used anywhere. So binding one is kind of pointless.
82 ///
83 /// **Known problems:** None
84 ///
85 /// **Example:** `let x = { 1; };`
86 declare_lint! {
87     pub LET_UNIT_VALUE, Warn,
88     "creating a let binding to a value of unit type, which usually can't be used afterwards"
89 }
90
91 fn check_let_unit(cx: &LateContext, decl: &Decl) {
92     if let DeclLocal(ref local) = decl.node {
93         let bindtype = &cx.tcx.pat_ty(&local.pat).sty;
94         if *bindtype == ty::TyTuple(vec![]) {
95             if in_external_macro(cx, decl.span) || in_macro(cx, local.pat.span) {
96                 return;
97             }
98             if is_from_for_desugar(decl) {
99                 return;
100             }
101             span_lint(cx,
102                       LET_UNIT_VALUE,
103                       decl.span,
104                       &format!("this let-binding has unit value. Consider omitting `let {} =`",
105                                snippet(cx, local.pat.span, "..")));
106         }
107     }
108 }
109
110 impl LintPass for LetPass {
111     fn get_lints(&self) -> LintArray {
112         lint_array!(LET_UNIT_VALUE)
113     }
114 }
115
116 impl LateLintPass for LetPass {
117     fn check_decl(&mut self, cx: &LateContext, decl: &Decl) {
118         check_let_unit(cx, decl)
119     }
120 }
121
122 /// **What it does:** This lint checks for comparisons to unit.
123 ///
124 /// **Why is this bad?** Unit is always equal to itself, and thus is just a clumsily written constant. Mostly this happens when someone accidentally adds semicolons at the end of the operands.
125 ///
126 /// **Known problems:** None
127 ///
128 /// **Example:** `if { foo(); } == { bar(); } { baz(); }` is equal to `{ foo(); bar(); baz(); }`
129 declare_lint! {
130     pub UNIT_CMP, Warn,
131     "comparing unit values (which is always `true` or `false`, respectively)"
132 }
133
134 #[allow(missing_copy_implementations)]
135 pub struct UnitCmp;
136
137 impl LintPass for UnitCmp {
138     fn get_lints(&self) -> LintArray {
139         lint_array!(UNIT_CMP)
140     }
141 }
142
143 impl LateLintPass for UnitCmp {
144     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
145         if in_macro(cx, expr.span) {
146             return;
147         }
148         if let ExprBinary(ref cmp, ref left, _) = expr.node {
149             let op = cmp.node;
150             let sty = &cx.tcx.expr_ty(left).sty;
151             if *sty == ty::TyTuple(vec![]) && is_comparison_binop(op) {
152                 let result = match op {
153                     BiEq | BiLe | BiGe => "true",
154                     _ => "false",
155                 };
156                 span_lint(cx,
157                           UNIT_CMP,
158                           expr.span,
159                           &format!("{}-comparison of unit values detected. This will always be {}",
160                                    binop_to_string(op),
161                                    result));
162             }
163         }
164     }
165 }
166
167 pub struct CastPass;
168
169 /// **What it does:** This lint checks for casts from any numerical to a float type where the receiving type cannot store all values from the original type without rounding errors. This possible rounding is to be expected, so this lint is `Allow` by default.
170 ///
171 /// Basically, this warns on casting any integer with 32 or more bits to `f32` or any 64-bit integer to `f64`.
172 ///
173 /// **Why is this bad?** It's not bad at all. But in some applications it can be helpful to know where precision loss can take place. This lint can help find those places in the code.
174 ///
175 /// **Known problems:** None
176 ///
177 /// **Example:** `let x = u64::MAX; x as f64`
178 declare_lint! {
179     pub CAST_PRECISION_LOSS, Allow,
180     "casts that cause loss of precision, e.g `x as f32` where `x: u64`"
181 }
182
183 /// **What it does:** This lint checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values, which can be quite surprising in practice. However, as the cast works as defined, this lint is `Allow` by default.
184 ///
185 /// **Why is this bad?** Possibly surprising results. You can activate this lint as a one-time check to see where numerical wrapping can arise.
186 ///
187 /// **Known problems:** None
188 ///
189 /// **Example:** `let y : i8 = -1; y as u64` will return 18446744073709551615
190 declare_lint! {
191     pub CAST_SIGN_LOSS, Allow,
192     "casts from signed types to unsigned types, e.g `x as u32` where `x: i32`"
193 }
194
195 /// **What it does:** This lint checks for on casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by default.
196 ///
197 /// **Why is this bad?** In some problem domains, it is good practice to avoid truncation. This lint can be activated to help assess where additional checks could be beneficial.
198 ///
199 /// **Known problems:** None
200 ///
201 /// **Example:** `fn as_u8(x: u64) -> u8 { x as u8 }`
202 declare_lint! {
203     pub CAST_POSSIBLE_TRUNCATION, Allow,
204     "casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
205 }
206
207 /// **What it does:** This lint checks for casts from an unsigned type to a signed type of the same size. Performing such a cast is a 'no-op' for the compiler, i.e. nothing is changed at the bit level, and the binary representation of the value is reinterpreted. This can cause wrapping if the value is too big for the target signed type. However, the cast works as defined, so this lint is `Allow` by default.
208 ///
209 /// **Why is this bad?** While such a cast is not bad in itself, the results can be surprising when this is not the intended behavior, as demonstrated by the example below.
210 ///
211 /// **Known problems:** None
212 ///
213 /// **Example:** `u32::MAX as i32` will yield a value of `-1`.
214 declare_lint! {
215     pub CAST_POSSIBLE_WRAP, Allow,
216     "casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` and `x > i32::MAX`"
217 }
218
219 /// Returns the size in bits of an integral type.
220 /// Will return 0 if the type is not an int or uint variant
221 fn int_ty_to_nbits(typ: &ty::TyS) -> usize {
222     let n = match typ.sty {
223         ty::TyInt(i) => 4 << (i as usize),
224         ty::TyUint(u) => 4 << (u as usize),
225         _ => 0,
226     };
227     // n == 4 is the usize/isize case
228     if n == 4 {
229         ::std::mem::size_of::<usize>() * 8
230     } else {
231         n
232     }
233 }
234
235 fn is_isize_or_usize(typ: &ty::TyS) -> bool {
236     match typ.sty {
237         ty::TyInt(IntTy::Is) | ty::TyUint(UintTy::Us) => true,
238         _ => false,
239     }
240 }
241
242 fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: &ty::TyS, cast_to_f64: bool) {
243     let mantissa_nbits = if cast_to_f64 {
244         52
245     } else {
246         23
247     };
248     let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
249     let arch_dependent_str = "on targets with 64-bit wide pointers ";
250     let from_nbits_str = if arch_dependent {
251         "64".to_owned()
252     } else if is_isize_or_usize(cast_from) {
253         "32 or 64".to_owned()
254     } else {
255         int_ty_to_nbits(cast_from).to_string()
256     };
257     span_lint(cx,
258               CAST_PRECISION_LOSS,
259               expr.span,
260               &format!("casting {0} to {1} causes a loss of precision {2}({0} is {3} bits wide, but {1}'s mantissa \
261                         is only {4} bits wide)",
262                        cast_from,
263                        if cast_to_f64 {
264                            "f64"
265                        } else {
266                            "f32"
267                        },
268                        if arch_dependent {
269                            arch_dependent_str
270                        } else {
271                            ""
272                        },
273                        from_nbits_str,
274                        mantissa_nbits));
275 }
276
277 enum ArchSuffix {
278     _32,
279     _64,
280     None,
281 }
282
283 fn check_truncation_and_wrapping(cx: &LateContext, expr: &Expr, cast_from: &ty::TyS, cast_to: &ty::TyS) {
284     let arch_64_suffix = " on targets with 64-bit wide pointers";
285     let arch_32_suffix = " on targets with 32-bit wide pointers";
286     let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
287     let (from_nbits, to_nbits) = (int_ty_to_nbits(cast_from), int_ty_to_nbits(cast_to));
288     let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) = match (is_isize_or_usize(cast_from),
289                                                                               is_isize_or_usize(cast_to)) {
290         (true, true) | (false, false) => {
291             (to_nbits < from_nbits,
292              ArchSuffix::None,
293              to_nbits == from_nbits && cast_unsigned_to_signed,
294              ArchSuffix::None)
295         }
296         (true, false) => {
297             (to_nbits <= 32,
298              if to_nbits == 32 {
299                 ArchSuffix::_64
300             } else {
301                 ArchSuffix::None
302             },
303              to_nbits <= 32 && cast_unsigned_to_signed,
304              ArchSuffix::_32)
305         }
306         (false, true) => {
307             (from_nbits == 64,
308              ArchSuffix::_32,
309              cast_unsigned_to_signed,
310              if from_nbits == 64 {
311                 ArchSuffix::_64
312             } else {
313                 ArchSuffix::_32
314             })
315         }
316     };
317     if span_truncation {
318         span_lint(cx,
319                   CAST_POSSIBLE_TRUNCATION,
320                   expr.span,
321                   &format!("casting {} to {} may truncate the value{}",
322                            cast_from,
323                            cast_to,
324                            match suffix_truncation {
325                                ArchSuffix::_32 => arch_32_suffix,
326                                ArchSuffix::_64 => arch_64_suffix,
327                                ArchSuffix::None => "",
328                            }));
329     }
330     if span_wrap {
331         span_lint(cx,
332                   CAST_POSSIBLE_WRAP,
333                   expr.span,
334                   &format!("casting {} to {} may wrap around the value{}",
335                            cast_from,
336                            cast_to,
337                            match suffix_wrap {
338                                ArchSuffix::_32 => arch_32_suffix,
339                                ArchSuffix::_64 => arch_64_suffix,
340                                ArchSuffix::None => "",
341                            }));
342     }
343 }
344
345 impl LintPass for CastPass {
346     fn get_lints(&self) -> LintArray {
347         lint_array!(CAST_PRECISION_LOSS,
348                     CAST_SIGN_LOSS,
349                     CAST_POSSIBLE_TRUNCATION,
350                     CAST_POSSIBLE_WRAP)
351     }
352 }
353
354 impl LateLintPass for CastPass {
355     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
356         if let ExprCast(ref ex, _) = expr.node {
357             let (cast_from, cast_to) = (cx.tcx.expr_ty(ex), cx.tcx.expr_ty(expr));
358             if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx, expr.span) {
359                 match (cast_from.is_integral(), cast_to.is_integral()) {
360                     (true, false) => {
361                         let from_nbits = int_ty_to_nbits(cast_from);
362                         let to_nbits = if let ty::TyFloat(FloatTy::F32) = cast_to.sty {
363                             32
364                         } else {
365                             64
366                         };
367                         if is_isize_or_usize(cast_from) || from_nbits >= to_nbits {
368                             span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64);
369                         }
370                     }
371                     (false, true) => {
372                         span_lint(cx,
373                                   CAST_POSSIBLE_TRUNCATION,
374                                   expr.span,
375                                   &format!("casting {} to {} may truncate the value", cast_from, cast_to));
376                         if !cast_to.is_signed() {
377                             span_lint(cx,
378                                       CAST_SIGN_LOSS,
379                                       expr.span,
380                                       &format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
381                         }
382                     }
383                     (true, true) => {
384                         if cast_from.is_signed() && !cast_to.is_signed() {
385                             span_lint(cx,
386                                       CAST_SIGN_LOSS,
387                                       expr.span,
388                                       &format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
389                         }
390                         check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
391                     }
392                     (false, false) => {
393                         if let (&ty::TyFloat(FloatTy::F64), &ty::TyFloat(FloatTy::F32)) = (&cast_from.sty,
394                                                                                            &cast_to.sty) {
395                             span_lint(cx,
396                                       CAST_POSSIBLE_TRUNCATION,
397                                       expr.span,
398                                       "casting f64 to f32 may truncate the value");
399                         }
400                     }
401                 }
402             }
403         }
404     }
405 }
406
407 /// **What it does:** This lint checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.
408 ///
409 /// **Why is this bad?** Too complex types make the code less readable. Consider using a `type` definition to simplify them.
410 ///
411 /// **Known problems:** None
412 ///
413 /// **Example:** `struct Foo { inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>> }`
414 declare_lint! {
415     pub TYPE_COMPLEXITY, Warn,
416     "usage of very complex types; recommends factoring out parts into `type` definitions"
417 }
418
419 #[allow(missing_copy_implementations)]
420 pub struct TypeComplexityPass {
421     threshold: u64,
422 }
423
424 impl TypeComplexityPass {
425     pub fn new(threshold: u64) -> Self {
426         TypeComplexityPass {
427             threshold: threshold
428         }
429     }
430 }
431
432 impl LintPass for TypeComplexityPass {
433     fn get_lints(&self) -> LintArray {
434         lint_array!(TYPE_COMPLEXITY)
435     }
436 }
437
438 impl LateLintPass for TypeComplexityPass {
439     fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
440         self.check_fndecl(cx, decl);
441     }
442
443     fn check_struct_field(&mut self, cx: &LateContext, field: &StructField) {
444         // enum variants are also struct fields now
445         self.check_type(cx, &field.ty);
446     }
447
448     fn check_item(&mut self, cx: &LateContext, item: &Item) {
449         match item.node {
450             ItemStatic(ref ty, _, _) |
451             ItemConst(ref ty, _) => self.check_type(cx, ty),
452             // functions, enums, structs, impls and traits are covered
453             _ => (),
454         }
455     }
456
457     fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
458         match item.node {
459             ConstTraitItem(ref ty, _) |
460             TypeTraitItem(_, Some(ref ty)) => self.check_type(cx, ty),
461             MethodTraitItem(MethodSig { ref decl, .. }, None) => self.check_fndecl(cx, decl),
462             // methods with default impl are covered by check_fn
463             _ => (),
464         }
465     }
466
467     fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
468         match item.node {
469             ImplItemKind::Const(ref ty, _) |
470             ImplItemKind::Type(ref ty) => self.check_type(cx, ty),
471             // methods are covered by check_fn
472             _ => (),
473         }
474     }
475
476     fn check_local(&mut self, cx: &LateContext, local: &Local) {
477         if let Some(ref ty) = local.ty {
478             self.check_type(cx, ty);
479         }
480     }
481 }
482
483 impl TypeComplexityPass {
484     fn check_fndecl(&self, cx: &LateContext, decl: &FnDecl) {
485         for arg in &decl.inputs {
486             self.check_type(cx, &arg.ty);
487         }
488         if let Return(ref ty) = decl.output {
489             self.check_type(cx, ty);
490         }
491     }
492
493     fn check_type(&self, cx: &LateContext, ty: &Ty) {
494         if in_macro(cx, ty.span) {
495             return;
496         }
497         let score = {
498             let mut visitor = TypeComplexityVisitor {
499                 score: 0,
500                 nest: 1,
501             };
502             visitor.visit_ty(ty);
503             visitor.score
504         };
505
506         if score > self.threshold {
507             span_lint(cx,
508                       TYPE_COMPLEXITY,
509                       ty.span,
510                       "very complex type used. Consider factoring parts into `type` definitions");
511         }
512     }
513 }
514
515 /// Walks a type and assigns a complexity score to it.
516 struct TypeComplexityVisitor {
517     /// total complexity score of the type
518     score: u64,
519     /// current nesting level
520     nest: u64,
521 }
522
523 impl<'v> Visitor<'v> for TypeComplexityVisitor {
524     fn visit_ty(&mut self, ty: &'v Ty) {
525         let (add_score, sub_nest) = match ty.node {
526             // _, &x and *x have only small overhead; don't mess with nesting level
527             TyInfer |
528             TyPtr(..) |
529             TyRptr(..) => (1, 0),
530
531             // the "normal" components of a type: named types, arrays/tuples
532             TyPath(..) |
533             TyVec(..) |
534             TyTup(..) |
535             TyFixedLengthVec(..) => (10 * self.nest, 1),
536
537             // "Sum" of trait bounds
538             TyObjectSum(..) => (20 * self.nest, 0),
539
540             // function types and "for<...>" bring a lot of overhead
541             TyBareFn(..) |
542             TyPolyTraitRef(..) => (50 * self.nest, 1),
543
544             _ => (0, 0),
545         };
546         self.score += add_score;
547         self.nest += sub_nest;
548         walk_ty(self, ty);
549         self.nest -= sub_nest;
550     }
551 }
552
553 /// **What it does:** This lint points out expressions where a character literal is casted to `u8` and suggests using a byte literal instead.
554 ///
555 /// **Why is this bad?** In general, casting values to smaller types is error-prone and should be avoided where possible. In the particular case of converting a character literal to u8, it is easy to avoid by just using a byte literal instead. As an added bonus, `b'a'` is even slightly shorter than `'a' as u8`.
556 ///
557 /// **Known problems:** None
558 ///
559 /// **Example:** `'x' as u8`
560 declare_lint! {
561     pub CHAR_LIT_AS_U8, Warn,
562     "Casting a character literal to u8"
563 }
564
565 pub struct CharLitAsU8;
566
567 impl LintPass for CharLitAsU8 {
568     fn get_lints(&self) -> LintArray {
569         lint_array!(CHAR_LIT_AS_U8)
570     }
571 }
572
573 impl LateLintPass for CharLitAsU8 {
574     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
575         use syntax::ast::{LitKind, UintTy};
576
577         if let ExprCast(ref e, _) = expr.node {
578             if let ExprLit(ref l) = e.node {
579                 if let LitKind::Char(_) = l.node {
580                     if ty::TyUint(UintTy::U8) == cx.tcx.expr_ty(expr).sty && !in_macro(cx, expr.span) {
581                         let msg = "casting character literal to u8. `char`s \
582                                    are 4 bytes wide in rust, so casting to u8 \
583                                    truncates them";
584                         let help = format!("Consider using a byte literal \
585                                             instead:\nb{}",
586                                            snippet(cx, e.span, "'x'"));
587                         span_help_and_lint(cx, CHAR_LIT_AS_U8, expr.span, msg, &help);
588                     }
589                 }
590             }
591         }
592     }
593 }
594
595 /// **What it does:** This lint checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a case that is always true or always false. Only integer and boolean types are checked.
596 ///
597 /// **Why is this bad?** An expression like `min <= x` may misleadingly imply that is is possible for `x` to be less than the minimum. Expressions like `max < x` are probably mistakes.
598 ///
599 /// **Known problems:** None
600 ///
601 /// **Example:** `vec.len() <= 0`, `100 > std::i32::MAX`
602 declare_lint! {
603     pub ABSURD_EXTREME_COMPARISONS, Warn,
604     "a comparison involving a maximum or minimum value involves a case that is always \
605     true or always false"
606 }
607
608 pub struct AbsurdExtremeComparisons;
609
610 impl LintPass for AbsurdExtremeComparisons {
611     fn get_lints(&self) -> LintArray {
612         lint_array!(ABSURD_EXTREME_COMPARISONS)
613     }
614 }
615
616 enum ExtremeType {
617     Minimum,
618     Maximum,
619 }
620
621 struct ExtremeExpr<'a> {
622     which: ExtremeType,
623     expr: &'a Expr,
624 }
625
626 enum AbsurdComparisonResult {
627     AlwaysFalse,
628     AlwaysTrue,
629     InequalityImpossible,
630 }
631
632 fn detect_absurd_comparison<'a>(cx: &LateContext, op: BinOp_, lhs: &'a Expr, rhs: &'a Expr)
633                                 -> Option<(ExtremeExpr<'a>, AbsurdComparisonResult)> {
634     use types::ExtremeType::*;
635     use types::AbsurdComparisonResult::*;
636     type Extr<'a> = ExtremeExpr<'a>;
637
638     // Put the expression in the form lhs < rhs or lhs <= rhs.
639     enum Rel {
640         Lt,
641         Le,
642     };
643     let (rel, lhs2, rhs2) = match op {
644         BiLt => (Rel::Lt, lhs, rhs),
645         BiLe => (Rel::Le, lhs, rhs),
646         BiGt => (Rel::Lt, rhs, lhs),
647         BiGe => (Rel::Le, rhs, lhs),
648         _ => return None,
649     };
650
651     let lx = detect_extreme_expr(cx, lhs2);
652     let rx = detect_extreme_expr(cx, rhs2);
653
654     Some(match rel {
655         Rel::Lt => {
656             match (lx, rx) {
657                 (Some(l @ Extr { which: Maximum, ..}), _) => (l, AlwaysFalse), // max < x
658                 (_, Some(r @ Extr { which: Minimum, ..})) => (r, AlwaysFalse), // x < min
659                 _ => return None,
660             }
661         }
662         Rel::Le => {
663             match (lx, rx) {
664                 (Some(l @ Extr { which: Minimum, ..}), _) => (l, AlwaysTrue), // min <= x
665                 (Some(l @ Extr { which: Maximum, ..}), _) => (l, InequalityImpossible), //max <= x
666                 (_, Some(r @ Extr { which: Minimum, ..})) => (r, InequalityImpossible), // x <= min
667                 (_, Some(r @ Extr { which: Maximum, ..})) => (r, AlwaysTrue), // x <= max
668                 _ => return None,
669             }
670         }
671     })
672 }
673
674 fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeExpr<'a>> {
675     use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
676     use types::ExtremeType::*;
677     use rustc::middle::const_eval::ConstVal::*;
678
679     let ty = &cx.tcx.expr_ty(expr).sty;
680
681     match *ty {
682         ty::TyBool | ty::TyInt(_) | ty::TyUint(_) => (),
683         _ => return None,
684     };
685
686     let cv = match const_eval::eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None) {
687         Ok(val) => val,
688         Err(_) => return None,
689     };
690
691     let which = match (ty, cv) {
692         (&ty::TyBool, Bool(false)) => Minimum,
693
694         (&ty::TyInt(IntTy::Is), Int(x)) if x == ::std::isize::MIN as i64 => Minimum,
695         (&ty::TyInt(IntTy::I8), Int(x)) if x == ::std::i8::MIN as i64 => Minimum,
696         (&ty::TyInt(IntTy::I16), Int(x)) if x == ::std::i16::MIN as i64 => Minimum,
697         (&ty::TyInt(IntTy::I32), Int(x)) if x == ::std::i32::MIN as i64 => Minimum,
698         (&ty::TyInt(IntTy::I64), Int(x)) if x == ::std::i64::MIN as i64 => Minimum,
699
700         (&ty::TyUint(UintTy::Us), Uint(x)) if x == ::std::usize::MIN as u64 => Minimum,
701         (&ty::TyUint(UintTy::U8), Uint(x)) if x == ::std::u8::MIN as u64 => Minimum,
702         (&ty::TyUint(UintTy::U16), Uint(x)) if x == ::std::u16::MIN as u64 => Minimum,
703         (&ty::TyUint(UintTy::U32), Uint(x)) if x == ::std::u32::MIN as u64 => Minimum,
704         (&ty::TyUint(UintTy::U64), Uint(x)) if x == ::std::u64::MIN as u64 => Minimum,
705
706         (&ty::TyBool, Bool(true)) => Maximum,
707
708         (&ty::TyInt(IntTy::Is), Int(x)) if x == ::std::isize::MAX as i64 => Maximum,
709         (&ty::TyInt(IntTy::I8), Int(x)) if x == ::std::i8::MAX as i64 => Maximum,
710         (&ty::TyInt(IntTy::I16), Int(x)) if x == ::std::i16::MAX as i64 => Maximum,
711         (&ty::TyInt(IntTy::I32), Int(x)) if x == ::std::i32::MAX as i64 => Maximum,
712         (&ty::TyInt(IntTy::I64), Int(x)) if x == ::std::i64::MAX as i64 => Maximum,
713
714         (&ty::TyUint(UintTy::Us), Uint(x)) if x == ::std::usize::MAX as u64 => Maximum,
715         (&ty::TyUint(UintTy::U8), Uint(x)) if x == ::std::u8::MAX as u64 => Maximum,
716         (&ty::TyUint(UintTy::U16), Uint(x)) if x == ::std::u16::MAX as u64 => Maximum,
717         (&ty::TyUint(UintTy::U32), Uint(x)) if x == ::std::u32::MAX as u64 => Maximum,
718         (&ty::TyUint(UintTy::U64), Uint(x)) if x == ::std::u64::MAX as u64 => Maximum,
719
720         _ => return None,
721     };
722     Some(ExtremeExpr {
723         which: which,
724         expr: expr,
725     })
726 }
727
728 impl LateLintPass for AbsurdExtremeComparisons {
729     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
730         use types::ExtremeType::*;
731         use types::AbsurdComparisonResult::*;
732
733         if let ExprBinary(ref cmp, ref lhs, ref rhs) = expr.node {
734             if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
735                 if !in_macro(cx, expr.span) {
736                     let msg = "this comparison involving the minimum or maximum element for this \
737                                type contains a case that is always true or always false";
738
739                     let conclusion = match result {
740                         AlwaysFalse => "this comparison is always false".to_owned(),
741                         AlwaysTrue => "this comparison is always true".to_owned(),
742                         InequalityImpossible => {
743                             format!("the case where the two sides are not equal never occurs, consider using {} == {} \
744                                      instead",
745                                     snippet(cx, lhs.span, "lhs"),
746                                     snippet(cx, rhs.span, "rhs"))
747                         }
748                     };
749
750                     let help = format!("because {} is the {} value for this type, {}",
751                                        snippet(cx, culprit.expr.span, "x"),
752                                        match culprit.which {
753                                            Minimum => "minimum",
754                                            Maximum => "maximum",
755                                        },
756                                        conclusion);
757
758                     span_help_and_lint(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, &help);
759                 }
760             }
761         }
762     }
763 }