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