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