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