]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/context.rs
Doc says to avoid mixing allocator instead of forbiding it
[rust.git] / src / librustc / lint / context.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 //! Implementation of lint checking.
12 //!
13 //! The lint checking is mostly consolidated into one pass which runs just
14 //! before translation to LLVM bytecode. Throughout compilation, lint warnings
15 //! can be added via the `add_lint` method on the Session structure. This
16 //! requires a span and an id of the node that the lint is being added to. The
17 //! lint isn't actually emitted at that time because it is unknown what the
18 //! actual lint level at that location is.
19 //!
20 //! To actually emit lint warnings/errors, a separate pass is used just before
21 //! translation. A context keeps track of the current state of all lint levels.
22 //! Upon entering a node of the ast which can modify the lint settings, the
23 //! previous lint state is pushed onto a stack and the ast is then recursed
24 //! upon.  As the ast is traversed, this keeps track of the current lint level
25 //! for all lint attributes.
26
27 use middle::privacy::ExportedItems;
28 use middle::ty;
29 use middle::typeck::astconv::AstConv;
30 use middle::typeck::infer;
31 use driver::session::Session;
32 use driver::early_error;
33 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject};
34 use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
35 use lint::builtin;
36
37 use std::collections::HashMap;
38 use std::rc::Rc;
39 use std::cell::RefCell;
40 use std::tuple::Tuple2;
41 use std::mem;
42 use syntax::ast_util::IdVisitingOperation;
43 use syntax::attr::AttrMetaMethods;
44 use syntax::attr;
45 use syntax::codemap::Span;
46 use syntax::visit::{Visitor, FnKind};
47 use syntax::parse::token::InternedString;
48 use syntax::{ast, ast_util, visit};
49
50 /// Information about the registered lints.
51 ///
52 /// This is basically the subset of `Context` that we can
53 /// build early in the compile pipeline.
54 pub struct LintStore {
55     /// Registered lints. The bool is true if the lint was
56     /// added by a plugin.
57     lints: Vec<(&'static Lint, bool)>,
58
59     /// Trait objects for each lint pass.
60     /// This is only `None` while iterating over the objects. See the definition
61     /// of run_lints.
62     passes: Option<Vec<LintPassObject>>,
63
64     /// Lints indexed by name.
65     by_name: HashMap<String, LintId>,
66
67     /// Current levels of each lint, and where they were set.
68     levels: HashMap<LintId, LevelSource>,
69
70     /// Map of registered lint groups to what lints they expand to. The bool
71     /// is true if the lint group was added by a plugin.
72     lint_groups: HashMap<&'static str, (Vec<LintId>, bool)>,
73 }
74
75 impl LintStore {
76     fn get_level_source(&self, lint: LintId) -> LevelSource {
77         match self.levels.find(&lint) {
78             Some(&s) => s,
79             None => (Allow, Default),
80         }
81     }
82
83     fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
84         if lvlsrc.val0() == Allow {
85             self.levels.remove(&lint);
86         } else {
87             self.levels.insert(lint, lvlsrc);
88         }
89     }
90
91     pub fn new() -> LintStore {
92         LintStore {
93             lints: vec!(),
94             passes: Some(vec!()),
95             by_name: HashMap::new(),
96             levels: HashMap::new(),
97             lint_groups: HashMap::new(),
98         }
99     }
100
101     pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
102         self.lints.as_slice()
103     }
104
105     pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
106         self.lint_groups.iter().map(|(k, v)| (*k,
107                                               v.ref0().clone(),
108                                               *v.ref1())).collect()
109     }
110
111     pub fn register_pass(&mut self, sess: Option<&Session>,
112                          from_plugin: bool, pass: LintPassObject) {
113         for &lint in pass.get_lints().iter() {
114             self.lints.push((lint, from_plugin));
115
116             let id = LintId::of(lint);
117             if !self.by_name.insert(lint.name_lower(), id) {
118                 let msg = format!("duplicate specification of lint {}", lint.name_lower());
119                 match (sess, from_plugin) {
120                     // We load builtin lints first, so a duplicate is a compiler bug.
121                     // Use early_error when handling -W help with no crate.
122                     (None, _) => early_error(msg.as_slice()),
123                     (Some(sess), false) => sess.bug(msg.as_slice()),
124
125                     // A duplicate name from a plugin is a user error.
126                     (Some(sess), true)  => sess.err(msg.as_slice()),
127                 }
128             }
129
130             if lint.default_level != Allow {
131                 self.levels.insert(id, (lint.default_level, Default));
132             }
133         }
134         self.passes.get_mut_ref().push(pass);
135     }
136
137     pub fn register_group(&mut self, sess: Option<&Session>,
138                           from_plugin: bool, name: &'static str,
139                           to: Vec<LintId>) {
140         let new = self.lint_groups.insert(name, (to, from_plugin));
141
142         if !new {
143             let msg = format!("duplicate specification of lint group {}", name);
144             match (sess, from_plugin) {
145                 // We load builtin lints first, so a duplicate is a compiler bug.
146                 // Use early_error when handling -W help with no crate.
147                 (None, _) => early_error(msg.as_slice()),
148                 (Some(sess), false) => sess.bug(msg.as_slice()),
149
150                 // A duplicate name from a plugin is a user error.
151                 (Some(sess), true)  => sess.err(msg.as_slice()),
152             }
153         }
154     }
155
156     pub fn register_builtin(&mut self, sess: Option<&Session>) {
157         macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => (
158             {$(
159                 self.register_pass($sess, false, box builtin::$name as LintPassObject);
160             )*}
161         ))
162
163         macro_rules! add_builtin_with_new ( ( $sess:ident, $($name:ident),*, ) => (
164             {$(
165                 self.register_pass($sess, false, box builtin::$name::new() as LintPassObject);
166             )*}
167         ))
168
169         macro_rules! add_lint_group ( ( $sess:ident, $name:expr, $($lint:ident),* ) => (
170             self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]);
171         ))
172
173         add_builtin!(sess,
174                      HardwiredLints,
175                      WhileTrue,
176                      UnusedCasts,
177                      CTypes,
178                      HeapMemory,
179                      UnusedAttribute,
180                      PathStatement,
181                      UnusedResult,
182                      NonCamelCaseTypes,
183                      NonSnakeCase,
184                      NonUppercaseStatics,
185                      UnnecessaryParens,
186                      UnusedUnsafe,
187                      UnsafeBlock,
188                      UnusedMut,
189                      UnnecessaryAllocation,
190                      Stability,
191         )
192
193         add_builtin_with_new!(sess,
194                               TypeLimits,
195                               RawPointerDeriving,
196                               MissingDoc,
197         )
198
199         add_lint_group!(sess, "bad_style",
200                         NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPERCASE_STATICS)
201
202         add_lint_group!(sess, "unused",
203                         UNUSED_IMPORTS, UNUSED_VARIABLE, DEAD_ASSIGNMENT, DEAD_CODE,
204                         UNUSED_MUT, UNREACHABLE_CODE, UNUSED_EXTERN_CRATE)
205
206         // We have one lint pass defined in this module.
207         self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
208     }
209
210     pub fn process_command_line(&mut self, sess: &Session) {
211         for &(ref lint_name, level) in sess.opts.lint_opts.iter() {
212             match self.by_name.find_equiv(&lint_name.as_slice()) {
213                 Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)),
214                 None => {
215                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.ref0().clone()))
216                                                  .collect::<HashMap<&'static str, Vec<LintId>>>()
217                                                  .find_equiv(&lint_name.as_slice()) {
218                         Some(v) => {
219                             v.iter()
220                              .map(|lint_id: &LintId|
221                                      self.set_level(*lint_id, (level, CommandLine)))
222                              .collect::<Vec<()>>();
223                         }
224                         None => sess.err(format!("unknown {} flag: {}",
225                                                  level.as_str(), lint_name).as_slice()),
226                     }
227                 }
228             }
229         }
230     }
231 }
232
233 /// Context for lint checking.
234 pub struct Context<'a, 'tcx: 'a> {
235     /// Type context we're checking in.
236     pub tcx: &'a ty::ctxt<'tcx>,
237
238     /// The crate being checked.
239     pub krate: &'a ast::Crate,
240
241     /// Items exported from the crate being checked.
242     pub exported_items: &'a ExportedItems,
243
244     /// The store of registered lints.
245     lints: LintStore,
246
247     /// When recursing into an attributed node of the ast which modifies lint
248     /// levels, this stack keeps track of the previous lint levels of whatever
249     /// was modified.
250     level_stack: Vec<(LintId, LevelSource)>,
251
252     /// Level of lints for certain NodeIds, stored here because the body of
253     /// the lint needs to run in trans.
254     node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
255 }
256
257 /// Convenience macro for calling a `LintPass` method on every pass in the context.
258 macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ({
259     // Move the vector of passes out of `$cx` so that we can
260     // iterate over it mutably while passing `$cx` to the methods.
261     let mut passes = $cx.lints.passes.take().unwrap();
262     for obj in passes.mut_iter() {
263         obj.$f($cx, $($args),*);
264     }
265     $cx.lints.passes = Some(passes);
266 }))
267
268 /// Parse the lint attributes into a vector, with `Err`s for malformed lint
269 /// attributes. Writing this as an iterator is an enormous mess.
270 pub fn gather_attrs(attrs: &[ast::Attribute])
271                     -> Vec<Result<(InternedString, Level, Span), Span>> {
272     let mut out = vec!();
273     for attr in attrs.iter() {
274         let level = match Level::from_str(attr.name().get()) {
275             None => continue,
276             Some(lvl) => lvl,
277         };
278
279         attr::mark_used(attr);
280
281         let meta = &attr.node.value;
282         let metas = match meta.node {
283             ast::MetaList(_, ref metas) => metas,
284             _ => {
285                 out.push(Err(meta.span));
286                 continue;
287             }
288         };
289
290         for meta in metas.iter() {
291             out.push(match meta.node {
292                 ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
293                 _ => Err(meta.span),
294             });
295         }
296     }
297     out
298 }
299
300 /// Emit a lint as a warning or an error (or not at all)
301 /// according to `level`.
302 ///
303 /// This lives outside of `Context` so it can be used by checks
304 /// in trans that run after the main lint pass is finished. Most
305 /// lints elsewhere in the compiler should call
306 /// `Session::add_lint()` instead.
307 pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
308                      lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
309     let (mut level, source) = lvlsrc;
310     if level == Allow { return }
311
312     let name = lint.name_lower();
313     let mut note = None;
314     let msg = match source {
315         Default => {
316             format!("{}, #[{}({})] on by default", msg,
317                     level.as_str(), name)
318         },
319         CommandLine => {
320             format!("{} [-{} {}]", msg,
321                     match level {
322                         Warn => 'W', Deny => 'D', Forbid => 'F',
323                         Allow => fail!()
324                     }, name.replace("_", "-"))
325         },
326         Node(src) => {
327             note = Some(src);
328             msg.to_string()
329         }
330     };
331
332     // For purposes of printing, we can treat forbid as deny.
333     if level == Forbid { level = Deny; }
334
335     match (level, span) {
336         (Warn, Some(sp)) => sess.span_warn(sp, msg.as_slice()),
337         (Warn, None)     => sess.warn(msg.as_slice()),
338         (Deny, Some(sp)) => sess.span_err(sp, msg.as_slice()),
339         (Deny, None)     => sess.err(msg.as_slice()),
340         _ => sess.bug("impossible level in raw_emit_lint"),
341     }
342
343     for span in note.move_iter() {
344         sess.span_note(span, "lint level defined here");
345     }
346 }
347
348 impl<'a, 'tcx> Context<'a, 'tcx> {
349     fn new(tcx: &'a ty::ctxt<'tcx>,
350            krate: &'a ast::Crate,
351            exported_items: &'a ExportedItems) -> Context<'a, 'tcx> {
352         // We want to own the lint store, so move it out of the session.
353         let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
354                                       LintStore::new());
355
356         Context {
357             tcx: tcx,
358             krate: krate,
359             exported_items: exported_items,
360             lints: lint_store,
361             level_stack: vec![],
362             node_levels: RefCell::new(HashMap::new()),
363         }
364     }
365
366     /// Get the overall compiler `Session` object.
367     pub fn sess(&'a self) -> &'a Session {
368         &self.tcx.sess
369     }
370
371     /// Get the level of `lint` at the current position of the lint
372     /// traversal.
373     pub fn current_level(&self, lint: &'static Lint) -> Level {
374         self.lints.levels.find(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
375     }
376
377     fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
378         let (level, src) = match self.lints.levels.find(&LintId::of(lint)) {
379             None => return,
380             Some(&(Warn, src)) => {
381                 let lint_id = LintId::of(builtin::WARNINGS);
382                 (self.lints.get_level_source(lint_id).val0(), src)
383             }
384             Some(&pair) => pair,
385         };
386
387         raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
388     }
389
390     /// Emit a lint at the appropriate level, with no associated span.
391     pub fn lint(&self, lint: &'static Lint, msg: &str) {
392         self.lookup_and_emit(lint, None, msg);
393     }
394
395     /// Emit a lint at the appropriate level, for a particular span.
396     pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
397         self.lookup_and_emit(lint, Some(span), msg);
398     }
399
400     /**
401      * Merge the lints specified by any lint attributes into the
402      * current lint context, call the provided function, then reset the
403      * lints in effect to their previous state.
404      */
405     fn with_lint_attrs(&mut self,
406                        attrs: &[ast::Attribute],
407                        f: |&mut Context|) {
408         // Parse all of the lint attributes, and then add them all to the
409         // current dictionary of lint information. Along the way, keep a history
410         // of what we changed so we can roll everything back after invoking the
411         // specified closure
412         let mut pushed = 0u;
413
414         for result in gather_attrs(attrs).move_iter() {
415             let v = match result {
416                 Err(span) => {
417                     self.tcx.sess.span_err(span, "malformed lint attribute");
418                     continue;
419                 }
420                 Ok((lint_name, level, span)) => {
421                     match self.lints.by_name.find_equiv(&lint_name.get()) {
422                         Some(&lint_id) => vec![(lint_id, level, span)],
423                         None => {
424                             match self.lints.lint_groups.find_equiv(&lint_name.get()) {
425                                 Some(&(ref v, _)) => v.iter()
426                                                       .map(|lint_id: &LintId|
427                                                            (*lint_id, level, span))
428                                                       .collect(),
429                                 None => {
430                                     self.span_lint(builtin::UNRECOGNIZED_LINT, span,
431                                                format!("unknown `{}` attribute: `{}`",
432                                                        level.as_str(), lint_name).as_slice());
433                                     continue;
434                                 }
435                             }
436                         }
437                     }
438                 }
439             };
440
441             for (lint_id, level, span) in v.move_iter() {
442                 let now = self.lints.get_level_source(lint_id).val0();
443                 if now == Forbid && level != Forbid {
444                     let lint_name = lint_id.as_str();
445                     self.tcx.sess.span_err(span,
446                                            format!("{}({}) overruled by outer forbid({})",
447                                                    level.as_str(), lint_name,
448                                                    lint_name).as_slice());
449                 } else if now != level {
450                     let src = self.lints.get_level_source(lint_id).val1();
451                     self.level_stack.push((lint_id, (now, src)));
452                     pushed += 1;
453                     self.lints.set_level(lint_id, (level, Node(span)));
454                 }
455             }
456         }
457
458         run_lints!(self, enter_lint_attrs, attrs);
459         f(self);
460         run_lints!(self, exit_lint_attrs, attrs);
461
462         // rollback
463         for _ in range(0, pushed) {
464             let (lint, lvlsrc) = self.level_stack.pop().unwrap();
465             self.lints.set_level(lint, lvlsrc);
466         }
467     }
468
469     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
470         let mut v = ast_util::IdVisitor {
471             operation: self,
472             pass_through_items: false,
473             visited_outermost: false,
474         };
475         f(&mut v);
476     }
477 }
478
479 impl<'a, 'tcx> AstConv<'tcx> for Context<'a, 'tcx>{
480     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
481
482     fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype {
483         ty::lookup_item_type(self.tcx, id)
484     }
485
486     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
487         ty::lookup_trait_def(self.tcx, id)
488     }
489
490     fn ty_infer(&self, _span: Span) -> ty::t {
491         infer::new_infer_ctxt(self.tcx).next_ty_var()
492     }
493 }
494
495 impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
496     fn visit_item(&mut self, it: &ast::Item) {
497         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
498             run_lints!(cx, check_item, it);
499             cx.visit_ids(|v| v.visit_item(it));
500             visit::walk_item(cx, it);
501         })
502     }
503
504     fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
505         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
506             run_lints!(cx, check_foreign_item, it);
507             visit::walk_foreign_item(cx, it);
508         })
509     }
510
511     fn visit_view_item(&mut self, i: &ast::ViewItem) {
512         self.with_lint_attrs(i.attrs.as_slice(), |cx| {
513             run_lints!(cx, check_view_item, i);
514             cx.visit_ids(|v| v.visit_view_item(i));
515             visit::walk_view_item(cx, i);
516         })
517     }
518
519     fn visit_pat(&mut self, p: &ast::Pat) {
520         run_lints!(self, check_pat, p);
521         visit::walk_pat(self, p);
522     }
523
524     fn visit_expr(&mut self, e: &ast::Expr) {
525         run_lints!(self, check_expr, e);
526         visit::walk_expr(self, e);
527     }
528
529     fn visit_stmt(&mut self, s: &ast::Stmt) {
530         run_lints!(self, check_stmt, s);
531         visit::walk_stmt(self, s);
532     }
533
534     fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v ast::FnDecl,
535                 body: &'v ast::Block, span: Span, id: ast::NodeId) {
536         match fk {
537             visit::FkMethod(_, _, m) => {
538                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
539                     run_lints!(cx, check_fn, fk, decl, body, span, id);
540                     cx.visit_ids(|v| {
541                         v.visit_fn(fk, decl, body, span, id);
542                     });
543                     visit::walk_fn(cx, fk, decl, body, span);
544                 })
545             },
546             _ => {
547                 run_lints!(self, check_fn, fk, decl, body, span, id);
548                 visit::walk_fn(self, fk, decl, body, span);
549             }
550         }
551     }
552
553     fn visit_ty_method(&mut self, t: &ast::TypeMethod) {
554         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
555             run_lints!(cx, check_ty_method, t);
556             visit::walk_ty_method(cx, t);
557         })
558     }
559
560     fn visit_struct_def(&mut self,
561                         s: &ast::StructDef,
562                         ident: ast::Ident,
563                         g: &ast::Generics,
564                         id: ast::NodeId) {
565         run_lints!(self, check_struct_def, s, ident, g, id);
566         visit::walk_struct_def(self, s);
567         run_lints!(self, check_struct_def_post, s, ident, g, id);
568     }
569
570     fn visit_struct_field(&mut self, s: &ast::StructField) {
571         self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
572             run_lints!(cx, check_struct_field, s);
573             visit::walk_struct_field(cx, s);
574         })
575     }
576
577     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) {
578         self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
579             run_lints!(cx, check_variant, v, g);
580             visit::walk_variant(cx, v, g);
581         })
582     }
583
584     // FIXME(#10894) should continue recursing
585     fn visit_ty(&mut self, t: &ast::Ty) {
586         run_lints!(self, check_ty, t);
587     }
588
589     fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
590         run_lints!(self, check_ident, sp, id);
591     }
592
593     fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
594         run_lints!(self, check_mod, m, s, n);
595         visit::walk_mod(self, m);
596     }
597
598     fn visit_local(&mut self, l: &ast::Local) {
599         run_lints!(self, check_local, l);
600         visit::walk_local(self, l);
601     }
602
603     fn visit_block(&mut self, b: &ast::Block) {
604         run_lints!(self, check_block, b);
605         visit::walk_block(self, b);
606     }
607
608     fn visit_arm(&mut self, a: &ast::Arm) {
609         run_lints!(self, check_arm, a);
610         visit::walk_arm(self, a);
611     }
612
613     fn visit_decl(&mut self, d: &ast::Decl) {
614         run_lints!(self, check_decl, d);
615         visit::walk_decl(self, d);
616     }
617
618     fn visit_expr_post(&mut self, e: &ast::Expr) {
619         run_lints!(self, check_expr_post, e);
620     }
621
622     fn visit_generics(&mut self, g: &ast::Generics) {
623         run_lints!(self, check_generics, g);
624         visit::walk_generics(self, g);
625     }
626
627     fn visit_trait_item(&mut self, m: &ast::TraitItem) {
628         run_lints!(self, check_trait_method, m);
629         visit::walk_trait_item(self, m);
630     }
631
632     fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>) {
633         run_lints!(self, check_opt_lifetime_ref, sp, lt);
634     }
635
636     fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime) {
637         run_lints!(self, check_lifetime_ref, lt);
638     }
639
640     fn visit_lifetime_decl(&mut self, lt: &ast::LifetimeDef) {
641         run_lints!(self, check_lifetime_decl, lt);
642     }
643
644     fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
645         run_lints!(self, check_explicit_self, es);
646         visit::walk_explicit_self(self, es);
647     }
648
649     fn visit_mac(&mut self, mac: &ast::Mac) {
650         run_lints!(self, check_mac, mac);
651         visit::walk_mac(self, mac);
652     }
653
654     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
655         run_lints!(self, check_path, p, id);
656         visit::walk_path(self, p);
657     }
658
659     fn visit_attribute(&mut self, attr: &ast::Attribute) {
660         run_lints!(self, check_attribute, attr);
661     }
662 }
663
664 // Output any lints that were previously added to the session.
665 impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> {
666     fn visit_id(&self, id: ast::NodeId) {
667         match self.tcx.sess.lints.borrow_mut().pop(&id) {
668             None => {}
669             Some(lints) => {
670                 for (lint_id, span, msg) in lints.move_iter() {
671                     self.span_lint(lint_id.lint, span, msg.as_slice())
672                 }
673             }
674         }
675     }
676 }
677
678 // This lint pass is defined here because it touches parts of the `Context`
679 // that we don't want to expose. It records the lint level at certain AST
680 // nodes, so that the variant size difference check in trans can call
681 // `raw_emit_lint`.
682
683 struct GatherNodeLevels;
684
685 impl LintPass for GatherNodeLevels {
686     fn get_lints(&self) -> LintArray {
687         lint_array!()
688     }
689
690     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
691         match it.node {
692             ast::ItemEnum(..) => {
693                 let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCE);
694                 let lvlsrc = cx.lints.get_level_source(lint_id);
695                 match lvlsrc {
696                     (lvl, _) if lvl != Allow => {
697                         cx.node_levels.borrow_mut()
698                             .insert((it.id, lint_id), lvlsrc);
699                     },
700                     _ => { }
701                 }
702             },
703             _ => { }
704         }
705     }
706 }
707
708 /// Perform lint checking on a crate.
709 ///
710 /// Consumes the `lint_store` field of the `Session`.
711 pub fn check_crate(tcx: &ty::ctxt,
712                    exported_items: &ExportedItems) {
713     let krate = tcx.map.krate();
714     let mut cx = Context::new(tcx, krate, exported_items);
715
716     // Visit the whole crate.
717     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
718         cx.visit_id(ast::CRATE_NODE_ID);
719         cx.visit_ids(|v| {
720             v.visited_outermost = true;
721             visit::walk_crate(v, krate);
722         });
723
724         // since the root module isn't visited as an item (because it isn't an
725         // item), warn for it here.
726         run_lints!(cx, check_crate, krate);
727
728         visit::walk_crate(cx, krate);
729     });
730
731     // If we missed any lints added to the session, then there's a bug somewhere
732     // in the iteration code.
733     for (id, v) in tcx.sess.lints.borrow().iter() {
734         for &(lint, span, ref msg) in v.iter() {
735             tcx.sess.span_bug(span,
736                               format!("unprocessed lint {} at {}: {}",
737                                       lint.as_str(), tcx.map.node_to_string(*id), *msg).as_slice())
738         }
739     }
740
741     tcx.sess.abort_if_errors();
742     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap();
743 }