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