]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/mod.rs
Replace enum LintId with an extensible alternative
[rust.git] / src / librustc / lint / mod.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 //! Most of the lints built into `rustc` are structs implementing `LintPass`,
32 //! and are defined within `builtin.rs`. To add a new lint you can define such
33 //! a struct and add it to the `builtin_lints!` macro invocation in this file.
34 //! `LintPass` itself is not a subtrait of `Default`, but the `builtin_lints!`
35 //! macro requires `Default` (usually via `deriving`).
36 //!
37 //! Some lints are defined elsewhere in the compiler and work by calling
38 //! `add_lint()` on the overall `Session` object.
39 //!
40 //! If you're adding lints to the `Context` infrastructure itself, defined in
41 //! this file, use `span_lint` instead of `add_lint`.
42
43 #![allow(non_camel_case_types)]
44 #![macro_escape]
45
46 use middle::privacy::ExportedItems;
47 use middle::ty;
48 use middle::typeck::astconv::AstConv;
49 use middle::typeck::infer;
50 use driver::session::Session;
51
52 use std::collections::HashMap;
53 use std::rc::Rc;
54 use std::gc::Gc;
55 use std::to_str::ToStr;
56 use std::cell::RefCell;
57 use std::default::Default;
58 use std::hash::Hash;
59 use std::tuple::Tuple2;
60 use std::hash;
61 use syntax::ast_util::IdVisitingOperation;
62 use syntax::attr::AttrMetaMethods;
63 use syntax::attr;
64 use syntax::codemap::Span;
65 use syntax::visit::{Visitor, FnKind};
66 use syntax::{ast, ast_util, visit};
67
68 #[macro_export]
69 macro_rules! lint_initializer (
70     ($name:ident, $level:ident, $desc:expr) => (
71         ::rustc::lint::Lint {
72             name: stringify!($name),
73             default_level: ::rustc::lint::$level,
74             desc: $desc,
75         }
76     )
77 )
78
79 #[macro_export]
80 macro_rules! declare_lint (
81     // FIXME(#14660): deduplicate
82     (pub $name:ident, $level:ident, $desc:expr) => (
83         pub static $name: &'static ::rustc::lint::Lint
84             = &lint_initializer!($name, $level, $desc);
85     );
86     ($name:ident, $level:ident, $desc:expr) => (
87         static $name: &'static ::rustc::lint::Lint
88             = &lint_initializer!($name, $level, $desc);
89     );
90 )
91
92 #[macro_export]
93 macro_rules! lint_array ( ($( $lint:expr ),*) => (
94     {
95         static array: LintArray = &[ $( $lint ),* ];
96         array
97     }
98 ))
99
100 pub mod builtin;
101
102 /// Specification of a single lint.
103 pub struct Lint {
104     /// An identifier for the lint, written with underscores,
105     /// e.g. "unused_imports". This identifies the lint in
106     /// attributes and in command-line arguments. On the
107     /// command line, underscores become dashes.
108     pub name: &'static str,
109
110     /// Default level for the lint.
111     pub default_level: Level,
112
113     /// Description of the lint or the issue it detects,
114     /// e.g. "imports that are never used"
115     pub desc: &'static str,
116 }
117
118 type LintArray = &'static [&'static Lint];
119
120 /// Trait for types providing lint checks. Each `check` method checks a single
121 /// syntax node, and should not invoke methods recursively (unlike `Visitor`).
122 /// By default they do nothing.
123 //
124 // FIXME: eliminate the duplication with `Visitor`. But this also
125 // contains a few lint-specific methods with no equivalent in `Visitor`.
126 trait LintPass {
127     /// Get descriptions of the lints this `LintPass` object can emit.
128     ///
129     /// NB: there is no enforcement that the object only emits lints it registered.
130     /// And some `rustc` internal `LintPass`es register lints to be emitted by other
131     /// parts of the compiler. If you want enforced access restrictions for your
132     /// `Lint`, make it a private `static` item in its own module.
133     fn get_lints(&self) -> LintArray;
134
135     fn check_crate(&mut self, _: &Context, _: &ExportedItems, _: &ast::Crate) { }
136     fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { }
137     fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { }
138     fn check_view_item(&mut self, _: &Context, _: &ast::ViewItem) { }
139     fn check_foreign_item(&mut self, _: &Context, _: &ast::ForeignItem) { }
140     fn check_item(&mut self, _: &Context, _: &ast::Item) { }
141     fn check_local(&mut self, _: &Context, _: &ast::Local) { }
142     fn check_block(&mut self, _: &Context, _: &ast::Block) { }
143     fn check_stmt(&mut self, _: &Context, _: &ast::Stmt) { }
144     fn check_arm(&mut self, _: &Context, _: &ast::Arm) { }
145     fn check_pat(&mut self, _: &Context, _: &ast::Pat) { }
146     fn check_decl(&mut self, _: &Context, _: &ast::Decl) { }
147     fn check_expr(&mut self, _: &Context, _: &ast::Expr) { }
148     fn check_expr_post(&mut self, _: &Context, _: &ast::Expr) { }
149     fn check_ty(&mut self, _: &Context, _: &ast::Ty) { }
150     fn check_generics(&mut self, _: &Context, _: &ast::Generics) { }
151     fn check_fn(&mut self, _: &Context,
152         _: &FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
153     fn check_ty_method(&mut self, _: &Context, _: &ast::TypeMethod) { }
154     fn check_trait_method(&mut self, _: &Context, _: &ast::TraitMethod) { }
155     fn check_struct_def(&mut self, _: &Context,
156         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
157     fn check_struct_def_post(&mut self, _: &Context,
158         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
159     fn check_struct_field(&mut self, _: &Context, _: &ast::StructField) { }
160     fn check_variant(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { }
161     fn check_opt_lifetime_ref(&mut self, _: &Context, _: Span, _: &Option<ast::Lifetime>) { }
162     fn check_lifetime_ref(&mut self, _: &Context, _: &ast::Lifetime) { }
163     fn check_lifetime_decl(&mut self, _: &Context, _: &ast::Lifetime) { }
164     fn check_explicit_self(&mut self, _: &Context, _: &ast::ExplicitSelf) { }
165     fn check_mac(&mut self, _: &Context, _: &ast::Mac) { }
166     fn check_path(&mut self, _: &Context, _: &ast::Path, _: ast::NodeId) { }
167     fn check_attribute(&mut self, _: &Context, _: &ast::Attribute) { }
168
169     /// Called when entering a syntax node that can have lint attributes such
170     /// as `#[allow(...)]`. Called with *all* the attributes of that node.
171     fn enter_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
172
173     /// Counterpart to `enter_lint_attrs`.
174     fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
175 }
176
177 type LintPassObject = Box<LintPass + 'static>;
178
179 /// Identifies a lint known to the compiler.
180 #[deriving(Clone)]
181 pub struct LintId {
182     // Identity is based on pointer equality of this field.
183     lint: &'static Lint,
184 }
185
186 impl PartialEq for LintId {
187     fn eq(&self, other: &LintId) -> bool {
188         (self.lint as *Lint) == (other.lint as *Lint)
189     }
190 }
191
192 impl Eq for LintId { }
193
194 impl<S: hash::Writer> Hash<S> for LintId {
195     fn hash(&self, state: &mut S) {
196         let ptr = self.lint as *Lint;
197         ptr.hash(state);
198     }
199 }
200
201 impl LintId {
202     pub fn of(lint: &'static Lint) -> LintId {
203         LintId {
204             lint: lint,
205         }
206     }
207
208     pub fn as_str(&self) -> &'static str {
209         self.lint.name
210     }
211 }
212
213 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
214 pub enum Level {
215     Allow, Warn, Deny, Forbid
216 }
217
218 impl Level {
219     pub fn as_str(self) -> &'static str {
220         match self {
221             Allow => "allow",
222             Warn => "warn",
223             Deny => "deny",
224             Forbid => "forbid",
225         }
226     }
227
228     pub fn from_str(x: &str) -> Option<Level> {
229         match x {
230             "allow" => Some(Allow),
231             "warn" => Some(Warn),
232             "deny" => Some(Deny),
233             "forbid" => Some(Forbid),
234             _ => None,
235         }
236     }
237 }
238
239 // this is public for the lints that run in trans
240 #[deriving(PartialEq)]
241 pub enum LintSource {
242     Node(Span),
243     Default,
244     CommandLine
245 }
246
247 pub type LevelSource = (Level, LintSource);
248
249 struct Context<'a> {
250     /// Trait objects for each lint pass.
251     lint_objects: Vec<RefCell<LintPassObject>>,
252
253     /// Lints indexed by name.
254     lints_by_name: HashMap<&'static str, LintId>,
255
256     /// Current levels of each lint, and where they were set.
257     levels: HashMap<LintId, LevelSource>,
258
259     /// Context we're checking in (used to access fields like sess).
260     tcx: &'a ty::ctxt,
261
262     /// When recursing into an attributed node of the ast which modifies lint
263     /// levels, this stack keeps track of the previous lint levels of whatever
264     /// was modified.
265     level_stack: Vec<(LintId, LevelSource)>,
266
267     /// Level of lints for certain NodeIds, stored here because the body of
268     /// the lint needs to run in trans.
269     node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
270 }
271
272 /// Convenience macro for calling a `LintPass` method on every pass in the context.
273 macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => (
274     for obj in $cx.lint_objects.iter() {
275         obj.borrow_mut().$f($cx, $($args),*);
276     }
277 ))
278
279 /// Emit a lint as a `span_warn` or `span_err` (or not at all)
280 /// according to `level`.  This lives outside of `Context` so
281 /// it can be used by checks in trans that run after the main
282 /// lint phase is finished.
283 pub fn emit_lint(sess: &Session, lint: &'static Lint,
284                  lvlsrc: LevelSource, span: Span, msg: &str) {
285     let (level, source) = lvlsrc;
286     if level == Allow { return }
287
288     let mut note = None;
289     let msg = match source {
290         Default => {
291             format!("{}, #[{}({})] on by default", msg,
292                 level_to_str(level), lint_str)
293         },
294         CommandLine => {
295             format!("{} [-{} {}]", msg,
296                 match level {
297                     Warn => 'W', Deny => 'D', Forbid => 'F',
298                     Allow => fail!()
299                 }, lint.name.replace("_", "-"))
300         },
301         Node(src) => {
302             note = Some(src);
303             msg.to_string()
304         }
305     };
306
307     match level {
308         Warn =>          { sess.span_warn(span, msg.as_slice()); }
309         Deny | Forbid => { sess.span_err(span, msg.as_slice());  }
310         Allow => fail!(),
311     }
312
313     for span in note.move_iter() {
314         sess.span_note(span, "lint level defined here");
315     }
316 }
317
318 impl<'a> Context<'a> {
319     fn get_level_source(&self, lint: LintId) -> LevelSource {
320         match self.levels.find(&lint) {
321             Some(&s) => s,
322             None => (Allow, Default),
323         }
324     }
325
326     fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
327         if lvlsrc.val0() == Allow {
328             self.levels.remove(&lint);
329         } else {
330             self.levels.insert(lint, lvlsrc);
331         }
332     }
333
334     fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
335         let (level, src) = match self.levels.find(&LintId::of(lint)) {
336             None => return,
337             Some(&(Warn, src))
338                 => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src),
339             Some(&pair) => pair,
340         };
341
342         emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
343     }
344
345     /**
346      * Merge the lints specified by any lint attributes into the
347      * current lint context, call the provided function, then reset the
348      * lints in effect to their previous state.
349      */
350     fn with_lint_attrs(&mut self,
351                        attrs: &[ast::Attribute],
352                        f: |&mut Context|) {
353         // Parse all of the lint attributes, and then add them all to the
354         // current dictionary of lint information. Along the way, keep a history
355         // of what we changed so we can roll everything back after invoking the
356         // specified closure
357         let lint_attrs = self.gather_lint_attrs(attrs);
358         let mut pushed = 0u;
359         for (lint_id, level, span) in lint_attrs.move_iter() {
360             let now = self.get_level_source(lint_id).val0();
361             if now == Forbid && level != Forbid {
362                 let lint_name = lint_id.as_str();
363                 self.tcx.sess.span_err(span,
364                 format!("{}({}) overruled by outer forbid({})",
365                         level.as_str(), lint_name, lint_name).as_slice());
366             } else if now != level {
367                 let src = self.get_level_source(lint_id).val1();
368                 self.level_stack.push((lint_id, (now, src)));
369                 pushed += 1;
370                 self.set_level(lint_id, (level, Node(span)));
371             }
372         }
373
374         run_lints!(self, enter_lint_attrs, attrs);
375         f(self);
376         run_lints!(self, exit_lint_attrs, attrs);
377
378         // rollback
379         for _ in range(0, pushed) {
380             let (lint, lvlsrc) = self.level_stack.pop().unwrap();
381             self.set_level(lint, lvlsrc);
382         }
383     }
384
385     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
386         let mut v = ast_util::IdVisitor {
387             operation: self,
388             pass_through_items: false,
389             visited_outermost: false,
390         };
391         f(&mut v);
392     }
393
394     fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvlsrc: LevelSource) {
395         self.node_levels.borrow_mut().insert((id, lint), lvlsrc);
396     }
397
398     fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> {
399         // Doing this as an iterator is messy due to multiple borrowing.
400         // Allocating and copying these should be quick.
401         let mut out = vec!();
402         for attr in attrs.iter() {
403             let level = match Level::from_str(attr.name().get()) {
404                 None => continue,
405                 Some(lvl) => lvl,
406             };
407
408             attr::mark_used(attr);
409
410             let meta = attr.node.value;
411             let metas = match meta.node {
412                 ast::MetaList(_, ref metas) => metas,
413                 _ => {
414                     self.tcx.sess.span_err(meta.span, "malformed lint attribute");
415                     continue;
416                 }
417             };
418
419             for meta in metas.iter() {
420                 match meta.node {
421                     ast::MetaWord(ref lint_name) => {
422                         match self.lints_by_name.find_equiv(lint_name) {
423                             Some(lint_id) => out.push((*lint_id, level, meta.span)),
424
425                             None => self.span_lint(builtin::unrecognized_lint,
426                                 meta.span,
427                                 format!("unknown `{}` attribute: `{}`",
428                                     level.as_str(), lint_name).as_slice()),
429                         }
430                     }
431                     _ => self.tcx.sess.span_err(meta.span, "malformed lint attribute"),
432                 }
433             }
434         }
435         out
436     }
437 }
438
439 impl<'a> AstConv for Context<'a>{
440     fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
441
442     fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
443         ty::lookup_item_type(self.tcx, id)
444     }
445
446     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
447         ty::lookup_trait_def(self.tcx, id)
448     }
449
450     fn ty_infer(&self, _span: Span) -> ty::t {
451         infer::new_infer_ctxt(self.tcx).next_ty_var()
452     }
453 }
454
455 impl<'a> Visitor<()> for Context<'a> {
456     fn visit_item(&mut self, it: &ast::Item, _: ()) {
457         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
458             run_lints!(cx, check_item, it);
459             cx.visit_ids(|v| v.visit_item(it, ()));
460             visit::walk_item(cx, it, ());
461         })
462     }
463
464     fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
465         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
466             run_lints!(cx, check_foreign_item, it);
467             visit::walk_foreign_item(cx, it, ());
468         })
469     }
470
471     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
472         self.with_lint_attrs(i.attrs.as_slice(), |cx| {
473             run_lints!(cx, check_view_item, i);
474             cx.visit_ids(|v| v.visit_view_item(i, ()));
475             visit::walk_view_item(cx, i, ());
476         })
477     }
478
479     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
480         run_lints!(self, check_pat, p);
481         visit::walk_pat(self, p, ());
482     }
483
484     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
485         run_lints!(self, check_expr, e);
486         visit::walk_expr(self, e, ());
487     }
488
489     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
490         run_lints!(self, check_stmt, s);
491         visit::walk_stmt(self, s, ());
492     }
493
494     fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl,
495                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
496         match *fk {
497             visit::FkMethod(_, _, m) => {
498                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
499                     run_lints!(cx, check_fn, fk, decl, body, span, id);
500                     cx.visit_ids(|v| {
501                         v.visit_fn(fk, decl, body, span, id, ());
502                     });
503                     visit::walk_fn(cx, fk, decl, body, span, ());
504                 })
505             },
506             _ => {
507                 run_lints!(self, check_fn, fk, decl, body, span, id);
508                 visit::walk_fn(self, fk, decl, body, span, ());
509             }
510         }
511     }
512
513     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
514         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
515             run_lints!(cx, check_ty_method, t);
516             visit::walk_ty_method(cx, t, ());
517         })
518     }
519
520     fn visit_struct_def(&mut self,
521                         s: &ast::StructDef,
522                         ident: ast::Ident,
523                         g: &ast::Generics,
524                         id: ast::NodeId,
525                         _: ()) {
526         run_lints!(self, check_struct_def, s, ident, g, id);
527         visit::walk_struct_def(self, s, ());
528         run_lints!(self, check_struct_def_post, s, ident, g, id);
529     }
530
531     fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
532         self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
533             run_lints!(cx, check_struct_field, s);
534             visit::walk_struct_field(cx, s, ());
535         })
536     }
537
538     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
539         self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
540             run_lints!(cx, check_variant, v, g);
541             visit::walk_variant(cx, v, g, ());
542         })
543     }
544
545     // FIXME(#10894) should continue recursing
546     fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
547         run_lints!(self, check_ty, t);
548     }
549
550     fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
551         run_lints!(self, check_ident, sp, id);
552     }
553
554     fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) {
555         run_lints!(self, check_mod, m, s, n);
556         visit::walk_mod(self, m, ());
557     }
558
559     fn visit_local(&mut self, l: &ast::Local, _: ()) {
560         run_lints!(self, check_local, l);
561         visit::walk_local(self, l, ());
562     }
563
564     fn visit_block(&mut self, b: &ast::Block, _: ()) {
565         run_lints!(self, check_block, b);
566         visit::walk_block(self, b, ());
567     }
568
569     fn visit_arm(&mut self, a: &ast::Arm, _: ()) {
570         run_lints!(self, check_arm, a);
571         visit::walk_arm(self, a, ());
572     }
573
574     fn visit_decl(&mut self, d: &ast::Decl, _: ()) {
575         run_lints!(self, check_decl, d);
576         visit::walk_decl(self, d, ());
577     }
578
579     fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) {
580         run_lints!(self, check_expr_post, e);
581     }
582
583     fn visit_generics(&mut self, g: &ast::Generics, _: ()) {
584         run_lints!(self, check_generics, g);
585         visit::walk_generics(self, g, ());
586     }
587
588     fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) {
589         run_lints!(self, check_trait_method, m);
590         visit::walk_trait_method(self, m, ());
591     }
592
593     fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>, _: ()) {
594         run_lints!(self, check_opt_lifetime_ref, sp, lt);
595     }
596
597     fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) {
598         run_lints!(self, check_lifetime_ref, lt);
599     }
600
601     fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) {
602         run_lints!(self, check_lifetime_decl, lt);
603     }
604
605     fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) {
606         run_lints!(self, check_explicit_self, es);
607         visit::walk_explicit_self(self, es, ());
608     }
609
610     fn visit_mac(&mut self, mac: &ast::Mac, _: ()) {
611         run_lints!(self, check_mac, mac);
612         visit::walk_mac(self, mac, ());
613     }
614
615     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) {
616         run_lints!(self, check_path, p, id);
617         visit::walk_path(self, p, ());
618     }
619
620     fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
621         run_lints!(self, check_attribute, attr);
622     }
623 }
624
625 // Output any lints that were previously added to the session.
626 impl<'a> IdVisitingOperation for Context<'a> {
627     fn visit_id(&self, id: ast::NodeId) {
628         match self.tcx.sess.lints.borrow_mut().pop(&id) {
629             None => {}
630             Some(lints) => {
631                 for (lint_id, span, msg) in lints.move_iter() {
632                     self.span_lint(lint_id.lint, span, msg.as_slice())
633                 }
634             }
635         }
636     }
637 }
638
639 fn builtin_lints() -> Vec<Box<LintPass>> {
640     macro_rules! builtin_lints (( $($name:ident),*, ) => (
641         vec!($(
642             {
643                 let obj: builtin::$name = Default::default();
644                 box obj as LintPassObject
645             }
646         ),*)
647     ))
648
649     builtin_lints!(
650         WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
651         RawPointerDeriving, UnusedAttribute, PathStatement,
652         UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
653         NonSnakeCaseFunctions, NonUppercaseStatics,
654         NonUppercasePatternStatics, UppercaseVariables,
655         UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
656         UnnecessaryAllocation, MissingDoc, Stability,
657
658         GatherNodeLevels, HardwiredLints,
659     )
660 }
661
662 /// Get specs for all builtin lints.  Used for `-W help`.
663 pub fn builtin_lint_specs() -> Vec<&'static Lint> {
664     builtin_lints().move_iter()
665         .flat_map(|x| x.get_lints().iter().map(|&y| y))
666         .collect()
667 }
668
669 pub fn check_crate(tcx: &ty::ctxt,
670                    exported_items: &ExportedItems,
671                    krate: &ast::Crate) {
672     let lints = builtin_lints().move_iter().map(|x| RefCell::new(x)).collect();
673
674     let mut cx = Context {
675         lint_objects: lints,
676         lints_by_name: HashMap::new(),
677         levels: HashMap::new(),
678         tcx: tcx,
679         level_stack: Vec::new(),
680         node_levels: RefCell::new(HashMap::new()),
681     };
682
683     // Index the lints by name, and set the default levels.
684     for obj in cx.lint_objects.iter() {
685         for &lint in obj.borrow_mut().get_lints().iter() {
686             let id = LintId::of(lint);
687             if !cx.lints_by_name.insert(lint.name, id) {
688                 cx.tcx.sess.err(format!("duplicate specification of lint {}",
689                     lint.name).as_slice());
690             }
691             if lint.default_level != Allow {
692                 cx.levels.insert(id, (lint.default_level, Default));
693             }
694         }
695     }
696
697     // Set command line lint levels.
698     for &(ref lint_name, level) in tcx.sess.opts.lint_opts.iter() {
699         match cx.lints_by_name.find_equiv(&lint_name.as_slice()) {
700             Some(&lint_id) => cx.set_level(lint_id, (level, CommandLine)),
701             None => cx.tcx.sess.err(format!("unknown {} flag: {}",
702                 level.as_str(), lint_name).as_slice()),
703         }
704     }
705
706     tcx.sess.abort_if_errors();
707
708     // Visit the whole crate.
709     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
710         cx.visit_id(ast::CRATE_NODE_ID);
711         cx.visit_ids(|v| {
712             v.visited_outermost = true;
713             visit::walk_crate(v, krate, ());
714         });
715
716         // since the root module isn't visited as an item (because it isn't an
717         // item), warn for it here.
718         run_lints!(cx, check_crate, exported_items, krate);
719
720         visit::walk_crate(cx, krate, ());
721     });
722
723     // If we missed any lints added to the session, then there's a bug somewhere
724     // in the iteration code.
725     for (id, v) in tcx.sess.lints.borrow().iter() {
726         for &(lint, span, ref msg) in v.iter() {
727             tcx.sess.span_bug(span,
728                 format!("unprocessed lint {} at {}: {}",
729                     lint.as_str(), tcx.map.node_to_str(*id), *msg)
730                 .as_slice())
731         }
732     }
733
734     tcx.sess.abort_if_errors();
735     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap();
736 }