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