]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lint.rs
b3b690c804d231282dff3eaebcf594adeaa1d52c
[rust.git] / src / librustc / middle / lint.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! A 'lint' check is a kind of miscellaneous constraint that a user _might_
12 //! want to enforce, but might reasonably want to permit as well, on a
13 //! module-by-module basis. They contrast with static constraints enforced by
14 //! other phases of the compiler, which are generally required to hold in order
15 //! to compile the program at all.
16 //!
17 //! The lint checking is all consolidated into one pass which runs just before
18 //! translation to LLVM bytecode. Throughout compilation, lint warnings can be
19 //! added via the `add_lint` method on the Session structure. This requires a
20 //! span and an id of the node that the lint is being added to. The lint isn't
21 //! actually emitted at that time because it is unknown what the actual lint
22 //! level at that location is.
23 //!
24 //! To actually emit lint warnings/errors, a separate pass is used just before
25 //! translation. A context keeps track of the current state of all lint levels.
26 //! Upon entering a node of the ast which can modify the lint settings, the
27 //! previous lint state is pushed onto a stack and the ast is then recursed
28 //! upon.  As the ast is traversed, this keeps track of the current lint level
29 //! for all lint attributes.
30 //!
31 //! To add a new lint warning, all you need to do is to either invoke `add_lint`
32 //! on the session at the appropriate time, or write a few linting functions and
33 //! modify the Context visitor appropriately. If you're adding lints from the
34 //! Context itself, span_lint should be used instead of add_lint.
35
36 #![allow(non_camel_case_types)]
37
38 use driver::session;
39 use metadata::csearch;
40 use middle::dead::DEAD_CODE_LINT_STR;
41 use middle::def;
42 use middle::def::*;
43 use middle::pat_util;
44 use middle::privacy;
45 use middle::trans::adt; // for `adt::is_ffi_safe`
46 use middle::ty;
47 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
48 use middle::typeck::infer;
49 use middle::typeck;
50 use util::ppaux::{ty_to_str};
51 use util::nodemap::NodeSet;
52
53 use std::cmp;
54 use std::collections::HashMap;
55 use std::i16;
56 use std::i32;
57 use std::i64;
58 use std::i8;
59 use std::rc::Rc;
60 use std::gc::Gc;
61 use std::to_str::ToStr;
62 use std::u16;
63 use std::u32;
64 use std::u64;
65 use std::u8;
66 use std::collections::SmallIntMap;
67 use syntax::abi;
68 use syntax::ast_map;
69 use syntax::ast_util::IdVisitingOperation;
70 use syntax::attr::AttrMetaMethods;
71 use syntax::attr;
72 use syntax::codemap::Span;
73 use syntax::parse::token::InternedString;
74 use syntax::parse::token;
75 use syntax::visit::Visitor;
76 use syntax::{ast, ast_util, visit};
77
78 #[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)]
79 pub enum Lint {
80     CTypes,
81     UnusedImports,
82     UnnecessaryQualification,
83     WhileTrue,
84     PathStatement,
85     UnrecognizedLint,
86     NonCamelCaseTypes,
87     NonUppercaseStatics,
88     NonUppercasePatternStatics,
89     NonSnakeCaseFunctions,
90     UppercaseVariables,
91     UnnecessaryParens,
92     TypeLimits,
93     TypeOverflow,
94     UnusedUnsafe,
95     UnsafeBlock,
96     UnusedAttribute,
97     UnknownFeatures,
98     UnknownCrateType,
99     UnsignedNegate,
100     VariantSizeDifference,
101
102     ManagedHeapMemory,
103     OwnedHeapMemory,
104     HeapMemory,
105
106     UnusedVariable,
107     DeadAssignment,
108     UnusedMut,
109     UnnecessaryAllocation,
110     DeadCode,
111     VisiblePrivateTypes,
112     UnnecessaryTypecast,
113
114     MissingDoc,
115     UnreachableCode,
116
117     Deprecated,
118     Experimental,
119     Unstable,
120
121     UnusedMustUse,
122     UnusedResult,
123
124     Warnings,
125
126     RawPointerDeriving,
127 }
128
129 pub fn level_to_str(lv: Level) -> &'static str {
130     match lv {
131       Allow => "allow",
132       Warn => "warn",
133       Deny => "deny",
134       Forbid => "forbid"
135     }
136 }
137
138 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
139 pub enum Level {
140     Allow, Warn, Deny, Forbid
141 }
142
143 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
144 pub struct LintSpec {
145     pub default: Level,
146     pub lint: Lint,
147     pub desc: &'static str,
148 }
149
150 pub type LintDict = HashMap<&'static str, LintSpec>;
151
152 // this is public for the lints that run in trans
153 #[deriving(PartialEq)]
154 pub enum LintSource {
155     Node(Span),
156     Default,
157     CommandLine
158 }
159
160 static lint_table: &'static [(&'static str, LintSpec)] = &[
161     ("ctypes",
162      LintSpec {
163         lint: CTypes,
164         desc: "proper use of libc types in foreign modules",
165         default: Warn
166      }),
167
168     ("unused_imports",
169      LintSpec {
170         lint: UnusedImports,
171         desc: "imports that are never used",
172         default: Warn
173      }),
174
175     ("unnecessary_qualification",
176      LintSpec {
177         lint: UnnecessaryQualification,
178         desc: "detects unnecessarily qualified names",
179         default: Allow
180      }),
181
182     ("while_true",
183      LintSpec {
184         lint: WhileTrue,
185         desc: "suggest using `loop { }` instead of `while true { }`",
186         default: Warn
187      }),
188
189     ("path_statement",
190      LintSpec {
191         lint: PathStatement,
192         desc: "path statements with no effect",
193         default: Warn
194      }),
195
196     ("unrecognized_lint",
197      LintSpec {
198         lint: UnrecognizedLint,
199         desc: "unrecognized lint attribute",
200         default: Warn
201      }),
202
203     ("non_camel_case_types",
204      LintSpec {
205         lint: NonCamelCaseTypes,
206         desc: "types, variants and traits should have camel case names",
207         default: Warn
208      }),
209
210     ("non_uppercase_statics",
211      LintSpec {
212          lint: NonUppercaseStatics,
213          desc: "static constants should have uppercase identifiers",
214          default: Allow
215      }),
216
217     ("non_uppercase_pattern_statics",
218      LintSpec {
219          lint: NonUppercasePatternStatics,
220          desc: "static constants in match patterns should be all caps",
221          default: Warn
222      }),
223
224     ("non_snake_case_functions",
225      LintSpec {
226          lint: NonSnakeCaseFunctions,
227          desc: "methods and functions should have snake case names",
228          default: Warn
229      }),
230
231     ("uppercase_variables",
232      LintSpec {
233          lint: UppercaseVariables,
234          desc: "variable and structure field names should start with a lowercase character",
235          default: Warn
236      }),
237
238      ("unnecessary_parens",
239      LintSpec {
240         lint: UnnecessaryParens,
241         desc: "`if`, `match`, `while` and `return` do not need parentheses",
242         default: Warn
243      }),
244
245     ("managed_heap_memory",
246      LintSpec {
247         lint: ManagedHeapMemory,
248         desc: "use of managed (@ type) heap memory",
249         default: Allow
250      }),
251
252     ("owned_heap_memory",
253      LintSpec {
254         lint: OwnedHeapMemory,
255         desc: "use of owned (Box type) heap memory",
256         default: Allow
257      }),
258
259     ("heap_memory",
260      LintSpec {
261         lint: HeapMemory,
262         desc: "use of any (Box type or @ type) heap memory",
263         default: Allow
264      }),
265
266     ("type_limits",
267      LintSpec {
268         lint: TypeLimits,
269         desc: "comparisons made useless by limits of the types involved",
270         default: Warn
271      }),
272
273     ("type_overflow",
274      LintSpec {
275         lint: TypeOverflow,
276         desc: "literal out of range for its type",
277         default: Warn
278      }),
279
280
281     ("unused_unsafe",
282      LintSpec {
283         lint: UnusedUnsafe,
284         desc: "unnecessary use of an `unsafe` block",
285         default: Warn
286     }),
287
288     ("unsafe_block",
289      LintSpec {
290         lint: UnsafeBlock,
291         desc: "usage of an `unsafe` block",
292         default: Allow
293     }),
294
295     ("unused_attribute",
296      LintSpec {
297          lint: UnusedAttribute,
298          desc: "detects attributes that were not used by the compiler",
299          default: Warn
300     }),
301
302     ("unused_variable",
303      LintSpec {
304         lint: UnusedVariable,
305         desc: "detect variables which are not used in any way",
306         default: Warn
307     }),
308
309     ("dead_assignment",
310      LintSpec {
311         lint: DeadAssignment,
312         desc: "detect assignments that will never be read",
313         default: Warn
314     }),
315
316     ("unnecessary_typecast",
317      LintSpec {
318         lint: UnnecessaryTypecast,
319         desc: "detects unnecessary type casts, that can be removed",
320         default: Allow,
321     }),
322
323     ("unused_mut",
324      LintSpec {
325         lint: UnusedMut,
326         desc: "detect mut variables which don't need to be mutable",
327         default: Warn
328     }),
329
330     ("unnecessary_allocation",
331      LintSpec {
332         lint: UnnecessaryAllocation,
333         desc: "detects unnecessary allocations that can be eliminated",
334         default: Warn
335     }),
336
337     (DEAD_CODE_LINT_STR,
338      LintSpec {
339         lint: DeadCode,
340         desc: "detect piece of code that will never be used",
341         default: Warn
342     }),
343     ("visible_private_types",
344      LintSpec {
345         lint: VisiblePrivateTypes,
346         desc: "detect use of private types in exported type signatures",
347         default: Warn
348     }),
349
350     ("missing_doc",
351      LintSpec {
352         lint: MissingDoc,
353         desc: "detects missing documentation for public members",
354         default: Allow
355     }),
356
357     ("unreachable_code",
358      LintSpec {
359         lint: UnreachableCode,
360         desc: "detects unreachable code",
361         default: Warn
362     }),
363
364     ("deprecated",
365      LintSpec {
366         lint: Deprecated,
367         desc: "detects use of #[deprecated] items",
368         default: Warn
369     }),
370
371     ("experimental",
372      LintSpec {
373         lint: Experimental,
374         desc: "detects use of #[experimental] items",
375         default: Warn
376     }),
377
378     ("unstable",
379      LintSpec {
380         lint: Unstable,
381         desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
382         default: Allow
383     }),
384
385     ("warnings",
386      LintSpec {
387         lint: Warnings,
388         desc: "mass-change the level for lints which produce warnings",
389         default: Warn
390     }),
391
392     ("unknown_features",
393      LintSpec {
394         lint: UnknownFeatures,
395         desc: "unknown features found in crate-level #[feature] directives",
396         default: Deny,
397     }),
398
399     ("unknown_crate_type",
400     LintSpec {
401         lint: UnknownCrateType,
402         desc: "unknown crate type found in #[crate_type] directive",
403         default: Deny,
404     }),
405
406     ("unsigned_negate",
407     LintSpec {
408         lint: UnsignedNegate,
409         desc: "using an unary minus operator on unsigned type",
410         default: Warn
411     }),
412
413     ("variant_size_difference",
414     LintSpec {
415         lint: VariantSizeDifference,
416         desc: "detects enums with widely varying variant sizes",
417         default: Allow,
418     }),
419
420     ("unused_must_use",
421     LintSpec {
422         lint: UnusedMustUse,
423         desc: "unused result of a type flagged as #[must_use]",
424         default: Warn,
425     }),
426
427     ("unused_result",
428     LintSpec {
429         lint: UnusedResult,
430         desc: "unused result of an expression in a statement",
431         default: Allow,
432     }),
433
434     ("raw_pointer_deriving",
435      LintSpec {
436         lint: RawPointerDeriving,
437         desc: "uses of #[deriving] with raw pointers are rarely correct",
438         default: Warn,
439     }),
440 ];
441
442 /*
443   Pass names should not contain a '-', as the compiler normalizes
444   '-' to '_' in command-line flags
445  */
446 pub fn get_lint_dict() -> LintDict {
447     lint_table.iter().map(|&(k, v)| (k, v)).collect()
448 }
449
450 struct Context<'a> {
451     /// All known lint modes (string versions)
452     dict: LintDict,
453     /// Current levels of each lint warning
454     cur: SmallIntMap<(Level, LintSource)>,
455     /// Context we're checking in (used to access fields like sess)
456     tcx: &'a ty::ctxt,
457     /// Items exported by the crate; used by the missing_doc lint.
458     exported_items: &'a privacy::ExportedItems,
459     /// The id of the current `ast::StructDef` being walked.
460     cur_struct_def_id: ast::NodeId,
461     /// Whether some ancestor of the current node was marked
462     /// #[doc(hidden)].
463     is_doc_hidden: bool,
464
465     /// When recursing into an attributed node of the ast which modifies lint
466     /// levels, this stack keeps track of the previous lint levels of whatever
467     /// was modified.
468     lint_stack: Vec<(Lint, Level, LintSource)>,
469
470     /// Id of the last visited negated expression
471     negated_expr_id: ast::NodeId,
472
473     /// Ids of structs/enums which have been checked for raw_pointer_deriving
474     checked_raw_pointers: NodeSet,
475
476     /// Level of lints for certain NodeIds, stored here because the body of
477     /// the lint needs to run in trans.
478     node_levels: HashMap<(ast::NodeId, Lint), (Level, LintSource)>,
479 }
480
481 pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
482                  lint_str: &str, tcx: &ty::ctxt) {
483     if level == Allow { return }
484
485     let mut note = None;
486     let msg = match src {
487         #[cfg(stage0)]
488         Default => {
489             format!("{}, \\#[{}({})] on by default", msg,
490                 level_to_str(level), lint_str)
491         },
492         #[cfg(not(stage0))]
493         Default => {
494             format!("{}, #[{}({})] on by default", msg,
495                 level_to_str(level), lint_str)
496         },
497         CommandLine => {
498             format!("{} [-{} {}]", msg,
499                 match level {
500                     Warn => 'W', Deny => 'D', Forbid => 'F',
501                     Allow => fail!()
502                 }, lint_str.replace("_", "-"))
503         },
504         Node(src) => {
505             note = Some(src);
506             msg.to_str()
507         }
508     };
509
510     match level {
511         Warn =>          { tcx.sess.span_warn(span, msg.as_slice()); }
512         Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice());  }
513         Allow => fail!(),
514     }
515
516     for &span in note.iter() {
517         tcx.sess.span_note(span, "lint level defined here");
518     }
519 }
520
521 pub fn lint_to_str(lint: Lint) -> &'static str {
522     for &(name, lspec) in lint_table.iter() {
523         if lspec.lint == lint {
524             return name;
525         }
526     }
527
528     fail!("unrecognized lint: {}", lint);
529 }
530
531 impl<'a> Context<'a> {
532     fn get_level(&self, lint: Lint) -> Level {
533         match self.cur.find(&(lint as uint)) {
534           Some(&(lvl, _)) => lvl,
535           None => Allow
536         }
537     }
538
539     fn get_source(&self, lint: Lint) -> LintSource {
540         match self.cur.find(&(lint as uint)) {
541           Some(&(_, src)) => src,
542           None => Default
543         }
544     }
545
546     fn set_level(&mut self, lint: Lint, level: Level, src: LintSource) {
547         if level == Allow {
548             self.cur.remove(&(lint as uint));
549         } else {
550             self.cur.insert(lint as uint, (level, src));
551         }
552     }
553
554     fn lint_to_str(&self, lint: Lint) -> &'static str {
555         for (k, v) in self.dict.iter() {
556             if v.lint == lint {
557                 return *k;
558             }
559         }
560         fail!("unregistered lint {}", lint);
561     }
562
563     fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
564         let (level, src) = match self.cur.find(&(lint as uint)) {
565             None => { return }
566             Some(&(Warn, src)) => (self.get_level(Warnings), src),
567             Some(&pair) => pair,
568         };
569
570         emit_lint(level, src, msg, span, self.lint_to_str(lint), self.tcx);
571     }
572
573     /**
574      * Merge the lints specified by any lint attributes into the
575      * current lint context, call the provided function, then reset the
576      * lints in effect to their previous state.
577      */
578     fn with_lint_attrs(&mut self,
579                        attrs: &[ast::Attribute],
580                        f: |&mut Context|) {
581         // Parse all of the lint attributes, and then add them all to the
582         // current dictionary of lint information. Along the way, keep a history
583         // of what we changed so we can roll everything back after invoking the
584         // specified closure
585         let mut pushed = 0u;
586         each_lint(&self.tcx.sess, attrs, |meta, level, lintname| {
587             match self.dict.find_equiv(&lintname) {
588                 None => {
589                     self.span_lint(
590                         UnrecognizedLint,
591                         meta.span,
592                         format!("unknown `{}` attribute: `{}`",
593                                 level_to_str(level), lintname).as_slice());
594                 }
595                 Some(lint) => {
596                     let lint = lint.lint;
597                     let now = self.get_level(lint);
598                     if now == Forbid && level != Forbid {
599                         self.tcx.sess.span_err(meta.span,
600                         format!("{}({}) overruled by outer forbid({})",
601                                 level_to_str(level),
602                                 lintname,
603                                 lintname).as_slice());
604                     } else if now != level {
605                         let src = self.get_source(lint);
606                         self.lint_stack.push((lint, now, src));
607                         pushed += 1;
608                         self.set_level(lint, level, Node(meta.span));
609                     }
610                 }
611             }
612             true
613         });
614
615         let old_is_doc_hidden = self.is_doc_hidden;
616         self.is_doc_hidden =
617             self.is_doc_hidden ||
618             attrs.iter()
619                  .any(|attr| {
620                      attr.name().equiv(&("doc")) &&
621                      match attr.meta_item_list() {
622                          None => false,
623                          Some(l) => {
624                              attr::contains_name(l.as_slice(), "hidden")
625                          }
626                      }
627                  });
628
629         f(self);
630
631         // rollback
632         self.is_doc_hidden = old_is_doc_hidden;
633         for _ in range(0, pushed) {
634             let (lint, lvl, src) = self.lint_stack.pop().unwrap();
635             self.set_level(lint, lvl, src);
636         }
637     }
638
639     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
640         let mut v = ast_util::IdVisitor {
641             operation: self,
642             pass_through_items: false,
643             visited_outermost: false,
644         };
645         f(&mut v);
646     }
647 }
648
649 /// Check that every lint from the list of attributes satisfies `f`.
650 /// Return true if that's the case. Otherwise return false.
651 pub fn each_lint(sess: &session::Session,
652                  attrs: &[ast::Attribute],
653                  f: |Gc<ast::MetaItem>, Level, InternedString| -> bool)
654                  -> bool {
655     let xs = [Allow, Warn, Deny, Forbid];
656     for &level in xs.iter() {
657         let level_name = level_to_str(level);
658         for attr in attrs.iter().filter(|m| m.check_name(level_name)) {
659             let meta = attr.node.value;
660             let metas = match meta.node {
661                 ast::MetaList(_, ref metas) => metas,
662                 _ => {
663                     sess.span_err(meta.span, "malformed lint attribute");
664                     continue;
665                 }
666             };
667             for meta in metas.iter() {
668                 match meta.node {
669                     ast::MetaWord(ref lintname) => {
670                         if !f(*meta, level, (*lintname).clone()) {
671                             return false;
672                         }
673                     }
674                     _ => {
675                         sess.span_err(meta.span, "malformed lint attribute");
676                     }
677                 }
678             }
679         }
680     }
681     true
682 }
683
684 /// Check from a list of attributes if it contains the appropriate
685 /// `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
686 pub fn contains_lint(attrs: &[ast::Attribute],
687                      level: Level,
688                      lintname: &'static str)
689                      -> bool {
690     let level_name = level_to_str(level);
691     for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
692         if attr.meta_item_list().is_none() {
693             continue
694         }
695         let list = attr.meta_item_list().unwrap();
696         for meta_item in list.iter() {
697             if meta_item.name().equiv(&lintname) {
698                 return true;
699             }
700         }
701     }
702     false
703 }
704
705 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
706     match e.node {
707         ast::ExprWhile(cond, _) => {
708             match cond.node {
709                 ast::ExprLit(lit) => {
710                     match lit.node {
711                         ast::LitBool(true) => {
712                             cx.span_lint(WhileTrue,
713                                          e.span,
714                                          "denote infinite loops with loop \
715                                           { ... }");
716                         }
717                         _ => {}
718                     }
719                 }
720                 _ => ()
721             }
722         }
723         _ => ()
724     }
725 }
726 impl<'a> AstConv for Context<'a>{
727     fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
728
729     fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
730         ty::lookup_item_type(self.tcx, id)
731     }
732
733     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
734         ty::lookup_trait_def(self.tcx, id)
735     }
736
737     fn ty_infer(&self, _span: Span) -> ty::t {
738         infer::new_infer_ctxt(self.tcx).next_ty_var()
739     }
740 }
741
742
743 fn check_unused_casts(cx: &Context, e: &ast::Expr) {
744     return match e.node {
745         ast::ExprCast(expr, ty) => {
746             let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), &*ty);
747             if  ty::get(ty::expr_ty(cx.tcx, &*expr)).sty == ty::get(t_t).sty {
748                 cx.span_lint(UnnecessaryTypecast, ty.span,
749                              "unnecessary type cast");
750             }
751         }
752         _ => ()
753     };
754 }
755
756 fn check_type_limits(cx: &Context, e: &ast::Expr) {
757     return match e.node {
758         ast::ExprUnary(ast::UnNeg, ex) => {
759             match ex.node  {
760                 ast::ExprLit(lit) => {
761                     match lit.node {
762                         ast::LitUint(..) => {
763                             cx.span_lint(UnsignedNegate, e.span,
764                                          "negation of unsigned int literal may be unintentional");
765                         },
766                         _ => ()
767                     }
768                 },
769                 _ => {
770                     let t = ty::expr_ty(cx.tcx, &*ex);
771                     match ty::get(t).sty {
772                         ty::ty_uint(_) => {
773                             cx.span_lint(UnsignedNegate, e.span,
774                                          "negation of unsigned int variable may be unintentional");
775                         },
776                         _ => ()
777                     }
778                 }
779             }
780         },
781         ast::ExprBinary(binop, l, r) => {
782             if is_comparison(binop) && !check_limits(cx.tcx, binop, &*l, &*r) {
783                 cx.span_lint(TypeLimits, e.span,
784                              "comparison is useless due to type limits");
785             }
786         },
787         ast::ExprLit(lit) => {
788             match ty::get(ty::expr_ty(cx.tcx, e)).sty {
789                 ty::ty_int(t) => {
790                     let int_type = if t == ast::TyI {
791                         cx.tcx.sess.targ_cfg.int_type
792                     } else { t };
793                     let (min, max) = int_ty_range(int_type);
794                     let mut lit_val: i64 = match lit.node {
795                         ast::LitInt(v, _) => v,
796                         ast::LitUint(v, _) => v as i64,
797                         ast::LitIntUnsuffixed(v) => v,
798                         _ => fail!()
799                     };
800                     if cx.negated_expr_id == e.id {
801                         lit_val *= -1;
802                     }
803                     if  lit_val < min || lit_val > max {
804                         cx.span_lint(TypeOverflow, e.span,
805                                      "literal out of range for its type");
806                     }
807                 },
808                 ty::ty_uint(t) => {
809                     let uint_type = if t == ast::TyU {
810                         cx.tcx.sess.targ_cfg.uint_type
811                     } else { t };
812                     let (min, max) = uint_ty_range(uint_type);
813                     let lit_val: u64 = match lit.node {
814                         ast::LitInt(v, _) => v as u64,
815                         ast::LitUint(v, _) => v,
816                         ast::LitIntUnsuffixed(v) => v as u64,
817                         _ => fail!()
818                     };
819                     if  lit_val < min || lit_val > max {
820                         cx.span_lint(TypeOverflow, e.span,
821                                      "literal out of range for its type");
822                     }
823                 },
824
825                 _ => ()
826             };
827         },
828         _ => ()
829     };
830
831     fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
832                             min: T, max: T) -> bool {
833         match binop {
834             ast::BiLt => v >  min && v <= max,
835             ast::BiLe => v >= min && v <  max,
836             ast::BiGt => v >= min && v <  max,
837             ast::BiGe => v >  min && v <= max,
838             ast::BiEq | ast::BiNe => v >= min && v <= max,
839             _ => fail!()
840         }
841     }
842
843     fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
844         match binop {
845             ast::BiLt => ast::BiGt,
846             ast::BiLe => ast::BiGe,
847             ast::BiGt => ast::BiLt,
848             ast::BiGe => ast::BiLe,
849             _ => binop
850         }
851     }
852
853     // for int & uint, be conservative with the warnings, so that the
854     // warnings are consistent between 32- and 64-bit platforms
855     fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
856         match int_ty {
857             ast::TyI =>    (i64::MIN,        i64::MAX),
858             ast::TyI8 =>   (i8::MIN  as i64, i8::MAX  as i64),
859             ast::TyI16 =>  (i16::MIN as i64, i16::MAX as i64),
860             ast::TyI32 =>  (i32::MIN as i64, i32::MAX as i64),
861             ast::TyI64 =>  (i64::MIN,        i64::MAX)
862         }
863     }
864
865     fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
866         match uint_ty {
867             ast::TyU =>   (u64::MIN,         u64::MAX),
868             ast::TyU8 =>  (u8::MIN   as u64, u8::MAX   as u64),
869             ast::TyU16 => (u16::MIN  as u64, u16::MAX  as u64),
870             ast::TyU32 => (u32::MIN  as u64, u32::MAX  as u64),
871             ast::TyU64 => (u64::MIN,         u64::MAX)
872         }
873     }
874
875     fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
876                     l: &ast::Expr, r: &ast::Expr) -> bool {
877         let (lit, expr, swap) = match (&l.node, &r.node) {
878             (&ast::ExprLit(_), _) => (l, r, true),
879             (_, &ast::ExprLit(_)) => (r, l, false),
880             _ => return true
881         };
882         // Normalize the binop so that the literal is always on the RHS in
883         // the comparison
884         let norm_binop = if swap { rev_binop(binop) } else { binop };
885         match ty::get(ty::expr_ty(tcx, expr)).sty {
886             ty::ty_int(int_ty) => {
887                 let (min, max) = int_ty_range(int_ty);
888                 let lit_val: i64 = match lit.node {
889                     ast::ExprLit(li) => match li.node {
890                         ast::LitInt(v, _) => v,
891                         ast::LitUint(v, _) => v as i64,
892                         ast::LitIntUnsuffixed(v) => v,
893                         _ => return true
894                     },
895                     _ => fail!()
896                 };
897                 is_valid(norm_binop, lit_val, min, max)
898             }
899             ty::ty_uint(uint_ty) => {
900                 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
901                 let lit_val: u64 = match lit.node {
902                     ast::ExprLit(li) => match li.node {
903                         ast::LitInt(v, _) => v as u64,
904                         ast::LitUint(v, _) => v,
905                         ast::LitIntUnsuffixed(v) => v as u64,
906                         _ => return true
907                     },
908                     _ => fail!()
909                 };
910                 is_valid(norm_binop, lit_val, min, max)
911             }
912             _ => true
913         }
914     }
915
916     fn is_comparison(binop: ast::BinOp) -> bool {
917         match binop {
918             ast::BiEq | ast::BiLt | ast::BiLe |
919             ast::BiNe | ast::BiGe | ast::BiGt => true,
920             _ => false
921         }
922     }
923 }
924
925 fn check_item_ctypes(cx: &Context, it: &ast::Item) {
926     fn check_ty(cx: &Context, ty: &ast::Ty) {
927         match ty.node {
928             ast::TyPath(_, _, id) => {
929                 match cx.tcx.def_map.borrow().get_copy(&id) {
930                     def::DefPrimTy(ast::TyInt(ast::TyI)) => {
931                         cx.span_lint(CTypes, ty.span,
932                                 "found rust type `int` in foreign module, while \
933                                 libc::c_int or libc::c_long should be used");
934                     }
935                     def::DefPrimTy(ast::TyUint(ast::TyU)) => {
936                         cx.span_lint(CTypes, ty.span,
937                                 "found rust type `uint` in foreign module, while \
938                                 libc::c_uint or libc::c_ulong should be used");
939                     }
940                     def::DefTy(def_id) => {
941                         if !adt::is_ffi_safe(cx.tcx, def_id) {
942                             cx.span_lint(CTypes, ty.span,
943                                          "found enum type without foreign-function-safe \
944                                           representation annotation in foreign module");
945                             // hmm... this message could be more helpful
946                         }
947                     }
948                     _ => ()
949                 }
950             }
951             ast::TyPtr(ref mt) => { check_ty(cx, &*mt.ty) }
952             _ => {}
953         }
954     }
955
956     fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
957         for input in decl.inputs.iter() {
958             check_ty(cx, &*input.ty);
959         }
960         check_ty(cx, &*decl.output)
961     }
962
963     match it.node {
964       ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
965         for ni in nmod.items.iter() {
966             match ni.node {
967                 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, &*decl),
968                 ast::ForeignItemStatic(t, _) => check_ty(cx, &*t)
969             }
970         }
971       }
972       _ => {/* nothing to do */ }
973     }
974 }
975
976 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
977     let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory];
978     for &lint in xs.iter() {
979         if cx.get_level(lint) == Allow { continue }
980
981         let mut n_box = 0;
982         let mut n_uniq = 0;
983         ty::fold_ty(cx.tcx, ty, |t| {
984             match ty::get(t).sty {
985                 ty::ty_box(_) => {
986                     n_box += 1;
987                 }
988                 ty::ty_uniq(_) |
989                 ty::ty_trait(box ty::TyTrait {
990                     store: ty::UniqTraitStore, ..
991                 }) |
992                 ty::ty_closure(box ty::ClosureTy {
993                     store: ty::UniqTraitStore,
994                     ..
995                 }) => {
996                     n_uniq += 1;
997                 }
998
999                 _ => ()
1000             };
1001             t
1002         });
1003
1004         if n_uniq > 0 && lint != ManagedHeapMemory {
1005             let s = ty_to_str(cx.tcx, ty);
1006             let m = format!("type uses owned (Box type) pointers: {}", s);
1007             cx.span_lint(lint, span, m.as_slice());
1008         }
1009
1010         if n_box > 0 && lint != OwnedHeapMemory {
1011             let s = ty_to_str(cx.tcx, ty);
1012             let m = format!("type uses managed (@ type) pointers: {}", s);
1013             cx.span_lint(lint, span, m.as_slice());
1014         }
1015     }
1016 }
1017
1018 fn check_heap_item(cx: &Context, it: &ast::Item) {
1019     match it.node {
1020         ast::ItemFn(..) |
1021         ast::ItemTy(..) |
1022         ast::ItemEnum(..) |
1023         ast::ItemStruct(..) => check_heap_type(cx, it.span,
1024                                                ty::node_id_to_type(cx.tcx,
1025                                                                    it.id)),
1026         _ => ()
1027     }
1028
1029     // If it's a struct, we also have to check the fields' types
1030     match it.node {
1031         ast::ItemStruct(struct_def, _) => {
1032             for struct_field in struct_def.fields.iter() {
1033                 check_heap_type(cx, struct_field.span,
1034                                 ty::node_id_to_type(cx.tcx,
1035                                                     struct_field.node.id));
1036             }
1037         }
1038         _ => ()
1039     }
1040 }
1041
1042 struct RawPtrDerivingVisitor<'a> {
1043     cx: &'a Context<'a>
1044 }
1045
1046 impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
1047     fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1048         static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
1049         match ty.node {
1050             ast::TyPtr(..) => self.cx.span_lint(RawPointerDeriving, ty.span, MSG),
1051             _ => {}
1052         }
1053         visit::walk_ty(self, ty, ());
1054     }
1055     // explicit override to a no-op to reduce code bloat
1056     fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1057     fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1058 }
1059
1060 fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) {
1061     if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
1062         return
1063     }
1064     let did = match item.node {
1065         ast::ItemImpl(..) => {
1066             match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
1067                 ty::ty_enum(did, _) => did,
1068                 ty::ty_struct(did, _) => did,
1069                 _ => return,
1070             }
1071         }
1072         _ => return,
1073     };
1074     if !ast_util::is_local(did) { return }
1075     let item = match cx.tcx.map.find(did.node) {
1076         Some(ast_map::NodeItem(item)) => item,
1077         _ => return,
1078     };
1079     if !cx.checked_raw_pointers.insert(item.id) { return }
1080     match item.node {
1081         ast::ItemStruct(..) | ast::ItemEnum(..) => {
1082             let mut visitor = RawPtrDerivingVisitor { cx: cx };
1083             visit::walk_item(&mut visitor, &*item, ());
1084         }
1085         _ => {}
1086     }
1087 }
1088
1089 fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) {
1090     static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [
1091         // FIXME: #14408 whitelist docs since rustdoc looks at them
1092         "doc",
1093
1094         // FIXME: #14406 these are processed in trans, which happens after the
1095         // lint pass
1096         "address_insignificant",
1097         "cold",
1098         "inline",
1099         "link",
1100         "link_name",
1101         "link_section",
1102         "no_builtins",
1103         "no_mangle",
1104         "no_split_stack",
1105         "packed",
1106         "static_assert",
1107         "thread_local",
1108
1109         // not used anywhere (!?) but apparently we want to keep them around
1110         "comment",
1111         "desc",
1112         "license",
1113
1114         // FIXME: #14407 these are only looked at on-demand so we can't
1115         // guarantee they'll have already been checked
1116         "deprecated",
1117         "experimental",
1118         "frozen",
1119         "locked",
1120         "must_use",
1121         "stable",
1122         "unstable",
1123     ];
1124
1125     static CRATE_ATTRS: &'static [&'static str] = &'static [
1126         "crate_type",
1127         "feature",
1128         "no_start",
1129         "no_main",
1130         "no_std",
1131         "crate_id",
1132         "desc",
1133         "comment",
1134         "license",
1135         "copyright",
1136         "no_builtins",
1137     ];
1138
1139     for &name in ATTRIBUTE_WHITELIST.iter() {
1140         if attr.check_name(name) {
1141             break;
1142         }
1143     }
1144
1145     if !attr::is_used(attr) {
1146         cx.span_lint(UnusedAttribute, attr.span, "unused attribute");
1147         if CRATE_ATTRS.contains(&attr.name().get()) {
1148             let msg = match attr.node.style {
1149                 ast::AttrOuter => "crate-level attribute should be an inner \
1150                                   attribute: add an exclamation mark: #![foo]",
1151                 ast::AttrInner => "crate-level attribute should be in the \
1152                                    root module",
1153             };
1154             cx.span_lint(UnusedAttribute, attr.span, msg);
1155         }
1156     }
1157 }
1158
1159 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
1160     let ty = ty::expr_ty(cx.tcx, e);
1161     check_heap_type(cx, e.span, ty);
1162 }
1163
1164 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
1165     match s.node {
1166         ast::StmtSemi(expr, _) => {
1167             match expr.node {
1168                 ast::ExprPath(_) => {
1169                     cx.span_lint(PathStatement,
1170                                  s.span,
1171                                  "path statement with no effect");
1172                 }
1173                 _ => {}
1174             }
1175         }
1176         _ => ()
1177     }
1178 }
1179
1180 fn check_unused_result(cx: &Context, s: &ast::Stmt) {
1181     let expr = match s.node {
1182         ast::StmtSemi(expr, _) => expr,
1183         _ => return
1184     };
1185     let t = ty::expr_ty(cx.tcx, &*expr);
1186     match ty::get(t).sty {
1187         ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
1188         _ => {}
1189     }
1190     match expr.node {
1191         ast::ExprRet(..) => return,
1192         _ => {}
1193     }
1194
1195     let t = ty::expr_ty(cx.tcx, &*expr);
1196     let mut warned = false;
1197     match ty::get(t).sty {
1198         ty::ty_struct(did, _) |
1199         ty::ty_enum(did, _) => {
1200             if ast_util::is_local(did) {
1201                 match cx.tcx.map.get(did.node) {
1202                     ast_map::NodeItem(it) => {
1203                         if attr::contains_name(it.attrs.as_slice(),
1204                                                "must_use") {
1205                             cx.span_lint(UnusedMustUse, s.span,
1206                                          "unused result which must be used");
1207                             warned = true;
1208                         }
1209                     }
1210                     _ => {}
1211                 }
1212             } else {
1213                 csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
1214                     if attr::contains_name(attrs.as_slice(), "must_use") {
1215                         cx.span_lint(UnusedMustUse, s.span,
1216                                      "unused result which must be used");
1217                         warned = true;
1218                     }
1219                 });
1220             }
1221         }
1222         _ => {}
1223     }
1224     if !warned {
1225         cx.span_lint(UnusedResult, s.span, "unused result");
1226     }
1227 }
1228
1229 fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
1230     fn is_camel_case(ident: ast::Ident) -> bool {
1231         let ident = token::get_ident(ident);
1232         assert!(!ident.get().is_empty());
1233         let ident = ident.get().trim_chars('_');
1234
1235         // start with a non-lowercase letter rather than non-uppercase
1236         // ones (some scripts don't have a concept of upper/lowercase)
1237         !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
1238     }
1239
1240     fn to_camel_case(s: &str) -> String {
1241         s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
1242             if i == 0 { c.to_uppercase() }
1243             else { c }
1244         )).collect()
1245     }
1246
1247     fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1248         let s = token::get_ident(ident);
1249
1250         if !is_camel_case(ident) {
1251             cx.span_lint(
1252                 NonCamelCaseTypes, span,
1253                 format!("{} `{}` should have a camel case name such as `{}`",
1254                     sort, s, to_camel_case(s.get())).as_slice());
1255         }
1256     }
1257
1258     match it.node {
1259         ast::ItemTy(..) | ast::ItemStruct(..) => {
1260             check_case(cx, "type", it.ident, it.span)
1261         }
1262         ast::ItemTrait(..) => {
1263             check_case(cx, "trait", it.ident, it.span)
1264         }
1265         ast::ItemEnum(ref enum_definition, _) => {
1266             check_case(cx, "type", it.ident, it.span);
1267             for variant in enum_definition.variants.iter() {
1268                 check_case(cx, "variant", variant.node.name, variant.span);
1269             }
1270         }
1271         _ => ()
1272     }
1273 }
1274
1275 fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1276     fn is_snake_case(ident: ast::Ident) -> bool {
1277         let ident = token::get_ident(ident);
1278         assert!(!ident.get().is_empty());
1279         let ident = ident.get().trim_chars('_');
1280
1281         let mut allow_underscore = true;
1282         ident.chars().all(|c| {
1283             allow_underscore = match c {
1284                 c if c.is_lowercase() || c.is_digit() => true,
1285                 '_' if allow_underscore => false,
1286                 _ => return false,
1287             };
1288             true
1289         })
1290     }
1291
1292     fn to_snake_case(str: &str) -> String {
1293         let mut words = vec![];
1294         for s in str.split('_') {
1295             let mut buf = String::new();
1296             if s.is_empty() { continue; }
1297             for ch in s.chars() {
1298                 if !buf.is_empty() && ch.is_uppercase() {
1299                     words.push(buf);
1300                     buf = String::new();
1301                 }
1302                 buf.push_char(ch.to_lowercase());
1303             }
1304             words.push(buf);
1305         }
1306         words.connect("_")
1307     }
1308
1309     let s = token::get_ident(ident);
1310
1311     if !is_snake_case(ident) {
1312         cx.span_lint(NonSnakeCaseFunctions, span,
1313                     format!("{} `{}` should have a snake case name such as `{}`",
1314                             sort, s, to_snake_case(s.get())).as_slice());
1315     }
1316 }
1317
1318 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
1319     match it.node {
1320         // only check static constants
1321         ast::ItemStatic(_, ast::MutImmutable, _) => {
1322             let s = token::get_ident(it.ident);
1323             // check for lowercase letters rather than non-uppercase
1324             // ones (some scripts don't have a concept of
1325             // upper/lowercase)
1326             if s.get().chars().any(|c| c.is_lowercase()) {
1327                 cx.span_lint(NonUppercaseStatics, it.span,
1328                             format!("static constant `{}` should have an uppercase name \
1329                                 such as `{}`", s.get(),
1330                                 s.get().chars().map(|c| c.to_uppercase())
1331                                     .collect::<String>().as_slice()).as_slice());
1332             }
1333         }
1334         _ => {}
1335     }
1336 }
1337
1338 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
1339     // Lint for constants that look like binding identifiers (#7526)
1340     match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
1341         (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => {
1342             // last identifier alone is right choice for this lint.
1343             let ident = path.segments.last().unwrap().identifier;
1344             let s = token::get_ident(ident);
1345             if s.get().chars().any(|c| c.is_lowercase()) {
1346                 cx.span_lint(NonUppercasePatternStatics, path.span,
1347                             format!("static constant in pattern `{}` should have an uppercase \
1348                                 name such as `{}`", s.get(),
1349                                 s.get().chars().map(|c| c.to_uppercase())
1350                                     .collect::<String>().as_slice()).as_slice());
1351             }
1352         }
1353         _ => {}
1354     }
1355 }
1356
1357 fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) {
1358     match &p.node {
1359         &ast::PatIdent(_, ref path, _) => {
1360             match cx.tcx.def_map.borrow().find(&p.id) {
1361                 Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) |
1362                         Some(&def::DefArg(_, _)) => {
1363                     // last identifier alone is right choice for this lint.
1364                     let ident = path.segments.last().unwrap().identifier;
1365                     let s = token::get_ident(ident);
1366                     if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
1367                         cx.span_lint(
1368                             UppercaseVariables,
1369                             path.span,
1370                             "variable names should start with a lowercase character");
1371                     }
1372                 }
1373                 _ => {}
1374             }
1375         }
1376         _ => {}
1377     }
1378 }
1379
1380 fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) {
1381     for sf in s.fields.iter() {
1382         match sf.node {
1383             ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
1384                 let s = token::get_ident(ident);
1385                 if s.get().char_at(0).is_uppercase() {
1386                     cx.span_lint(
1387                         UppercaseVariables,
1388                         sf.span,
1389                         "structure field names should start with a lowercase character");
1390                 }
1391             }
1392             _ => {}
1393         }
1394     }
1395 }
1396
1397 fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
1398     match value.node {
1399         ast::ExprParen(_) => {
1400             cx.span_lint(UnnecessaryParens, value.span,
1401                          format!("unnecessary parentheses around {}",
1402                                  msg).as_slice())
1403         }
1404         _ => {}
1405     }
1406 }
1407
1408 fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
1409     let (value, msg) = match e.node {
1410         ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
1411         ast::ExprWhile(cond, _) => (cond, "`while` condition"),
1412         ast::ExprMatch(head, _) => (head, "`match` head expression"),
1413         ast::ExprRet(Some(value)) => (value, "`return` value"),
1414         ast::ExprAssign(_, value) => (value, "assigned value"),
1415         ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
1416         _ => return
1417     };
1418     check_unnecessary_parens_core(cx, &*value, msg);
1419 }
1420
1421 fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
1422     let (value, msg) = match s.node {
1423         ast::StmtDecl(decl, _) => match decl.node {
1424             ast::DeclLocal(local) => match local.init {
1425                 Some(value) => (value, "assigned value"),
1426                 None => return
1427             },
1428             _ => return
1429         },
1430         _ => return
1431     };
1432     check_unnecessary_parens_core(cx, &*value, msg);
1433 }
1434
1435 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
1436     match e.node {
1437         // Don't warn about generated blocks, that'll just pollute the output.
1438         ast::ExprBlock(ref blk) => {
1439             if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1440                 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1441                 cx.span_lint(UnusedUnsafe, blk.span,
1442                              "unnecessary `unsafe` block");
1443             }
1444         }
1445         _ => ()
1446     }
1447 }
1448
1449 fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
1450     match e.node {
1451         // Don't warn about generated blocks, that'll just pollute the output.
1452         ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
1453             cx.span_lint(UnsafeBlock, blk.span, "usage of an `unsafe` block");
1454         }
1455         _ => ()
1456     }
1457 }
1458
1459 fn check_unused_mut_pat(cx: &Context, pats: &[Gc<ast::Pat>]) {
1460     // collect all mutable pattern and group their NodeIDs by their Identifier to
1461     // avoid false warnings in match arms with multiple patterns
1462     let mut mutables = HashMap::new();
1463     for &p in pats.iter() {
1464         pat_util::pat_bindings(&cx.tcx.def_map, &*p, |mode, id, _, path| {
1465             match mode {
1466                 ast::BindByValue(ast::MutMutable) => {
1467                     if path.segments.len() != 1 {
1468                         cx.tcx.sess.span_bug(p.span,
1469                                              "mutable binding that doesn't consist \
1470                                               of exactly one segment");
1471                     }
1472                     let ident = path.segments.get(0).identifier;
1473                     if !token::get_ident(ident).get().starts_with("_") {
1474                         mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| {
1475                             old.push(id);
1476                         });
1477                     }
1478                 }
1479                 _ => {
1480                 }
1481             }
1482         });
1483     }
1484
1485     let used_mutables = cx.tcx.used_mut_nodes.borrow();
1486     for (_, v) in mutables.iter() {
1487         if !v.iter().any(|e| used_mutables.contains(e)) {
1488             cx.span_lint(UnusedMut, cx.tcx.map.span(*v.get(0)),
1489                          "variable does not need to be mutable");
1490         }
1491     }
1492 }
1493
1494 enum Allocation {
1495     VectorAllocation,
1496     BoxAllocation
1497 }
1498
1499 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1500     // Warn if string and vector literals with sigils, or boxing expressions,
1501     // are immediately borrowed.
1502     let allocation = match e.node {
1503         ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
1504             match e2.node {
1505                 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
1506                     VectorAllocation
1507                 }
1508                 ast::ExprVec(..) => VectorAllocation,
1509                 _ => return
1510             }
1511         }
1512         ast::ExprUnary(ast::UnUniq, _) |
1513         ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
1514
1515         _ => return
1516     };
1517
1518     let report = |msg| {
1519         cx.span_lint(UnnecessaryAllocation, e.span, msg);
1520     };
1521
1522     match cx.tcx.adjustments.borrow().find(&e.id) {
1523         Some(adjustment) => {
1524             match *adjustment {
1525                 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
1526                     match (allocation, autoref) {
1527                         (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
1528                             report("unnecessary allocation, the sigil can be \
1529                                     removed");
1530                         }
1531                         (BoxAllocation,
1532                          Some(ty::AutoPtr(_, ast::MutImmutable))) => {
1533                             report("unnecessary allocation, use & instead");
1534                         }
1535                         (BoxAllocation,
1536                          Some(ty::AutoPtr(_, ast::MutMutable))) => {
1537                             report("unnecessary allocation, use &mut \
1538                                     instead");
1539                         }
1540                         _ => ()
1541                     }
1542                 }
1543                 _ => {}
1544             }
1545         }
1546
1547         _ => ()
1548     }
1549 }
1550
1551 fn check_missing_doc_attrs(cx: &Context,
1552                            id: Option<ast::NodeId>,
1553                            attrs: &[ast::Attribute],
1554                            sp: Span,
1555                            desc: &'static str) {
1556     // If we're building a test harness, then warning about
1557     // documentation is probably not really relevant right now.
1558     if cx.tcx.sess.opts.test { return }
1559
1560     // `#[doc(hidden)]` disables missing_doc check.
1561     if cx.is_doc_hidden { return }
1562
1563     // Only check publicly-visible items, using the result from the privacy pass. It's an option so
1564     // the crate root can also use this function (it doesn't have a NodeId).
1565     match id {
1566         Some(ref id) if !cx.exported_items.contains(id) => return,
1567         _ => ()
1568     }
1569
1570     let has_doc = attrs.iter().any(|a| {
1571         match a.node.value.node {
1572             ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
1573             _ => false
1574         }
1575     });
1576     if !has_doc {
1577         cx.span_lint(MissingDoc,
1578                      sp,
1579                      format!("missing documentation for {}",
1580                              desc).as_slice());
1581     }
1582 }
1583
1584 fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
1585     let desc = match it.node {
1586         ast::ItemFn(..) => "a function",
1587         ast::ItemMod(..) => "a module",
1588         ast::ItemEnum(..) => "an enum",
1589         ast::ItemStruct(..) => "a struct",
1590         ast::ItemTrait(..) => "a trait",
1591         _ => return
1592     };
1593     check_missing_doc_attrs(cx,
1594                             Some(it.id),
1595                             it.attrs.as_slice(),
1596                             it.span,
1597                             desc);
1598 }
1599
1600 #[deriving(PartialEq)]
1601 enum MethodContext {
1602     TraitDefaultImpl,
1603     TraitImpl,
1604     PlainImpl
1605 }
1606
1607 fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
1608     // If the method is an impl for a trait, don't doc.
1609     if method_context(cx, m) == TraitImpl { return; }
1610
1611     // Otherwise, doc according to privacy. This will also check
1612     // doc for default methods defined on traits.
1613     check_missing_doc_attrs(cx,
1614                             Some(m.id),
1615                             m.attrs.as_slice(),
1616                             m.span,
1617                             "a method");
1618 }
1619
1620 fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
1621     let did = ast::DefId {
1622         krate: ast::LOCAL_CRATE,
1623         node: m.id
1624     };
1625
1626     match cx.tcx.methods.borrow().find_copy(&did) {
1627         None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
1628         Some(md) => {
1629             match md.container {
1630                 ty::TraitContainer(..) => TraitDefaultImpl,
1631                 ty::ImplContainer(cid) => {
1632                     match ty::impl_trait_ref(cx.tcx, cid) {
1633                         Some(..) => TraitImpl,
1634                         None => PlainImpl
1635                     }
1636                 }
1637             }
1638         }
1639     }
1640 }
1641
1642 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
1643     check_missing_doc_attrs(cx,
1644                             Some(tm.id),
1645                             tm.attrs.as_slice(),
1646                             tm.span,
1647                             "a type method");
1648 }
1649
1650 fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
1651     match sf.node.kind {
1652         ast::NamedField(_, vis) if vis == ast::Public =>
1653             check_missing_doc_attrs(cx,
1654                                     Some(cx.cur_struct_def_id),
1655                                     sf.node.attrs.as_slice(),
1656                                     sf.span,
1657                                     "a struct field"),
1658         _ => {}
1659     }
1660 }
1661
1662 fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
1663     check_missing_doc_attrs(cx,
1664                             Some(v.node.id),
1665                             v.node.attrs.as_slice(),
1666                             v.span,
1667                             "a variant");
1668 }
1669
1670 /// Checks for use of items with #[deprecated], #[experimental] and
1671 /// #[unstable] (or none of them) attributes.
1672 fn check_stability(cx: &Context, e: &ast::Expr) {
1673     let id = match e.node {
1674         ast::ExprPath(..) | ast::ExprStruct(..) => {
1675             match cx.tcx.def_map.borrow().find(&e.id) {
1676                 Some(&def) => def.def_id(),
1677                 None => return
1678             }
1679         }
1680         ast::ExprMethodCall(..) => {
1681             let method_call = typeck::MethodCall::expr(e.id);
1682             match cx.tcx.method_map.borrow().find(&method_call) {
1683                 Some(method) => {
1684                     match method.origin {
1685                         typeck::MethodStatic(def_id) => {
1686                             // If this implements a trait method, get def_id
1687                             // of the method inside trait definition.
1688                             // Otherwise, use the current def_id (which refers
1689                             // to the method inside impl).
1690                             ty::trait_method_of_method(
1691                                 cx.tcx, def_id).unwrap_or(def_id)
1692                         }
1693                         typeck::MethodParam(typeck::MethodParam {
1694                             trait_id: trait_id,
1695                             method_num: index,
1696                             ..
1697                         })
1698                         | typeck::MethodObject(typeck::MethodObject {
1699                             trait_id: trait_id,
1700                             method_num: index,
1701                             ..
1702                         }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1703                     }
1704                 }
1705                 None => return
1706             }
1707         }
1708         _ => return
1709     };
1710
1711     let stability = if ast_util::is_local(id) {
1712         // this crate
1713         let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1714             attrs.map(|a| attr::find_stability(a.as_slice()))
1715         });
1716         match s {
1717             Some(s) => s,
1718
1719             // no possibility of having attributes
1720             // (e.g. it's a local variable), so just
1721             // ignore it.
1722             None => return
1723         }
1724     } else {
1725         // cross-crate
1726
1727         let mut s = None;
1728         // run through all the attributes and take the first
1729         // stability one.
1730         csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| {
1731             if s.is_none() {
1732                 s = attr::find_stability(attrs.as_slice())
1733             }
1734         });
1735         s
1736     };
1737
1738     let (lint, label) = match stability {
1739         // no stability attributes == Unstable
1740         None => (Unstable, "unmarked"),
1741         Some(attr::Stability { level: attr::Unstable, .. }) =>
1742                 (Unstable, "unstable"),
1743         Some(attr::Stability { level: attr::Experimental, .. }) =>
1744                 (Experimental, "experimental"),
1745         Some(attr::Stability { level: attr::Deprecated, .. }) =>
1746                 (Deprecated, "deprecated"),
1747         _ => return
1748     };
1749
1750     let msg = match stability {
1751         Some(attr::Stability { text: Some(ref s), .. }) => {
1752             format!("use of {} item: {}", label, *s)
1753         }
1754         _ => format!("use of {} item", label)
1755     };
1756
1757     cx.span_lint(lint, e.span, msg.as_slice());
1758 }
1759
1760 fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) {
1761     match it.node {
1762         ast::ItemEnum(..) => {
1763             match cx.cur.find(&(VariantSizeDifference as uint)) {
1764                 Some(&(lvl, src)) if lvl != Allow => {
1765                     cx.node_levels.insert((it.id, VariantSizeDifference), (lvl, src));
1766                 },
1767                 _ => { }
1768             }
1769         },
1770         _ => { }
1771     }
1772 }
1773
1774 impl<'a> Visitor<()> for Context<'a> {
1775     fn visit_item(&mut self, it: &ast::Item, _: ()) {
1776         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1777             check_enum_variant_sizes(cx, it);
1778             check_item_ctypes(cx, it);
1779             check_item_non_camel_case_types(cx, it);
1780             check_item_non_uppercase_statics(cx, it);
1781             check_heap_item(cx, it);
1782             check_missing_doc_item(cx, it);
1783             check_raw_ptr_deriving(cx, it);
1784
1785             cx.visit_ids(|v| v.visit_item(it, ()));
1786
1787             visit::walk_item(cx, it, ());
1788         })
1789     }
1790
1791     fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
1792         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1793             visit::walk_foreign_item(cx, it, ());
1794         })
1795     }
1796
1797     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1798         self.with_lint_attrs(i.attrs.as_slice(), |cx| {
1799             cx.visit_ids(|v| v.visit_view_item(i, ()));
1800
1801             visit::walk_view_item(cx, i, ());
1802         })
1803     }
1804
1805     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1806         check_pat_non_uppercase_statics(self, p);
1807         check_pat_uppercase_variable(self, p);
1808
1809         visit::walk_pat(self, p, ());
1810     }
1811
1812     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
1813         match e.node {
1814             ast::ExprUnary(ast::UnNeg, expr) => {
1815                 // propagate negation, if the negation itself isn't negated
1816                 if self.negated_expr_id != e.id {
1817                     self.negated_expr_id = expr.id;
1818                 }
1819             },
1820             ast::ExprParen(expr) => if self.negated_expr_id == e.id {
1821                 self.negated_expr_id = expr.id
1822             },
1823             ast::ExprMatch(_, ref arms) => {
1824                 for a in arms.iter() {
1825                     check_unused_mut_pat(self, a.pats.as_slice());
1826                 }
1827             },
1828             _ => ()
1829         };
1830
1831         check_while_true_expr(self, e);
1832         check_stability(self, e);
1833         check_unnecessary_parens_expr(self, e);
1834         check_unused_unsafe(self, e);
1835         check_unsafe_block(self, e);
1836         check_unnecessary_allocation(self, e);
1837         check_heap_expr(self, e);
1838
1839         check_type_limits(self, e);
1840         check_unused_casts(self, e);
1841
1842         visit::walk_expr(self, e, ());
1843     }
1844
1845     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
1846         check_path_statement(self, s);
1847         check_unused_result(self, s);
1848         check_unnecessary_parens_stmt(self, s);
1849
1850         match s.node {
1851             ast::StmtDecl(d, _) => {
1852                 match d.node {
1853                     ast::DeclLocal(l) => {
1854                         check_unused_mut_pat(self, &[l.pat]);
1855                     },
1856                     _ => {}
1857                 }
1858             },
1859             _ => {}
1860         }
1861
1862         visit::walk_stmt(self, s, ());
1863     }
1864
1865     fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
1866                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1867         let recurse = |this: &mut Context| {
1868             visit::walk_fn(this, fk, decl, body, span, ());
1869         };
1870
1871         for a in decl.inputs.iter(){
1872             check_unused_mut_pat(self, &[a.pat]);
1873         }
1874
1875         match *fk {
1876             visit::FkMethod(ident, _, m) => {
1877                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
1878                     check_missing_doc_method(cx, m);
1879
1880                     match method_context(cx, m) {
1881                         PlainImpl => check_snake_case(cx, "method", ident, span),
1882                         TraitDefaultImpl => check_snake_case(cx, "trait method", ident, span),
1883                         _ => (),
1884                     }
1885
1886                     cx.visit_ids(|v| {
1887                         v.visit_fn(fk, decl, body, span, id, ());
1888                     });
1889                     recurse(cx);
1890                 })
1891             },
1892             visit::FkItemFn(ident, _, _, _) => {
1893                 check_snake_case(self, "function", ident, span);
1894                 recurse(self);
1895             }
1896             _ => recurse(self),
1897         }
1898     }
1899
1900     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1901         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
1902             check_missing_doc_ty_method(cx, t);
1903             check_snake_case(cx, "trait method", t.ident, t.span);
1904
1905             visit::walk_ty_method(cx, t, ());
1906         })
1907     }
1908
1909     fn visit_struct_def(&mut self,
1910                         s: &ast::StructDef,
1911                         _: ast::Ident,
1912                         _: &ast::Generics,
1913                         id: ast::NodeId,
1914                         _: ()) {
1915         check_struct_uppercase_variable(self, s);
1916
1917         let old_id = self.cur_struct_def_id;
1918         self.cur_struct_def_id = id;
1919         visit::walk_struct_def(self, s, ());
1920         self.cur_struct_def_id = old_id;
1921     }
1922
1923     fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1924         self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
1925             check_missing_doc_struct_field(cx, s);
1926
1927             visit::walk_struct_field(cx, s, ());
1928         })
1929     }
1930
1931     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1932         self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
1933             check_missing_doc_variant(cx, v);
1934
1935             visit::walk_variant(cx, v, g, ());
1936         })
1937     }
1938
1939     // FIXME(#10894) should continue recursing
1940     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
1941
1942     fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
1943         check_unused_attribute(self, attr);
1944     }
1945 }
1946
1947 impl<'a> IdVisitingOperation for Context<'a> {
1948     fn visit_id(&self, id: ast::NodeId) {
1949         match self.tcx.sess.lints.borrow_mut().pop(&id) {
1950             None => {}
1951             Some(l) => {
1952                 for (lint, span, msg) in l.move_iter() {
1953                     self.span_lint(lint, span, msg.as_slice())
1954                 }
1955             }
1956         }
1957     }
1958 }
1959
1960 pub fn check_crate(tcx: &ty::ctxt,
1961                    exported_items: &privacy::ExportedItems,
1962                    krate: &ast::Crate) {
1963     let mut cx = Context {
1964         dict: get_lint_dict(),
1965         cur: SmallIntMap::new(),
1966         tcx: tcx,
1967         exported_items: exported_items,
1968         cur_struct_def_id: -1,
1969         is_doc_hidden: false,
1970         lint_stack: Vec::new(),
1971         negated_expr_id: -1,
1972         checked_raw_pointers: NodeSet::new(),
1973         node_levels: HashMap::new(),
1974     };
1975
1976     // Install default lint levels, followed by the command line levels, and
1977     // then actually visit the whole crate.
1978     for (_, spec) in cx.dict.iter() {
1979         if spec.default != Allow {
1980             cx.cur.insert(spec.lint as uint, (spec.default, Default));
1981         }
1982     }
1983     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1984         cx.set_level(lint, level, CommandLine);
1985     }
1986     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
1987         cx.visit_id(ast::CRATE_NODE_ID);
1988         cx.visit_ids(|v| {
1989             v.visited_outermost = true;
1990             visit::walk_crate(v, krate, ());
1991         });
1992
1993         // since the root module isn't visited as an item (because it isn't an item), warn for it
1994         // here.
1995         check_missing_doc_attrs(cx,
1996                                 None,
1997                                 krate.attrs.as_slice(),
1998                                 krate.span,
1999                                 "crate");
2000
2001         visit::walk_crate(cx, krate, ());
2002     });
2003
2004     // If we missed any lints added to the session, then there's a bug somewhere
2005     // in the iteration code.
2006     for (id, v) in tcx.sess.lints.borrow().iter() {
2007         for &(lint, span, ref msg) in v.iter() {
2008             tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}",
2009                                             lint, tcx.map.node_to_str(*id), *msg).as_slice())
2010         }
2011     }
2012
2013     tcx.sess.abort_if_errors();
2014     *tcx.node_lint_levels.borrow_mut() = cx.node_levels;
2015 }