]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/context.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[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() {
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                      Stability,
215                      UnconditionalRecursion,
216                      InvalidNoMangleItems,
217                      PluginAsLibrary,
218         );
219
220         add_builtin_with_new!(sess,
221                               TypeLimits,
222                               RawPointerDerive,
223                               MissingDoc,
224                               MissingDebugImplementations,
225         );
226
227         add_lint_group!(sess, "bad_style",
228                         NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS);
229
230         add_lint_group!(sess, "unused",
231                         UNUSED_IMPORTS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE,
232                         UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE,
233                         UNUSED_UNSAFE, PATH_STATEMENTS);
234
235         // We have one lint pass defined in this module.
236         self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
237
238         // Insert temporary renamings for a one-time deprecation
239         self.register_renamed("raw_pointer_deriving", "raw_pointer_derive");
240
241         self.register_renamed("unknown_features", "unused_features");
242     }
243
244     #[allow(unused_variables)]
245     fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>)
246                  -> Option<LintId>
247     {
248         match self.by_name.get(lint_name) {
249             Some(&Id(lint_id)) => Some(lint_id),
250             Some(&Renamed(ref new_name, lint_id)) => {
251                 let warning = format!("lint {} has been renamed to {}",
252                                       lint_name, new_name);
253                 match span {
254                     Some(span) => sess.span_warn(span, &warning[..]),
255                     None => sess.warn(&warning[..]),
256                 };
257                 Some(lint_id)
258             }
259             None => None
260         }
261     }
262
263     pub fn process_command_line(&mut self, sess: &Session) {
264         for &(ref lint_name, level) in &sess.opts.lint_opts {
265             match self.find_lint(&lint_name[..], sess, None) {
266                 Some(lint_id) => self.set_level(lint_id, (level, CommandLine)),
267                 None => {
268                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
269                                                  .collect::<FnvHashMap<&'static str,
270                                                                        Vec<LintId>>>()
271                                                  .get(&lint_name[..]) {
272                         Some(v) => {
273                             v.iter()
274                              .map(|lint_id: &LintId|
275                                      self.set_level(*lint_id, (level, CommandLine)))
276                              .collect::<Vec<()>>();
277                         }
278                         None => sess.err(&format!("unknown {} flag: {}",
279                                                  level.as_str(), lint_name)[]),
280                     }
281                 }
282             }
283         }
284     }
285
286     fn maybe_stage_features(&mut self, sess: &Session) {
287         let lvl = match sess.opts.unstable_features {
288             UnstableFeatures::Default => return,
289             UnstableFeatures::Disallow => Warn,
290             UnstableFeatures::Cheat => Allow
291         };
292         match self.by_name.get("unstable_features") {
293             Some(&Id(lint_id)) => if self.get_level_source(lint_id).0 != Forbid {
294                 self.set_level(lint_id, (lvl, ReleaseChannel))
295             },
296             Some(&Renamed(_, lint_id)) => if self.get_level_source(lint_id).0 != Forbid {
297                 self.set_level(lint_id, (lvl, ReleaseChannel))
298             },
299             None => unreachable!()
300         }
301     }
302 }
303
304 /// Context for lint checking.
305 pub struct Context<'a, 'tcx: 'a> {
306     /// Type context we're checking in.
307     pub tcx: &'a ty::ctxt<'tcx>,
308
309     /// The crate being checked.
310     pub krate: &'a ast::Crate,
311
312     /// Items exported from the crate being checked.
313     pub exported_items: &'a ExportedItems,
314
315     /// The store of registered lints.
316     lints: LintStore,
317
318     /// When recursing into an attributed node of the ast which modifies lint
319     /// levels, this stack keeps track of the previous lint levels of whatever
320     /// was modified.
321     level_stack: Vec<(LintId, LevelSource)>,
322
323     /// Level of lints for certain NodeIds, stored here because the body of
324     /// the lint needs to run in trans.
325     node_levels: RefCell<FnvHashMap<(ast::NodeId, LintId), LevelSource>>,
326 }
327
328 /// Convenience macro for calling a `LintPass` method on every pass in the context.
329 macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({
330     // Move the vector of passes out of `$cx` so that we can
331     // iterate over it mutably while passing `$cx` to the methods.
332     let mut passes = $cx.lints.passes.take().unwrap();
333     for obj in &mut passes {
334         obj.$f($cx, $($args),*);
335     }
336     $cx.lints.passes = Some(passes);
337 }) }
338
339 /// Parse the lint attributes into a vector, with `Err`s for malformed lint
340 /// attributes. Writing this as an iterator is an enormous mess.
341 pub fn gather_attrs(attrs: &[ast::Attribute])
342                     -> Vec<Result<(InternedString, Level, Span), Span>> {
343     let mut out = vec!();
344     for attr in attrs {
345         let level = match Level::from_str(&attr.name()) {
346             None => continue,
347             Some(lvl) => lvl,
348         };
349
350         attr::mark_used(attr);
351
352         let meta = &attr.node.value;
353         let metas = match meta.node {
354             ast::MetaList(_, ref metas) => metas,
355             _ => {
356                 out.push(Err(meta.span));
357                 continue;
358             }
359         };
360
361         for meta in metas {
362             out.push(match meta.node {
363                 ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
364                 _ => Err(meta.span),
365             });
366         }
367     }
368     out
369 }
370
371 /// Emit a lint as a warning or an error (or not at all)
372 /// according to `level`.
373 ///
374 /// This lives outside of `Context` so it can be used by checks
375 /// in trans that run after the main lint pass is finished. Most
376 /// lints elsewhere in the compiler should call
377 /// `Session::add_lint()` instead.
378 pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
379                      lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
380     let (mut level, source) = lvlsrc;
381     if level == Allow { return }
382
383     let name = lint.name_lower();
384     let mut def = None;
385     let mut note = None;
386     let msg = match source {
387         Default => {
388             format!("{}, #[{}({})] on by default", msg,
389                     level.as_str(), name)
390         },
391         CommandLine => {
392             format!("{} [-{} {}]", msg,
393                     match level {
394                         Warn => 'W', Deny => 'D', Forbid => 'F',
395                         Allow => panic!()
396                     }, name.replace("_", "-"))
397         },
398         Node(src) => {
399             def = Some(src);
400             msg.to_string()
401         }
402         ReleaseChannel => {
403             let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
404             note = Some(format!("this feature may not be used in the {} release channel",
405                                 release_channel));
406             msg.to_string()
407         }
408     };
409
410     // For purposes of printing, we can treat forbid as deny.
411     if level == Forbid { level = Deny; }
412
413     match (level, span) {
414         (Warn, Some(sp)) => sess.span_warn(sp, &msg[..]),
415         (Warn, None)     => sess.warn(&msg[..]),
416         (Deny, Some(sp)) => sess.span_err(sp, &msg[..]),
417         (Deny, None)     => sess.err(&msg[..]),
418         _ => sess.bug("impossible level in raw_emit_lint"),
419     }
420
421     if let Some(note) = note {
422         sess.note(&note[..]);
423     }
424
425     if let Some(span) = def {
426         sess.span_note(span, "lint level defined here");
427     }
428 }
429
430 impl<'a, 'tcx> Context<'a, 'tcx> {
431     fn new(tcx: &'a ty::ctxt<'tcx>,
432            krate: &'a ast::Crate,
433            exported_items: &'a ExportedItems) -> Context<'a, 'tcx> {
434         // We want to own the lint store, so move it out of the session.
435         let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
436                                       LintStore::new());
437
438         Context {
439             tcx: tcx,
440             krate: krate,
441             exported_items: exported_items,
442             lints: lint_store,
443             level_stack: vec![],
444             node_levels: RefCell::new(FnvHashMap()),
445         }
446     }
447
448     /// Get the overall compiler `Session` object.
449     pub fn sess(&'a self) -> &'a Session {
450         &self.tcx.sess
451     }
452
453     /// Get the level of `lint` at the current position of the lint
454     /// traversal.
455     pub fn current_level(&self, lint: &'static Lint) -> Level {
456         self.lints.levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
457     }
458
459     fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
460         let (level, src) = match self.lints.levels.get(&LintId::of(lint)) {
461             None => return,
462             Some(&(Warn, src)) => {
463                 let lint_id = LintId::of(builtin::WARNINGS);
464                 (self.lints.get_level_source(lint_id).0, src)
465             }
466             Some(&pair) => pair,
467         };
468
469         raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
470     }
471
472     /// Emit a lint at the appropriate level, with no associated span.
473     pub fn lint(&self, lint: &'static Lint, msg: &str) {
474         self.lookup_and_emit(lint, None, msg);
475     }
476
477     /// Emit a lint at the appropriate level, for a particular span.
478     pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
479         self.lookup_and_emit(lint, Some(span), msg);
480     }
481
482     /// Merge the lints specified by any lint attributes into the
483     /// current lint context, call the provided function, then reset the
484     /// lints in effect to their previous state.
485     fn with_lint_attrs<F>(&mut self,
486                           attrs: &[ast::Attribute],
487                           f: F) where
488         F: FnOnce(&mut Context),
489     {
490         // Parse all of the lint attributes, and then add them all to the
491         // current dictionary of lint information. Along the way, keep a history
492         // of what we changed so we can roll everything back after invoking the
493         // specified closure
494         let mut pushed = 0;
495
496         for result in gather_attrs(attrs) {
497             let v = match result {
498                 Err(span) => {
499                     self.tcx.sess.span_err(span, "malformed lint attribute");
500                     continue;
501                 }
502                 Ok((lint_name, level, span)) => {
503                     match self.lints.find_lint(&lint_name, &self.tcx.sess, Some(span)) {
504                         Some(lint_id) => vec![(lint_id, level, span)],
505                         None => {
506                             match self.lints.lint_groups.get(&lint_name[..]) {
507                                 Some(&(ref v, _)) => v.iter()
508                                                       .map(|lint_id: &LintId|
509                                                            (*lint_id, level, span))
510                                                       .collect(),
511                                 None => {
512                                     self.span_lint(builtin::UNKNOWN_LINTS, span,
513                                                    &format!("unknown `{}` attribute: `{}`",
514                                                             level.as_str(), lint_name));
515                                     continue;
516                                 }
517                             }
518                         }
519                     }
520                 }
521             };
522
523             for (lint_id, level, span) in v {
524                 let now = self.lints.get_level_source(lint_id).0;
525                 if now == Forbid && level != Forbid {
526                     let lint_name = lint_id.as_str();
527                     self.tcx.sess.span_err(span,
528                                            &format!("{}({}) overruled by outer forbid({})",
529                                                    level.as_str(), lint_name,
530                                                    lint_name)[]);
531                 } else if now != level {
532                     let src = self.lints.get_level_source(lint_id).1;
533                     self.level_stack.push((lint_id, (now, src)));
534                     pushed += 1;
535                     self.lints.set_level(lint_id, (level, Node(span)));
536                 }
537             }
538         }
539
540         run_lints!(self, enter_lint_attrs, attrs);
541         f(self);
542         run_lints!(self, exit_lint_attrs, attrs);
543
544         // rollback
545         for _ in 0..pushed {
546             let (lint, lvlsrc) = self.level_stack.pop().unwrap();
547             self.lints.set_level(lint, lvlsrc);
548         }
549     }
550
551     fn visit_ids<F>(&mut self, f: F) where
552         F: FnOnce(&mut ast_util::IdVisitor<Context>)
553     {
554         let mut v = ast_util::IdVisitor {
555             operation: self,
556             pass_through_items: false,
557             visited_outermost: false,
558         };
559         f(&mut v);
560     }
561 }
562
563 impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
564     fn visit_item(&mut self, it: &ast::Item) {
565         self.with_lint_attrs(&it.attrs[], |cx| {
566             run_lints!(cx, check_item, it);
567             cx.visit_ids(|v| v.visit_item(it));
568             visit::walk_item(cx, it);
569         })
570     }
571
572     fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
573         self.with_lint_attrs(&it.attrs[], |cx| {
574             run_lints!(cx, check_foreign_item, it);
575             visit::walk_foreign_item(cx, it);
576         })
577     }
578
579     fn visit_pat(&mut self, p: &ast::Pat) {
580         run_lints!(self, check_pat, p);
581         visit::walk_pat(self, p);
582     }
583
584     fn visit_expr(&mut self, e: &ast::Expr) {
585         run_lints!(self, check_expr, e);
586         visit::walk_expr(self, e);
587     }
588
589     fn visit_stmt(&mut self, s: &ast::Stmt) {
590         run_lints!(self, check_stmt, s);
591         visit::walk_stmt(self, s);
592     }
593
594     fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v ast::FnDecl,
595                 body: &'v ast::Block, span: Span, id: ast::NodeId) {
596         match fk {
597             visit::FkMethod(_, _, m) => {
598                 self.with_lint_attrs(&m.attrs[], |cx| {
599                     run_lints!(cx, check_fn, fk, decl, body, span, id);
600                     cx.visit_ids(|v| {
601                         v.visit_fn(fk, decl, body, span, id);
602                     });
603                     visit::walk_fn(cx, fk, decl, body, span);
604                 })
605             },
606             _ => {
607                 run_lints!(self, check_fn, fk, decl, body, span, id);
608                 visit::walk_fn(self, fk, decl, body, span);
609             }
610         }
611     }
612
613     fn visit_ty_method(&mut self, t: &ast::TypeMethod) {
614         self.with_lint_attrs(&t.attrs[], |cx| {
615             run_lints!(cx, check_ty_method, t);
616             visit::walk_ty_method(cx, t);
617         })
618     }
619
620     fn visit_struct_def(&mut self,
621                         s: &ast::StructDef,
622                         ident: ast::Ident,
623                         g: &ast::Generics,
624                         id: ast::NodeId) {
625         run_lints!(self, check_struct_def, s, ident, g, id);
626         visit::walk_struct_def(self, s);
627         run_lints!(self, check_struct_def_post, s, ident, g, id);
628     }
629
630     fn visit_struct_field(&mut self, s: &ast::StructField) {
631         self.with_lint_attrs(&s.node.attrs[], |cx| {
632             run_lints!(cx, check_struct_field, s);
633             visit::walk_struct_field(cx, s);
634         })
635     }
636
637     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) {
638         self.with_lint_attrs(&v.node.attrs[], |cx| {
639             run_lints!(cx, check_variant, v, g);
640             visit::walk_variant(cx, v, g);
641             run_lints!(cx, check_variant_post, v, g);
642         })
643     }
644
645     // FIXME(#10894) should continue recursing
646     fn visit_ty(&mut self, t: &ast::Ty) {
647         run_lints!(self, check_ty, t);
648     }
649
650     fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
651         run_lints!(self, check_ident, sp, id);
652     }
653
654     fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
655         run_lints!(self, check_mod, m, s, n);
656         visit::walk_mod(self, m);
657     }
658
659     fn visit_local(&mut self, l: &ast::Local) {
660         run_lints!(self, check_local, l);
661         visit::walk_local(self, l);
662     }
663
664     fn visit_block(&mut self, b: &ast::Block) {
665         run_lints!(self, check_block, b);
666         visit::walk_block(self, b);
667     }
668
669     fn visit_arm(&mut self, a: &ast::Arm) {
670         run_lints!(self, check_arm, a);
671         visit::walk_arm(self, a);
672     }
673
674     fn visit_decl(&mut self, d: &ast::Decl) {
675         run_lints!(self, check_decl, d);
676         visit::walk_decl(self, d);
677     }
678
679     fn visit_expr_post(&mut self, e: &ast::Expr) {
680         run_lints!(self, check_expr_post, e);
681     }
682
683     fn visit_generics(&mut self, g: &ast::Generics) {
684         run_lints!(self, check_generics, g);
685         visit::walk_generics(self, g);
686     }
687
688     fn visit_trait_item(&mut self, m: &ast::TraitItem) {
689         run_lints!(self, check_trait_method, m);
690         visit::walk_trait_item(self, m);
691     }
692
693     fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>) {
694         run_lints!(self, check_opt_lifetime_ref, sp, lt);
695     }
696
697     fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime) {
698         run_lints!(self, check_lifetime_ref, lt);
699     }
700
701     fn visit_lifetime_def(&mut self, lt: &ast::LifetimeDef) {
702         run_lints!(self, check_lifetime_def, lt);
703     }
704
705     fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
706         run_lints!(self, check_explicit_self, es);
707         visit::walk_explicit_self(self, es);
708     }
709
710     fn visit_mac(&mut self, mac: &ast::Mac) {
711         run_lints!(self, check_mac, mac);
712         visit::walk_mac(self, mac);
713     }
714
715     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
716         run_lints!(self, check_path, p, id);
717         visit::walk_path(self, p);
718     }
719
720     fn visit_attribute(&mut self, attr: &ast::Attribute) {
721         run_lints!(self, check_attribute, attr);
722     }
723 }
724
725 // Output any lints that were previously added to the session.
726 impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> {
727     fn visit_id(&mut self, id: ast::NodeId) {
728         match self.tcx.sess.lints.borrow_mut().remove(&id) {
729             None => {}
730             Some(lints) => {
731                 for (lint_id, span, msg) in lints {
732                     self.span_lint(lint_id.lint, span, &msg[..])
733                 }
734             }
735         }
736     }
737 }
738
739 // This lint pass is defined here because it touches parts of the `Context`
740 // that we don't want to expose. It records the lint level at certain AST
741 // nodes, so that the variant size difference check in trans can call
742 // `raw_emit_lint`.
743
744 struct GatherNodeLevels;
745
746 impl LintPass for GatherNodeLevels {
747     fn get_lints(&self) -> LintArray {
748         lint_array!()
749     }
750
751     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
752         match it.node {
753             ast::ItemEnum(..) => {
754                 let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES);
755                 let lvlsrc = cx.lints.get_level_source(lint_id);
756                 match lvlsrc {
757                     (lvl, _) if lvl != Allow => {
758                         cx.node_levels.borrow_mut()
759                             .insert((it.id, lint_id), lvlsrc);
760                     },
761                     _ => { }
762                 }
763             },
764             _ => { }
765         }
766     }
767 }
768
769 /// Perform lint checking on a crate.
770 ///
771 /// Consumes the `lint_store` field of the `Session`.
772 pub fn check_crate(tcx: &ty::ctxt,
773                    exported_items: &ExportedItems) {
774
775     // If this is a feature-staged build of rustc then flip several lints to 'forbid'
776     tcx.sess.lint_store.borrow_mut().maybe_stage_features(&tcx.sess);
777
778     let krate = tcx.map.krate();
779     let mut cx = Context::new(tcx, krate, exported_items);
780
781     // Visit the whole crate.
782     cx.with_lint_attrs(&krate.attrs[], |cx| {
783         cx.visit_id(ast::CRATE_NODE_ID);
784         cx.visit_ids(|v| {
785             v.visited_outermost = true;
786             visit::walk_crate(v, krate);
787         });
788
789         // since the root module isn't visited as an item (because it isn't an
790         // item), warn for it here.
791         run_lints!(cx, check_crate, krate);
792
793         visit::walk_crate(cx, krate);
794     });
795
796     // If we missed any lints added to the session, then there's a bug somewhere
797     // in the iteration code.
798     for (id, v) in &*tcx.sess.lints.borrow() {
799         for &(lint, span, ref msg) in v {
800             tcx.sess.span_bug(span,
801                               &format!("unprocessed lint {} at {}: {}",
802                                        lint.as_str(), tcx.map.node_to_string(*id), *msg))
803         }
804     }
805
806     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
807 }