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