]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lint.rs
693f6fb35f4fce56a570cf7459bb8c10ac209bfe
[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::pat_util;
42 use middle::privacy;
43 use middle::trans::adt; // for `adt::is_ffi_safe`
44 use middle::ty;
45 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
46 use middle::typeck::infer;
47 use middle::typeck;
48 use std::to_str::ToStr;
49 use util::ppaux::{ty_to_str};
50
51 use std::cmp;
52 use collections::HashMap;
53 use std::i16;
54 use std::i32;
55 use std::i64;
56 use std::i8;
57 use std::u16;
58 use std::u32;
59 use std::u64;
60 use std::u8;
61 use collections::SmallIntMap;
62 use syntax::ast_map;
63 use syntax::ast_util::IdVisitingOperation;
64 use syntax::attr::{AttrMetaMethods, AttributeMethods};
65 use syntax::attr;
66 use syntax::codemap::Span;
67 use syntax::parse::token::InternedString;
68 use syntax::parse::token;
69 use syntax::visit::Visitor;
70 use syntax::{ast, ast_util, visit};
71
72 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
73 pub enum Lint {
74     CTypes,
75     UnusedImports,
76     UnnecessaryQualification,
77     WhileTrue,
78     PathStatement,
79     UnrecognizedLint,
80     NonCamelCaseTypes,
81     NonUppercaseStatics,
82     NonUppercasePatternStatics,
83     UnnecessaryParens,
84     TypeLimits,
85     TypeOverflow,
86     UnusedUnsafe,
87     UnsafeBlock,
88     AttributeUsage,
89     UnknownFeatures,
90     UnknownCrateType,
91
92     ManagedHeapMemory,
93     OwnedHeapMemory,
94     HeapMemory,
95
96     UnusedVariable,
97     DeadAssignment,
98     UnusedMut,
99     UnnecessaryAllocation,
100     DeadCode,
101     VisiblePrivateTypes,
102     UnnecessaryTypecast,
103
104     MissingDoc,
105     UnreachableCode,
106
107     Deprecated,
108     Experimental,
109     Unstable,
110
111     UnusedMustUse,
112     UnusedResult,
113
114     Warnings,
115 }
116
117 pub fn level_to_str(lv: level) -> &'static str {
118     match lv {
119       allow => "allow",
120       warn => "warn",
121       deny => "deny",
122       forbid => "forbid"
123     }
124 }
125
126 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
127 pub enum level {
128     allow, warn, deny, forbid
129 }
130
131 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
132 pub struct LintSpec {
133     default: level,
134     lint: Lint,
135     desc: &'static str,
136 }
137
138 pub type LintDict = HashMap<&'static str, LintSpec>;
139
140 #[deriving(Eq)]
141 enum LintSource {
142     Node(Span),
143     Default,
144     CommandLine
145 }
146
147 static lint_table: &'static [(&'static str, LintSpec)] = &[
148     ("ctypes",
149      LintSpec {
150         lint: CTypes,
151         desc: "proper use of std::libc types in foreign modules",
152         default: warn
153      }),
154
155     ("unused_imports",
156      LintSpec {
157         lint: UnusedImports,
158         desc: "imports that are never used",
159         default: warn
160      }),
161
162     ("unnecessary_qualification",
163      LintSpec {
164         lint: UnnecessaryQualification,
165         desc: "detects unnecessarily qualified names",
166         default: allow
167      }),
168
169     ("while_true",
170      LintSpec {
171         lint: WhileTrue,
172         desc: "suggest using `loop { }` instead of `while true { }`",
173         default: warn
174      }),
175
176     ("path_statement",
177      LintSpec {
178         lint: PathStatement,
179         desc: "path statements with no effect",
180         default: warn
181      }),
182
183     ("unrecognized_lint",
184      LintSpec {
185         lint: UnrecognizedLint,
186         desc: "unrecognized lint attribute",
187         default: warn
188      }),
189
190     ("non_camel_case_types",
191      LintSpec {
192         lint: NonCamelCaseTypes,
193         desc: "types, variants and traits should have camel case names",
194         default: warn
195      }),
196
197     ("non_uppercase_statics",
198      LintSpec {
199          lint: NonUppercaseStatics,
200          desc: "static constants should have uppercase identifiers",
201          default: allow
202      }),
203
204     ("non_uppercase_pattern_statics",
205      LintSpec {
206          lint: NonUppercasePatternStatics,
207          desc: "static constants in match patterns should be all caps",
208          default: warn
209      }),
210
211     ("unnecessary_parens",
212      LintSpec {
213         lint: UnnecessaryParens,
214         desc: "`if`, `match`, `while` and `return` do not need parentheses",
215         default: warn
216      }),
217
218     ("managed_heap_memory",
219      LintSpec {
220         lint: ManagedHeapMemory,
221         desc: "use of managed (@ type) heap memory",
222         default: allow
223      }),
224
225     ("owned_heap_memory",
226      LintSpec {
227         lint: OwnedHeapMemory,
228         desc: "use of owned (~ type) heap memory",
229         default: allow
230      }),
231
232     ("heap_memory",
233      LintSpec {
234         lint: HeapMemory,
235         desc: "use of any (~ type or @ type) heap memory",
236         default: allow
237      }),
238
239     ("type_limits",
240      LintSpec {
241         lint: TypeLimits,
242         desc: "comparisons made useless by limits of the types involved",
243         default: warn
244      }),
245
246     ("type_overflow",
247      LintSpec {
248         lint: TypeOverflow,
249         desc: "literal out of range for its type",
250         default: warn
251      }),
252
253
254     ("unused_unsafe",
255      LintSpec {
256         lint: UnusedUnsafe,
257         desc: "unnecessary use of an `unsafe` block",
258         default: warn
259     }),
260
261     ("unsafe_block",
262      LintSpec {
263         lint: UnsafeBlock,
264         desc: "usage of an `unsafe` block",
265         default: allow
266     }),
267
268     ("attribute_usage",
269      LintSpec {
270         lint: AttributeUsage,
271         desc: "detects bad use of attributes",
272         default: warn
273     }),
274
275     ("unused_variable",
276      LintSpec {
277         lint: UnusedVariable,
278         desc: "detect variables which are not used in any way",
279         default: warn
280     }),
281
282     ("dead_assignment",
283      LintSpec {
284         lint: DeadAssignment,
285         desc: "detect assignments that will never be read",
286         default: warn
287     }),
288
289     ("unnecessary_typecast",
290      LintSpec {
291         lint: UnnecessaryTypecast,
292         desc: "detects unnecessary type casts, that can be removed",
293         default: allow,
294     }),
295
296     ("unused_mut",
297      LintSpec {
298         lint: UnusedMut,
299         desc: "detect mut variables which don't need to be mutable",
300         default: warn
301     }),
302
303     ("unnecessary_allocation",
304      LintSpec {
305         lint: UnnecessaryAllocation,
306         desc: "detects unnecessary allocations that can be eliminated",
307         default: warn
308     }),
309
310     (DEAD_CODE_LINT_STR,
311      LintSpec {
312         lint: DeadCode,
313         desc: "detect piece of code that will never be used",
314         default: warn
315     }),
316     ("visible_private_types",
317      LintSpec {
318         lint: VisiblePrivateTypes,
319         desc: "detect use of private types in exported type signatures",
320         default: warn
321     }),
322
323     ("missing_doc",
324      LintSpec {
325         lint: MissingDoc,
326         desc: "detects missing documentation for public members",
327         default: allow
328     }),
329
330     ("unreachable_code",
331      LintSpec {
332         lint: UnreachableCode,
333         desc: "detects unreachable code",
334         default: warn
335     }),
336
337     ("deprecated",
338      LintSpec {
339         lint: Deprecated,
340         desc: "detects use of #[deprecated] items",
341         default: warn
342     }),
343
344     ("experimental",
345      LintSpec {
346         lint: Experimental,
347         desc: "detects use of #[experimental] items",
348         default: warn
349     }),
350
351     ("unstable",
352      LintSpec {
353         lint: Unstable,
354         desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
355         default: allow
356     }),
357
358     ("warnings",
359      LintSpec {
360         lint: Warnings,
361         desc: "mass-change the level for lints which produce warnings",
362         default: warn
363     }),
364
365     ("unknown_features",
366      LintSpec {
367         lint: UnknownFeatures,
368         desc: "unknown features found in crate-level #[feature] directives",
369         default: deny,
370     }),
371
372     ("unknown_crate_type",
373     LintSpec {
374         lint: UnknownCrateType,
375         desc: "unknown crate type found in #[crate_type] directive",
376         default: deny,
377     }),
378
379     ("unused_must_use",
380     LintSpec {
381         lint: UnusedMustUse,
382         desc: "unused result of a type flagged as #[must_use]",
383         default: warn,
384     }),
385
386     ("unused_result",
387     LintSpec {
388         lint: UnusedResult,
389         desc: "unused result of an expression in a statement",
390         default: allow,
391     })
392 ];
393
394 /*
395   Pass names should not contain a '-', as the compiler normalizes
396   '-' to '_' in command-line flags
397  */
398 pub fn get_lint_dict() -> LintDict {
399     let mut map = HashMap::new();
400     for &(k, v) in lint_table.iter() {
401         map.insert(k, v);
402     }
403     return map;
404 }
405
406 struct Context<'a> {
407     // All known lint modes (string versions)
408     dict: @LintDict,
409     // Current levels of each lint warning
410     cur: SmallIntMap<(level, LintSource)>,
411     // context we're checking in (used to access fields like sess)
412     tcx: ty::ctxt,
413     // maps from an expression id that corresponds to a method call to the
414     // details of the method to be invoked
415     method_map: typeck::MethodMap,
416     // Items exported by the crate; used by the missing_doc lint.
417     exported_items: &'a privacy::ExportedItems,
418     // The id of the current `ast::StructDef` being walked.
419     cur_struct_def_id: ast::NodeId,
420     // Whether some ancestor of the current node was marked
421     // #[doc(hidden)].
422     is_doc_hidden: bool,
423
424     // When recursing into an attributed node of the ast which modifies lint
425     // levels, this stack keeps track of the previous lint levels of whatever
426     // was modified.
427     lint_stack: ~[(Lint, level, LintSource)],
428
429     // id of the last visited negated expression
430     negated_expr_id: ast::NodeId
431 }
432
433 impl<'a> Context<'a> {
434     fn get_level(&self, lint: Lint) -> level {
435         match self.cur.find(&(lint as uint)) {
436           Some(&(lvl, _)) => lvl,
437           None => allow
438         }
439     }
440
441     fn get_source(&self, lint: Lint) -> LintSource {
442         match self.cur.find(&(lint as uint)) {
443           Some(&(_, src)) => src,
444           None => Default
445         }
446     }
447
448     fn set_level(&mut self, lint: Lint, level: level, src: LintSource) {
449         if level == allow {
450             self.cur.remove(&(lint as uint));
451         } else {
452             self.cur.insert(lint as uint, (level, src));
453         }
454     }
455
456     fn lint_to_str(&self, lint: Lint) -> &'static str {
457         for (k, v) in self.dict.iter() {
458             if v.lint == lint {
459                 return *k;
460             }
461         }
462         fail!("unregistered lint {:?}", lint);
463     }
464
465     fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
466         let (level, src) = match self.cur.find(&(lint as uint)) {
467             None => { return }
468             Some(&(warn, src)) => (self.get_level(Warnings), src),
469             Some(&pair) => pair,
470         };
471         if level == allow { return }
472
473         let mut note = None;
474         let msg = match src {
475             Default => {
476                 format!("{}, \\#[{}({})] on by default", msg,
477                     level_to_str(level), self.lint_to_str(lint))
478             },
479             CommandLine => {
480                 format!("{} [-{} {}]", msg,
481                     match level {
482                         warn => 'W', deny => 'D', forbid => 'F',
483                         allow => fail!()
484                     }, self.lint_to_str(lint).replace("_", "-"))
485             },
486             Node(src) => {
487                 note = Some(src);
488                 msg.to_str()
489             }
490         };
491         match level {
492             warn =>          { self.tcx.sess.span_warn(span, msg); }
493             deny | forbid => { self.tcx.sess.span_err(span, msg);  }
494             allow => fail!(),
495         }
496
497         for &span in note.iter() {
498             self.tcx.sess.span_note(span, "lint level defined here");
499         }
500     }
501
502     /**
503      * Merge the lints specified by any lint attributes into the
504      * current lint context, call the provided function, then reset the
505      * lints in effect to their previous state.
506      */
507     fn with_lint_attrs(&mut self,
508                        attrs: &[ast::Attribute],
509                        f: |&mut Context|) {
510         // Parse all of the lint attributes, and then add them all to the
511         // current dictionary of lint information. Along the way, keep a history
512         // of what we changed so we can roll everything back after invoking the
513         // specified closure
514         let mut pushed = 0u;
515         each_lint(self.tcx.sess, attrs, |meta, level, lintname| {
516             match self.dict.find_equiv(&lintname) {
517                 None => {
518                     self.span_lint(
519                         UnrecognizedLint,
520                         meta.span,
521                         format!("unknown `{}` attribute: `{}`",
522                         level_to_str(level), lintname));
523                 }
524                 Some(lint) => {
525                     let lint = lint.lint;
526                     let now = self.get_level(lint);
527                     if now == forbid && level != forbid {
528                         self.tcx.sess.span_err(meta.span,
529                         format!("{}({}) overruled by outer forbid({})",
530                         level_to_str(level),
531                         lintname, lintname));
532                     } else if now != level {
533                         let src = self.get_source(lint);
534                         self.lint_stack.push((lint, now, src));
535                         pushed += 1;
536                         self.set_level(lint, level, Node(meta.span));
537                     }
538                 }
539             }
540             true
541         });
542
543         let old_is_doc_hidden = self.is_doc_hidden;
544         self.is_doc_hidden =
545             self.is_doc_hidden ||
546             attrs.iter()
547                  .any(|attr| {
548                      attr.name().equiv(&("doc")) &&
549                      match attr.meta_item_list() {
550                          None => false,
551                          Some(l) => attr::contains_name(l, "hidden")
552                      }
553                  });
554
555         f(self);
556
557         // rollback
558         self.is_doc_hidden = old_is_doc_hidden;
559         for _ in range(0, pushed) {
560             let (lint, lvl, src) = self.lint_stack.pop().unwrap();
561             self.set_level(lint, lvl, src);
562         }
563     }
564
565     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
566         let mut v = ast_util::IdVisitor {
567             operation: self,
568             pass_through_items: false,
569             visited_outermost: false,
570         };
571         f(&mut v);
572     }
573 }
574
575 // Check that every lint from the list of attributes satisfies `f`.
576 // Return true if that's the case. Otherwise return false.
577 pub fn each_lint(sess: session::Session,
578                  attrs: &[ast::Attribute],
579                  f: |@ast::MetaItem, level, InternedString| -> bool)
580                  -> bool {
581     let xs = [allow, warn, deny, forbid];
582     for &level in xs.iter() {
583         let level_name = level_to_str(level);
584         for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
585             let meta = attr.node.value;
586             let metas = match meta.node {
587                 ast::MetaList(_, ref metas) => metas,
588                 _ => {
589                     sess.span_err(meta.span, "malformed lint attribute");
590                     continue;
591                 }
592             };
593             for meta in metas.iter() {
594                 match meta.node {
595                     ast::MetaWord(ref lintname) => {
596                         if !f(*meta, level, (*lintname).clone()) {
597                             return false;
598                         }
599                     }
600                     _ => {
601                         sess.span_err(meta.span, "malformed lint attribute");
602                     }
603                 }
604             }
605         }
606     }
607     true
608 }
609
610 // Check from a list of attributes if it contains the appropriate
611 // `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
612 pub fn contains_lint(attrs: &[ast::Attribute],
613                      level: level,
614                      lintname: &'static str)
615                      -> bool {
616     let level_name = level_to_str(level);
617     for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
618         if attr.meta_item_list().is_none() {
619             continue
620         }
621         let list = attr.meta_item_list().unwrap();
622         for meta_item in list.iter() {
623             if meta_item.name().equiv(&lintname) {
624                 return true;
625             }
626         }
627     }
628     false
629 }
630
631 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
632     match e.node {
633         ast::ExprWhile(cond, _) => {
634             match cond.node {
635                 ast::ExprLit(lit) => {
636                     match lit.node {
637                         ast::LitBool(true) => {
638                             cx.span_lint(WhileTrue,
639                                          e.span,
640                                          "denote infinite loops with loop \
641                                           { ... }");
642                         }
643                         _ => {}
644                     }
645                 }
646                 _ => ()
647             }
648         }
649         _ => ()
650     }
651 }
652 impl<'a> AstConv for Context<'a>{
653     fn tcx(&self) -> ty::ctxt { self.tcx }
654
655     fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
656         ty::lookup_item_type(self.tcx, id)
657     }
658
659     fn get_trait_def(&self, id: ast::DefId) -> @ty::TraitDef {
660         ty::lookup_trait_def(self.tcx, id)
661     }
662
663     fn ty_infer(&self, _span: Span) -> ty::t {
664         infer::new_infer_ctxt(self.tcx).next_ty_var()
665     }
666 }
667
668
669 fn check_unused_casts(cx: &Context, e: &ast::Expr) {
670     return match e.node {
671         ast::ExprCast(expr, ty) => {
672             let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
673             if  ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
674                 cx.span_lint(UnnecessaryTypecast, ty.span,
675                              "unnecessary type cast");
676             }
677         }
678         _ => ()
679     };
680 }
681
682 fn check_type_limits(cx: &Context, e: &ast::Expr) {
683     return match e.node {
684         ast::ExprBinary(binop, l, r) => {
685             if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
686                 cx.span_lint(TypeLimits, e.span,
687                              "comparison is useless due to type limits");
688             }
689         },
690         ast::ExprLit(lit) => {
691             match ty::get(ty::expr_ty(cx.tcx, e)).sty {
692                 ty::ty_int(t) => {
693                     let int_type = if t == ast::TyI {
694                         cx.tcx.sess.targ_cfg.int_type
695                     } else { t };
696                     let (min, max) = int_ty_range(int_type);
697                     let mut lit_val: i64 = match lit.node {
698                         ast::LitInt(v, _) => v,
699                         ast::LitUint(v, _) => v as i64,
700                         ast::LitIntUnsuffixed(v) => v,
701                         _ => fail!()
702                     };
703                     if cx.negated_expr_id == e.id {
704                         lit_val *= -1;
705                     }
706                     if  lit_val < min || lit_val > max {
707                         cx.span_lint(TypeOverflow, e.span,
708                                      "literal out of range for its type");
709                     }
710                 },
711                 ty::ty_uint(t) => {
712                     let uint_type = if t == ast::TyU {
713                         cx.tcx.sess.targ_cfg.uint_type
714                     } else { t };
715                     let (min, max) = uint_ty_range(uint_type);
716                     let lit_val: u64 = match lit.node {
717                         ast::LitInt(v, _) => v as u64,
718                         ast::LitUint(v, _) => v,
719                         ast::LitIntUnsuffixed(v) => v as u64,
720                         _ => fail!()
721                     };
722                     if  lit_val < min || lit_val > max {
723                         cx.span_lint(TypeOverflow, e.span,
724                                      "literal out of range for its type");
725                     }
726                 },
727
728                 _ => ()
729             };
730         },
731         _ => ()
732     };
733
734     fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
735                             min: T, max: T) -> bool {
736         match binop {
737             ast::BiLt => v <= max,
738             ast::BiLe => v < max,
739             ast::BiGt => v >= min,
740             ast::BiGe => v > min,
741             ast::BiEq | ast::BiNe => v >= min && v <= max,
742             _ => fail!()
743         }
744     }
745
746     fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
747         match binop {
748             ast::BiLt => ast::BiGt,
749             ast::BiLe => ast::BiGe,
750             ast::BiGt => ast::BiLt,
751             ast::BiGe => ast::BiLe,
752             _ => binop
753         }
754     }
755
756     // for int & uint, be conservative with the warnings, so that the
757     // warnings are consistent between 32- and 64-bit platforms
758     fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
759         match int_ty {
760             ast::TyI =>    (i64::MIN,        i64::MAX),
761             ast::TyI8 =>   (i8::MIN  as i64, i8::MAX  as i64),
762             ast::TyI16 =>  (i16::MIN as i64, i16::MAX as i64),
763             ast::TyI32 =>  (i32::MIN as i64, i32::MAX as i64),
764             ast::TyI64 =>  (i64::MIN,        i64::MAX)
765         }
766     }
767
768     fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
769         match uint_ty {
770             ast::TyU =>   (u64::MIN,         u64::MAX),
771             ast::TyU8 =>  (u8::MIN   as u64, u8::MAX   as u64),
772             ast::TyU16 => (u16::MIN  as u64, u16::MAX  as u64),
773             ast::TyU32 => (u32::MIN  as u64, u32::MAX  as u64),
774             ast::TyU64 => (u64::MIN,         u64::MAX)
775         }
776     }
777
778     fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
779                     l: &ast::Expr, r: &ast::Expr) -> bool {
780         let (lit, expr, swap) = match (&l.node, &r.node) {
781             (&ast::ExprLit(_), _) => (l, r, true),
782             (_, &ast::ExprLit(_)) => (r, l, false),
783             _ => return true
784         };
785         // Normalize the binop so that the literal is always on the RHS in
786         // the comparison
787         let norm_binop = if swap { rev_binop(binop) } else { binop };
788         match ty::get(ty::expr_ty(tcx, expr)).sty {
789             ty::ty_int(int_ty) => {
790                 let (min, max) = int_ty_range(int_ty);
791                 let lit_val: i64 = match lit.node {
792                     ast::ExprLit(li) => match li.node {
793                         ast::LitInt(v, _) => v,
794                         ast::LitUint(v, _) => v as i64,
795                         ast::LitIntUnsuffixed(v) => v,
796                         _ => return true
797                     },
798                     _ => fail!()
799                 };
800                 is_valid(norm_binop, lit_val, min, max)
801             }
802             ty::ty_uint(uint_ty) => {
803                 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
804                 let lit_val: u64 = match lit.node {
805                     ast::ExprLit(li) => match li.node {
806                         ast::LitInt(v, _) => v as u64,
807                         ast::LitUint(v, _) => v,
808                         ast::LitIntUnsuffixed(v) => v as u64,
809                         _ => return true
810                     },
811                     _ => fail!()
812                 };
813                 is_valid(norm_binop, lit_val, min, max)
814             }
815             _ => true
816         }
817     }
818
819     fn is_comparison(binop: ast::BinOp) -> bool {
820         match binop {
821             ast::BiEq | ast::BiLt | ast::BiLe |
822             ast::BiNe | ast::BiGe | ast::BiGt => true,
823             _ => false
824         }
825     }
826 }
827
828 fn check_item_ctypes(cx: &Context, it: &ast::Item) {
829     fn check_ty(cx: &Context, ty: &ast::Ty) {
830         match ty.node {
831             ast::TyPath(_, _, id) => {
832                 let def_map = cx.tcx.def_map.borrow();
833                 match def_map.get().get_copy(&id) {
834                     ast::DefPrimTy(ast::TyInt(ast::TyI)) => {
835                         cx.span_lint(CTypes, ty.span,
836                                 "found rust type `int` in foreign module, while \
837                                 libc::c_int or libc::c_long should be used");
838                     }
839                     ast::DefPrimTy(ast::TyUint(ast::TyU)) => {
840                         cx.span_lint(CTypes, ty.span,
841                                 "found rust type `uint` in foreign module, while \
842                                 libc::c_uint or libc::c_ulong should be used");
843                     }
844                     ast::DefTy(def_id) => {
845                         if !adt::is_ffi_safe(cx.tcx, def_id) {
846                             cx.span_lint(CTypes, ty.span,
847                                          "found enum type without foreign-function-safe \
848                                           representation annotation in foreign module");
849                             // hmm... this message could be more helpful
850                         }
851                     }
852                     _ => ()
853                 }
854             }
855             ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) }
856             _ => {}
857         }
858     }
859
860     fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
861         for input in decl.inputs.iter() {
862             check_ty(cx, input.ty);
863         }
864         check_ty(cx, decl.output)
865     }
866
867     match it.node {
868       ast::ItemForeignMod(ref nmod) if !nmod.abis.is_intrinsic() => {
869         for ni in nmod.items.iter() {
870             match ni.node {
871                 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
872                 ast::ForeignItemStatic(t, _) => check_ty(cx, t)
873             }
874         }
875       }
876       _ => {/* nothing to do */ }
877     }
878 }
879
880 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
881     let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory];
882     for &lint in xs.iter() {
883         if cx.get_level(lint) == allow { continue }
884
885         let mut n_box = 0;
886         let mut n_uniq = 0;
887         ty::fold_ty(cx.tcx, ty, |t| {
888             match ty::get(t).sty {
889                 ty::ty_box(_) => {
890                     n_box += 1;
891                 }
892                 ty::ty_uniq(_) | ty::ty_str(ty::vstore_uniq) |
893                 ty::ty_vec(_, ty::vstore_uniq) |
894                 ty::ty_trait(_, _, ty::UniqTraitStore, _, _) => {
895                     n_uniq += 1;
896                 }
897                 ty::ty_closure(ref c) if c.sigil == ast::OwnedSigil => {
898                     n_uniq += 1;
899                 }
900
901                 _ => ()
902             };
903             t
904         });
905
906         if n_uniq > 0 && lint != ManagedHeapMemory {
907             let s = ty_to_str(cx.tcx, ty);
908             let m = format!("type uses owned (~ type) pointers: {}", s);
909             cx.span_lint(lint, span, m);
910         }
911
912         if n_box > 0 && lint != OwnedHeapMemory {
913             let s = ty_to_str(cx.tcx, ty);
914             let m = format!("type uses managed (@ type) pointers: {}", s);
915             cx.span_lint(lint, span, m);
916         }
917     }
918 }
919
920 fn check_heap_item(cx: &Context, it: &ast::Item) {
921     match it.node {
922         ast::ItemFn(..) |
923         ast::ItemTy(..) |
924         ast::ItemEnum(..) |
925         ast::ItemStruct(..) => check_heap_type(cx, it.span,
926                                                ty::node_id_to_type(cx.tcx,
927                                                                    it.id)),
928         _ => ()
929     }
930
931     // If it's a struct, we also have to check the fields' types
932     match it.node {
933         ast::ItemStruct(struct_def, _) => {
934             for struct_field in struct_def.fields.iter() {
935                 check_heap_type(cx, struct_field.span,
936                                 ty::node_id_to_type(cx.tcx,
937                                                     struct_field.node.id));
938             }
939         }
940         _ => ()
941     }
942 }
943
944 static crate_attrs: &'static [&'static str] = &[
945     "crate_type", "feature", "no_uv", "no_main", "no_std", "crate_id",
946     "desc", "comment", "license", "copyright", // not used in rustc now
947 ];
948
949
950 static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
951     ("abi", "Use `extern \"abi\" fn` instead"),
952     ("auto_encode", "Use `#[deriving(Encodable)]` instead"),
953     ("auto_decode", "Use `#[deriving(Decodable)]` instead"),
954     ("fast_ffi", "Remove it"),
955     ("fixed_stack_segment", "Remove it"),
956     ("rust_stack", "Remove it"),
957 ];
958
959 static other_attrs: &'static [&'static str] = &[
960     // item-level
961     "address_insignificant", // can be crate-level too
962     "thread_local", // for statics
963     "allow", "deny", "forbid", "warn", // lint options
964     "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
965     "crate_map", "cfg", "doc", "export_name", "link_section",
966     "no_mangle", "static_assert", "unsafe_no_drop_flag", "packed",
967     "simd", "repr", "deriving", "unsafe_destructor", "link", "phase",
968     "macro_export", "must_use",
969
970     //mod-level
971     "path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude",
972
973     // fn-level
974     "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
975     "no_split_stack", "cold", "macro_registrar",
976
977     // internal attribute: bypass privacy inside items
978     "!resolve_unexported",
979 ];
980
981 fn check_crate_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
982
983     for attr in attrs.iter() {
984         let name = attr.node.value.name();
985         let mut iter = crate_attrs.iter().chain(other_attrs.iter());
986         if !iter.any(|other_attr| { name.equiv(other_attr) }) {
987             cx.span_lint(AttributeUsage, attr.span, "unknown crate attribute");
988         }
989         if name.equiv(& &"link") {
990             cx.tcx.sess.span_err(attr.span,
991                                  "obsolete crate `link` attribute");
992             cx.tcx.sess.note("the link attribute has been superceded by the crate_id \
993                              attribute, which has the format `#[crate_id = \"name#version\"]`");
994         }
995     }
996 }
997
998 fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
999     // check if element has crate-level, obsolete, or any unknown attributes.
1000
1001     for attr in attrs.iter() {
1002         let name = attr.node.value.name();
1003         for crate_attr in crate_attrs.iter() {
1004             if name.equiv(crate_attr) {
1005                 let msg = match attr.node.style {
1006                     ast::AttrOuter => "crate-level attribute should be an inner attribute: \
1007                                        add semicolon at end",
1008                     ast::AttrInner => "crate-level attribute should be in the root module",
1009                 };
1010                 cx.span_lint(AttributeUsage, attr.span, msg);
1011                 return;
1012             }
1013         }
1014
1015         for &(obs_attr, obs_alter) in obsolete_attrs.iter() {
1016             if name.equiv(&obs_attr) {
1017                 cx.span_lint(AttributeUsage, attr.span,
1018                              format!("obsolete attribute: {:s}", obs_alter));
1019                 return;
1020             }
1021         }
1022
1023         if !other_attrs.iter().any(|other_attr| { name.equiv(other_attr) }) {
1024             cx.span_lint(AttributeUsage, attr.span, "unknown attribute");
1025         }
1026     }
1027 }
1028
1029 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
1030     let ty = ty::expr_ty(cx.tcx, e);
1031     check_heap_type(cx, e.span, ty);
1032 }
1033
1034 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
1035     match s.node {
1036         ast::StmtSemi(expr, _) => {
1037             match expr.node {
1038                 ast::ExprPath(_) => {
1039                     cx.span_lint(PathStatement,
1040                                  s.span,
1041                                  "path statement with no effect");
1042                 }
1043                 _ => {}
1044             }
1045         }
1046         _ => ()
1047     }
1048 }
1049
1050 fn check_unused_result(cx: &Context, s: &ast::Stmt) {
1051     let expr = match s.node {
1052         ast::StmtSemi(expr, _) => expr,
1053         _ => return
1054     };
1055     let t = ty::expr_ty(cx.tcx, expr);
1056     match ty::get(t).sty {
1057         ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
1058         _ => {}
1059     }
1060     match expr.node {
1061         ast::ExprRet(..) => return,
1062         _ => {}
1063     }
1064
1065     let t = ty::expr_ty(cx.tcx, expr);
1066     let mut warned = false;
1067     match ty::get(t).sty {
1068         ty::ty_struct(did, _) |
1069         ty::ty_enum(did, _) => {
1070             if ast_util::is_local(did) {
1071                 match cx.tcx.map.get(did.node) {
1072                     ast_map::NodeItem(it) => {
1073                         if attr::contains_name(it.attrs, "must_use") {
1074                             cx.span_lint(UnusedMustUse, s.span,
1075                                          "unused result which must be used");
1076                             warned = true;
1077                         }
1078                     }
1079                     _ => {}
1080                 }
1081             } else {
1082                 csearch::get_item_attrs(cx.tcx.sess.cstore, did, |attrs| {
1083                     if attr::contains_name(attrs, "must_use") {
1084                         cx.span_lint(UnusedMustUse, s.span,
1085                                      "unused result which must be used");
1086                         warned = true;
1087                     }
1088                 });
1089             }
1090         }
1091         _ => {}
1092     }
1093     if !warned {
1094         cx.span_lint(UnusedResult, s.span, "unused result");
1095     }
1096 }
1097
1098 fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
1099     fn is_camel_case(ident: ast::Ident) -> bool {
1100         let ident = token::get_ident(ident);
1101         assert!(!ident.get().is_empty());
1102         let ident = ident.get().trim_chars(&'_');
1103
1104         // start with a non-lowercase letter rather than non-uppercase
1105         // ones (some scripts don't have a concept of upper/lowercase)
1106         !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
1107     }
1108
1109     fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1110         if !is_camel_case(ident) {
1111             cx.span_lint(
1112                 NonCamelCaseTypes, span,
1113                 format!("{} `{}` should have a camel case identifier",
1114                     sort, token::get_ident(ident)));
1115         }
1116     }
1117
1118     match it.node {
1119         ast::ItemTy(..) | ast::ItemStruct(..) => {
1120             check_case(cx, "type", it.ident, it.span)
1121         }
1122         ast::ItemTrait(..) => {
1123             check_case(cx, "trait", it.ident, it.span)
1124         }
1125         ast::ItemEnum(ref enum_definition, _) => {
1126             check_case(cx, "type", it.ident, it.span);
1127             for variant in enum_definition.variants.iter() {
1128                 check_case(cx, "variant", variant.node.name, variant.span);
1129             }
1130         }
1131         _ => ()
1132     }
1133 }
1134
1135 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
1136     match it.node {
1137         // only check static constants
1138         ast::ItemStatic(_, ast::MutImmutable, _) => {
1139             let s = token::get_ident(it.ident);
1140             // check for lowercase letters rather than non-uppercase
1141             // ones (some scripts don't have a concept of
1142             // upper/lowercase)
1143             if s.get().chars().any(|c| c.is_lowercase()) {
1144                 cx.span_lint(NonUppercaseStatics, it.span,
1145                              "static constant should have an uppercase identifier");
1146             }
1147         }
1148         _ => {}
1149     }
1150 }
1151
1152 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
1153     // Lint for constants that look like binding identifiers (#7526)
1154     let def_map = cx.tcx.def_map.borrow();
1155     match (&p.node, def_map.get().find(&p.id)) {
1156         (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
1157             // last identifier alone is right choice for this lint.
1158             let ident = path.segments.last().unwrap().identifier;
1159             let s = token::get_ident(ident);
1160             if s.get().chars().any(|c| c.is_lowercase()) {
1161                 cx.span_lint(NonUppercasePatternStatics, path.span,
1162                              "static constant in pattern should be all caps");
1163             }
1164         }
1165         _ => {}
1166     }
1167 }
1168
1169 fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
1170     match value.node {
1171         ast::ExprParen(_) => {
1172             cx.span_lint(UnnecessaryParens, value.span,
1173                          format!("unnecessary parentheses around {}", msg))
1174         }
1175         _ => {}
1176     }
1177 }
1178
1179 fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
1180     let (value, msg) = match e.node {
1181         ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
1182         ast::ExprWhile(cond, _) => (cond, "`while` condition"),
1183         ast::ExprMatch(head, _) => (head, "`match` head expression"),
1184         ast::ExprRet(Some(value)) => (value, "`return` value"),
1185         ast::ExprAssign(_, value) => (value, "assigned value"),
1186         ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
1187         _ => return
1188     };
1189     check_unnecessary_parens_core(cx, value, msg);
1190 }
1191
1192 fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
1193     let (value, msg) = match s.node {
1194         ast::StmtDecl(decl, _) => match decl.node {
1195             ast::DeclLocal(local) => match local.init {
1196                 Some(value) => (value, "assigned value"),
1197                 None => return
1198             },
1199             _ => return
1200         },
1201         _ => return
1202     };
1203     check_unnecessary_parens_core(cx, value, msg);
1204 }
1205
1206 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
1207     match e.node {
1208         // Don't warn about generated blocks, that'll just pollute the output.
1209         ast::ExprBlock(ref blk) => {
1210             let used_unsafe = cx.tcx.used_unsafe.borrow();
1211             if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1212                 !used_unsafe.get().contains(&blk.id) {
1213                 cx.span_lint(UnusedUnsafe, blk.span,
1214                              "unnecessary `unsafe` block");
1215             }
1216         }
1217         _ => ()
1218     }
1219 }
1220
1221 fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
1222     match e.node {
1223         // Don't warn about generated blocks, that'll just pollute the output.
1224         ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
1225             cx.span_lint(UnsafeBlock, blk.span, "usage of an `unsafe` block");
1226         }
1227         _ => ()
1228     }
1229 }
1230
1231 fn check_unused_mut_pat(cx: &Context, p: &ast::Pat) {
1232     match p.node {
1233         ast::PatIdent(ast::BindByValue(ast::MutMutable),
1234                       ref path, _) if pat_util::pat_is_binding(cx.tcx.def_map, p)=> {
1235             // `let mut _a = 1;` doesn't need a warning.
1236             let initial_underscore = if path.segments.len() == 1 {
1237                 token::get_ident(path.segments[0].identifier).get()
1238                                                              .starts_with("_")
1239             } else {
1240                 cx.tcx.sess.span_bug(p.span,
1241                                      "mutable binding that doesn't consist \
1242                                       of exactly one segment")
1243             };
1244
1245             let used_mut_nodes = cx.tcx.used_mut_nodes.borrow();
1246             if !initial_underscore && !used_mut_nodes.get().contains(&p.id) {
1247                 cx.span_lint(UnusedMut, p.span,
1248                              "variable does not need to be mutable");
1249             }
1250         }
1251         _ => ()
1252     }
1253 }
1254
1255 enum Allocation {
1256     VectorAllocation,
1257     BoxAllocation
1258 }
1259
1260 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1261     // Warn if string and vector literals with sigils, or boxing expressions,
1262     // are immediately borrowed.
1263     let allocation = match e.node {
1264         ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
1265             match e2.node {
1266                 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
1267                     VectorAllocation
1268                 }
1269                 ast::ExprVec(..) => VectorAllocation,
1270                 _ => return
1271             }
1272         }
1273         ast::ExprUnary(ast::UnUniq, _) |
1274         ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
1275
1276         _ => return
1277     };
1278
1279     let report = |msg| {
1280         cx.span_lint(UnnecessaryAllocation, e.span, msg);
1281     };
1282
1283     let adjustment = {
1284         let adjustments = cx.tcx.adjustments.borrow();
1285         adjustments.get().find_copy(&e.id)
1286     };
1287     match adjustment {
1288         Some(adjustment) => {
1289             match *adjustment {
1290                 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
1291                     match (allocation, autoref) {
1292                         (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
1293                             report("unnecessary allocation, the sigil can be \
1294                                     removed");
1295                         }
1296                         (BoxAllocation,
1297                          Some(ty::AutoPtr(_, ast::MutImmutable))) => {
1298                             report("unnecessary allocation, use & instead");
1299                         }
1300                         (BoxAllocation,
1301                          Some(ty::AutoPtr(_, ast::MutMutable))) => {
1302                             report("unnecessary allocation, use &mut \
1303                                     instead");
1304                         }
1305                         _ => ()
1306                     }
1307                 }
1308                 _ => {}
1309             }
1310         }
1311
1312         _ => ()
1313     }
1314 }
1315
1316 fn check_missing_doc_attrs(cx: &Context,
1317                            id: Option<ast::NodeId>,
1318                            attrs: &[ast::Attribute],
1319                            sp: Span,
1320                            desc: &'static str) {
1321     // If we're building a test harness, then warning about
1322     // documentation is probably not really relevant right now.
1323     if cx.tcx.sess.opts.test { return }
1324
1325     // `#[doc(hidden)]` disables missing_doc check.
1326     if cx.is_doc_hidden { return }
1327
1328     // Only check publicly-visible items, using the result from the privacy pass. It's an option so
1329     // the crate root can also use this function (it doesn't have a NodeId).
1330     match id {
1331         Some(ref id) if !cx.exported_items.contains(id) => return,
1332         _ => ()
1333     }
1334
1335     let has_doc = attrs.iter().any(|a| {
1336         match a.node.value.node {
1337             ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
1338             _ => false
1339         }
1340     });
1341     if !has_doc {
1342         cx.span_lint(MissingDoc, sp,
1343                      format!("missing documentation for {}", desc));
1344     }
1345 }
1346
1347 fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
1348     let desc = match it.node {
1349         ast::ItemFn(..) => "a function",
1350         ast::ItemMod(..) => "a module",
1351         ast::ItemEnum(..) => "an enum",
1352         ast::ItemStruct(..) => "a struct",
1353         ast::ItemTrait(..) => "a trait",
1354         _ => return
1355     };
1356     check_missing_doc_attrs(cx, Some(it.id), it.attrs, it.span, desc);
1357 }
1358
1359 fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
1360     let did = ast::DefId {
1361         krate: ast::LOCAL_CRATE,
1362         node: m.id
1363     };
1364
1365     let method_opt;
1366     {
1367         let methods = cx.tcx.methods.borrow();
1368         method_opt = methods.get().find(&did).map(|method| *method);
1369     }
1370
1371     match method_opt {
1372         None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
1373         Some(md) => {
1374             match md.container {
1375                 // Always check default methods defined on traits.
1376                 ty::TraitContainer(..) => {}
1377                 // For methods defined on impls, it depends on whether
1378                 // it is an implementation for a trait or is a plain
1379                 // impl.
1380                 ty::ImplContainer(cid) => {
1381                     match ty::impl_trait_ref(cx.tcx, cid) {
1382                         Some(..) => return, // impl for trait: don't doc
1383                         None => {} // plain impl: doc according to privacy
1384                     }
1385                 }
1386             }
1387         }
1388     }
1389     check_missing_doc_attrs(cx, Some(m.id), m.attrs, m.span, "a method");
1390 }
1391
1392 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
1393     check_missing_doc_attrs(cx, Some(tm.id), tm.attrs, tm.span, "a type method");
1394 }
1395
1396 fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
1397     match sf.node.kind {
1398         ast::NamedField(_, vis) if vis != ast::Private =>
1399             check_missing_doc_attrs(cx, Some(cx.cur_struct_def_id), sf.node.attrs,
1400                                     sf.span, "a struct field"),
1401         _ => {}
1402     }
1403 }
1404
1405 fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
1406     check_missing_doc_attrs(cx, Some(v.node.id), v.node.attrs, v.span, "a variant");
1407 }
1408
1409 /// Checks for use of items with #[deprecated], #[experimental] and
1410 /// #[unstable] (or none of them) attributes.
1411 fn check_stability(cx: &Context, e: &ast::Expr) {
1412     let id = match e.node {
1413         ast::ExprPath(..) | ast::ExprStruct(..) => {
1414             let def_map = cx.tcx.def_map.borrow();
1415             match def_map.get().find(&e.id) {
1416                 Some(&def) => ast_util::def_id_of_def(def),
1417                 None => return
1418             }
1419         }
1420         ast::ExprMethodCall(..) => {
1421             match cx.method_map.borrow().get().find(&e.id) {
1422                 Some(method) => {
1423                     match method.origin {
1424                         typeck::MethodStatic(def_id) => {
1425                             // If this implements a trait method, get def_id
1426                             // of the method inside trait definition.
1427                             // Otherwise, use the current def_id (which refers
1428                             // to the method inside impl).
1429                             ty::trait_method_of_method(
1430                                 cx.tcx, def_id).unwrap_or(def_id)
1431                         }
1432                         typeck::MethodParam(typeck::MethodParam {
1433                             trait_id: trait_id,
1434                             method_num: index,
1435                             ..
1436                         })
1437                         | typeck::MethodObject(typeck::MethodObject {
1438                             trait_id: trait_id,
1439                             method_num: index,
1440                             ..
1441                         }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1442                     }
1443                 }
1444                 None => return
1445             }
1446         }
1447         _ => return
1448     };
1449
1450     let stability = if ast_util::is_local(id) {
1451         // this crate
1452         let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1453             attrs.map(|a| {
1454                 attr::find_stability(a.iter().map(|a| a.meta()))
1455             })
1456         });
1457         match s {
1458             Some(s) => s,
1459
1460             // no possibility of having attributes
1461             // (e.g. it's a local variable), so just
1462             // ignore it.
1463             None => return
1464         }
1465     } else {
1466         // cross-crate
1467
1468         let mut s = None;
1469         // run through all the attributes and take the first
1470         // stability one.
1471         csearch::get_item_attrs(cx.tcx.cstore, id, |meta_items| {
1472             if s.is_none() {
1473                 s = attr::find_stability(meta_items.move_iter())
1474             }
1475         });
1476         s
1477     };
1478
1479     let (lint, label) = match stability {
1480         // no stability attributes == Unstable
1481         None => (Unstable, "unmarked"),
1482         Some(attr::Stability { level: attr::Unstable, .. }) =>
1483                 (Unstable, "unstable"),
1484         Some(attr::Stability { level: attr::Experimental, .. }) =>
1485                 (Experimental, "experimental"),
1486         Some(attr::Stability { level: attr::Deprecated, .. }) =>
1487                 (Deprecated, "deprecated"),
1488         _ => return
1489     };
1490
1491     let msg = match stability {
1492         Some(attr::Stability { text: Some(ref s), .. }) => {
1493             format!("use of {} item: {}", label, *s)
1494         }
1495         _ => format!("use of {} item", label)
1496     };
1497
1498     cx.span_lint(lint, e.span, msg);
1499 }
1500
1501 impl<'a> Visitor<()> for Context<'a> {
1502     fn visit_item(&mut self, it: &ast::Item, _: ()) {
1503         self.with_lint_attrs(it.attrs, |cx| {
1504             check_item_ctypes(cx, it);
1505             check_item_non_camel_case_types(cx, it);
1506             check_item_non_uppercase_statics(cx, it);
1507             check_heap_item(cx, it);
1508             check_missing_doc_item(cx, it);
1509             check_attrs_usage(cx, it.attrs);
1510
1511             cx.visit_ids(|v| v.visit_item(it, ()));
1512
1513             visit::walk_item(cx, it, ());
1514         })
1515     }
1516
1517     fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
1518         self.with_lint_attrs(it.attrs, |cx| {
1519             check_attrs_usage(cx, it.attrs);
1520             visit::walk_foreign_item(cx, it, ());
1521         })
1522     }
1523
1524     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1525         self.with_lint_attrs(i.attrs, |cx| {
1526             check_attrs_usage(cx, i.attrs);
1527             visit::walk_view_item(cx, i, ());
1528         })
1529     }
1530
1531     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1532         check_pat_non_uppercase_statics(self, p);
1533         check_unused_mut_pat(self, p);
1534
1535         visit::walk_pat(self, p, ());
1536     }
1537
1538     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
1539         match e.node {
1540             ast::ExprUnary(ast::UnNeg, expr) => {
1541                 // propagate negation, if the negation itself isn't negated
1542                 if self.negated_expr_id != e.id {
1543                     self.negated_expr_id = expr.id;
1544                 }
1545             },
1546             ast::ExprParen(expr) => if self.negated_expr_id == e.id {
1547                 self.negated_expr_id = expr.id
1548             },
1549             _ => ()
1550         };
1551
1552         check_while_true_expr(self, e);
1553         check_stability(self, e);
1554         check_unnecessary_parens_expr(self, e);
1555         check_unused_unsafe(self, e);
1556         check_unsafe_block(self, e);
1557         check_unnecessary_allocation(self, e);
1558         check_heap_expr(self, e);
1559
1560         check_type_limits(self, e);
1561         check_unused_casts(self, e);
1562
1563         visit::walk_expr(self, e, ());
1564     }
1565
1566     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
1567         check_path_statement(self, s);
1568         check_unused_result(self, s);
1569         check_unnecessary_parens_stmt(self, s);
1570
1571         visit::walk_stmt(self, s, ());
1572     }
1573
1574     fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
1575                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1576         let recurse = |this: &mut Context| {
1577             visit::walk_fn(this, fk, decl, body, span, id, ());
1578         };
1579
1580         match *fk {
1581             visit::FkMethod(_, _, m) => {
1582                 self.with_lint_attrs(m.attrs, |cx| {
1583                     check_missing_doc_method(cx, m);
1584                     check_attrs_usage(cx, m.attrs);
1585
1586                     cx.visit_ids(|v| {
1587                         v.visit_fn(fk, decl, body, span, id, ());
1588                     });
1589                     recurse(cx);
1590                 })
1591             }
1592             _ => recurse(self),
1593         }
1594     }
1595
1596
1597     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1598         self.with_lint_attrs(t.attrs, |cx| {
1599             check_missing_doc_ty_method(cx, t);
1600             check_attrs_usage(cx, t.attrs);
1601
1602             visit::walk_ty_method(cx, t, ());
1603         })
1604     }
1605
1606     fn visit_struct_def(&mut self,
1607                         s: &ast::StructDef,
1608                         i: ast::Ident,
1609                         g: &ast::Generics,
1610                         id: ast::NodeId,
1611                         _: ()) {
1612         let old_id = self.cur_struct_def_id;
1613         self.cur_struct_def_id = id;
1614         visit::walk_struct_def(self, s, i, g, id, ());
1615         self.cur_struct_def_id = old_id;
1616     }
1617
1618     fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1619         self.with_lint_attrs(s.node.attrs, |cx| {
1620             check_missing_doc_struct_field(cx, s);
1621             check_attrs_usage(cx, s.node.attrs);
1622
1623             visit::walk_struct_field(cx, s, ());
1624         })
1625     }
1626
1627     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1628         self.with_lint_attrs(v.node.attrs, |cx| {
1629             check_missing_doc_variant(cx, v);
1630             check_attrs_usage(cx, v.node.attrs);
1631
1632             visit::walk_variant(cx, v, g, ());
1633         })
1634     }
1635
1636     // FIXME(#10894) should continue recursing
1637     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
1638 }
1639
1640 impl<'a> IdVisitingOperation for Context<'a> {
1641     fn visit_id(&self, id: ast::NodeId) {
1642         let mut lints = self.tcx.sess.lints.borrow_mut();
1643         match lints.get().pop(&id) {
1644             None => {}
1645             Some(l) => {
1646                 for (lint, span, msg) in l.move_iter() {
1647                     self.span_lint(lint, span, msg)
1648                 }
1649             }
1650         }
1651     }
1652 }
1653
1654 pub fn check_crate(tcx: ty::ctxt,
1655                    method_map: typeck::MethodMap,
1656                    exported_items: &privacy::ExportedItems,
1657                    krate: &ast::Crate) {
1658     let mut cx = Context {
1659         dict: @get_lint_dict(),
1660         cur: SmallIntMap::new(),
1661         tcx: tcx,
1662         method_map: method_map,
1663         exported_items: exported_items,
1664         cur_struct_def_id: -1,
1665         is_doc_hidden: false,
1666         lint_stack: ~[],
1667         negated_expr_id: -1
1668     };
1669
1670     // Install default lint levels, followed by the command line levels, and
1671     // then actually visit the whole crate.
1672     for (_, spec) in cx.dict.iter() {
1673         cx.set_level(spec.lint, spec.default, Default);
1674     }
1675     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1676         cx.set_level(lint, level, CommandLine);
1677     }
1678     cx.with_lint_attrs(krate.attrs, |cx| {
1679         cx.visit_id(ast::CRATE_NODE_ID);
1680         cx.visit_ids(|v| {
1681             v.visited_outermost = true;
1682             visit::walk_crate(v, krate, ());
1683         });
1684
1685         check_crate_attrs_usage(cx, krate.attrs);
1686         // since the root module isn't visited as an item (because it isn't an item), warn for it
1687         // here.
1688         check_missing_doc_attrs(cx, None, krate.attrs, krate.span, "crate");
1689
1690         visit::walk_crate(cx, krate, ());
1691     });
1692
1693     // If we missed any lints added to the session, then there's a bug somewhere
1694     // in the iteration code.
1695     let lints = tcx.sess.lints.borrow();
1696     for (id, v) in lints.get().iter() {
1697         for &(lint, span, ref msg) in v.iter() {
1698             tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1699                                             lint, tcx.map.node_to_str(*id), *msg))
1700         }
1701     }
1702
1703     tcx.sess.abort_if_errors();
1704 }