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