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