]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/context.rs
complete openbsd support for `std::env`
[rust.git] / src / librustc / lint / context.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Implementation of lint checking.
12 //!
13 //! The lint checking is mostly consolidated into one pass which runs just
14 //! before translation to LLVM bytecode. Throughout compilation, lint warnings
15 //! can be added via the `add_lint` method on the Session structure. This
16 //! requires a span and an id of the node that the lint is being added to. The
17 //! lint isn't actually emitted at that time because it is unknown what the
18 //! actual lint level at that location is.
19 //!
20 //! To actually emit lint warnings/errors, a separate pass is used just before
21 //! translation. A context keeps track of the current state of all lint levels.
22 //! Upon entering a node of the ast which can modify the lint settings, the
23 //! previous lint state is pushed onto a stack and the ast is then recursed
24 //! upon.  As the ast is traversed, this keeps track of the current lint level
25 //! for all lint attributes.
26 use self::TargetLint::*;
27
28 use middle::privacy::ExportedItems;
29 use middle::ty::{self, Ty};
30 use session::{early_error, Session};
31 use session::config::UnstableFeatures;
32 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject};
33 use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid, ReleaseChannel};
34 use lint::builtin;
35 use util::nodemap::FnvHashMap;
36
37 use std::cell::RefCell;
38 use std::mem;
39 use syntax::ast_util::IdVisitingOperation;
40 use syntax::attr::AttrMetaMethods;
41 use syntax::attr;
42 use syntax::codemap::Span;
43 use syntax::visit::{Visitor, FnKind};
44 use syntax::parse::token::InternedString;
45 use syntax::{ast, ast_util, visit};
46
47 /// Information about the registered lints.
48 ///
49 /// This is basically the subset of `Context` that we can
50 /// build early in the compile pipeline.
51 pub struct LintStore {
52     /// Registered lints. The bool is true if the lint was
53     /// added by a plugin.
54     lints: Vec<(&'static Lint, bool)>,
55
56     /// Trait objects for each lint pass.
57     /// This is only `None` while iterating over the objects. See the definition
58     /// of run_lints.
59     passes: Option<Vec<LintPassObject>>,
60
61     /// Lints indexed by name.
62     by_name: FnvHashMap<String, TargetLint>,
63
64     /// Current levels of each lint, and where they were set.
65     levels: FnvHashMap<LintId, LevelSource>,
66
67     /// Map of registered lint groups to what lints they expand to. The bool
68     /// is true if the lint group was added by a plugin.
69     lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>,
70 }
71
72 /// The targed of the `by_name` map, which accounts for renaming/deprecation.
73 enum TargetLint {
74     /// A direct lint target
75     Id(LintId),
76
77     /// Temporary renaming, used for easing migration pain; see #16545
78     Renamed(String, LintId),
79 }
80
81 impl LintStore {
82     fn get_level_source(&self, lint: LintId) -> LevelSource {
83         match self.levels.get(&lint) {
84             Some(&s) => s,
85             None => (Allow, Default),
86         }
87     }
88
89     fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
90         if lvlsrc.0 == Allow {
91             self.levels.remove(&lint);
92         } else {
93             self.levels.insert(lint, lvlsrc);
94         }
95     }
96
97     pub fn new() -> LintStore {
98         LintStore {
99             lints: vec!(),
100             passes: Some(vec!()),
101             by_name: FnvHashMap(),
102             levels: FnvHashMap(),
103             lint_groups: FnvHashMap(),
104         }
105     }
106
107     pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
108         &self.lints[]
109     }
110
111     pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
112         self.lint_groups.iter().map(|(k, v)| (*k,
113                                               v.0.clone(),
114                                               v.1)).collect()
115     }
116
117     pub fn register_pass(&mut self, sess: Option<&Session>,
118                          from_plugin: bool, pass: LintPassObject) {
119         for &lint in pass.get_lints() {
120             self.lints.push((*lint, from_plugin));
121
122             let id = LintId::of(*lint);
123             if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
124                 let msg = format!("duplicate specification of lint {}", lint.name_lower());
125                 match (sess, from_plugin) {
126                     // We load builtin lints first, so a duplicate is a compiler bug.
127                     // Use early_error when handling -W help with no crate.
128                     (None, _) => early_error(&msg[]),
129                     (Some(sess), false) => sess.bug(&msg[]),
130
131                     // A duplicate name from a plugin is a user error.
132                     (Some(sess), true)  => sess.err(&msg[]),
133                 }
134             }
135
136             if lint.default_level != Allow {
137                 self.levels.insert(id, (lint.default_level, Default));
138             }
139         }
140         self.passes.as_mut().unwrap().push(pass);
141     }
142
143     pub fn register_group(&mut self, sess: Option<&Session>,
144                           from_plugin: bool, name: &'static str,
145                           to: Vec<LintId>) {
146         let new = self.lint_groups.insert(name, (to, from_plugin)).is_none();
147
148         if !new {
149             let msg = format!("duplicate specification of lint group {}", name);
150             match (sess, from_plugin) {
151                 // We load builtin lints first, so a duplicate is a compiler bug.
152                 // Use early_error when handling -W help with no crate.
153                 (None, _) => early_error(&msg[]),
154                 (Some(sess), false) => sess.bug(&msg[]),
155
156                 // A duplicate name from a plugin is a user error.
157                 (Some(sess), true)  => sess.err(&msg[]),
158             }
159         }
160     }
161
162     fn register_renamed(&mut self, old_name: &str, new_name: &str) {
163         let target = match self.by_name.get(new_name) {
164             Some(&Id(lint_id)) => lint_id.clone(),
165             _ => panic!("invalid lint renaming of {} to {}", old_name, new_name)
166         };
167         self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
168     }
169
170     pub fn register_builtin(&mut self, sess: Option<&Session>) {
171         macro_rules! add_builtin {
172             ($sess:ident, $($name:ident),*,) => (
173                 {$(
174                     self.register_pass($sess, false, box builtin::$name as LintPassObject);
175                 )*}
176             )
177         }
178
179         macro_rules! add_builtin_with_new {
180             ($sess:ident, $($name:ident),*,) => (
181                 {$(
182                     self.register_pass($sess, false, box builtin::$name::new() as LintPassObject);
183                 )*}
184             )
185         }
186
187         macro_rules! add_lint_group {
188             ($sess:ident, $name:expr, $($lint:ident),*) => (
189                 self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]);
190             )
191         }
192
193         add_builtin!(sess,
194                      HardwiredLints,
195                      WhileTrue,
196                      UnusedCasts,
197                      ImproperCTypes,
198                      BoxPointers,
199                      UnusedAttributes,
200                      PathStatements,
201                      UnusedResults,
202                      NonCamelCaseTypes,
203                      NonSnakeCase,
204                      NonUpperCaseGlobals,
205                      UnusedParens,
206                      UnusedImportBraces,
207                      NonShorthandFieldPatterns,
208                      UnusedUnsafe,
209                      UnsafeBlocks,
210                      UnusedMut,
211                      UnusedAllocation,
212                      MissingCopyImplementations,
213                      UnstableFeatures,
214                      Stability,
215                      UnconditionalRecursion,
216                      PrivateNoMangleFns,
217         );
218
219         add_builtin_with_new!(sess,
220                               TypeLimits,
221                               RawPointerDerive,
222                               MissingDoc,
223                               MissingDebugImplementations,
224         );
225
226         add_lint_group!(sess, "bad_style",
227                         NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS);
228
229         add_lint_group!(sess, "unused",
230                         UNUSED_IMPORTS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE,
231                         UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE,
232                         UNUSED_UNSAFE, PATH_STATEMENTS);
233
234         // We have one lint pass defined in this module.
235         self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
236
237         // Insert temporary renamings for a one-time deprecation
238         self.register_renamed("raw_pointer_deriving", "raw_pointer_derive");
239
240         self.register_renamed("unknown_features", "unused_features");
241     }
242
243     #[allow(unused_variables)]
244     fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>)
245                  -> Option<LintId>
246     {
247         match self.by_name.get(lint_name) {
248             Some(&Id(lint_id)) => Some(lint_id),
249             Some(&Renamed(ref new_name, lint_id)) => {
250                 let warning = format!("lint {} has been renamed to {}",
251                                       lint_name, new_name);
252                 match span {
253                     Some(span) => sess.span_warn(span, &warning[]),
254                     None => sess.warn(&warning[]),
255                 };
256                 Some(lint_id)
257             }
258             None => None
259         }
260     }
261
262     pub fn process_command_line(&mut self, sess: &Session) {
263         for &(ref lint_name, level) in &sess.opts.lint_opts {
264             match self.find_lint(&lint_name[], sess, None) {
265                 Some(lint_id) => self.set_level(lint_id, (level, CommandLine)),
266                 None => {
267                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
268                                                  .collect::<FnvHashMap<&'static str,
269                                                                        Vec<LintId>>>()
270                                                  .get(&lint_name[]) {
271                         Some(v) => {
272                             v.iter()
273                              .map(|lint_id: &LintId|
274                                      self.set_level(*lint_id, (level, CommandLine)))
275                              .collect::<Vec<()>>();
276                         }
277                         None => sess.err(&format!("unknown {} flag: {}",
278                                                  level.as_str(), lint_name)[]),
279                     }
280                 }
281             }
282         }
283     }
284
285     fn maybe_stage_features(&mut self, sess: &Session) {
286         let lvl = match sess.opts.unstable_features {
287             UnstableFeatures::Default => return,
288             UnstableFeatures::Disallow => Warn,
289             UnstableFeatures::Cheat => Allow
290         };
291         match self.by_name.get("unstable_features") {
292             Some(&Id(lint_id)) => if self.get_level_source(lint_id).0 != Forbid {
293                 self.set_level(lint_id, (lvl, ReleaseChannel))
294             },
295             Some(&Renamed(_, lint_id)) => if self.get_level_source(lint_id).0 != Forbid {
296                 self.set_level(lint_id, (lvl, ReleaseChannel))
297             },
298             None => unreachable!()
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 &mut passes {
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 {
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 {
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 def = None;
384     let mut note = None;
385     let msg = match source {
386         Default => {
387             format!("{}, #[{}({})] on by default", msg,
388                     level.as_str(), name)
389         },
390         CommandLine => {
391             format!("{} [-{} {}]", msg,
392                     match level {
393                         Warn => 'W', Deny => 'D', Forbid => 'F',
394                         Allow => panic!()
395                     }, name.replace("_", "-"))
396         },
397         Node(src) => {
398             def = Some(src);
399             msg.to_string()
400         }
401         ReleaseChannel => {
402             let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
403             note = Some(format!("this feature may not be used in the {} release channel",
404                                 release_channel));
405             msg.to_string()
406         }
407     };
408
409     // For purposes of printing, we can treat forbid as deny.
410     if level == Forbid { level = Deny; }
411
412     match (level, span) {
413         (Warn, Some(sp)) => sess.span_warn(sp, &msg[]),
414         (Warn, None)     => sess.warn(&msg[]),
415         (Deny, Some(sp)) => sess.span_err(sp, &msg[]),
416         (Deny, None)     => sess.err(&msg[]),
417         _ => sess.bug("impossible level in raw_emit_lint"),
418     }
419
420     if let Some(note) = note {
421         sess.note(&note[]);
422     }
423
424     if let Some(span) = def {
425         sess.span_note(span, "lint level defined here");
426     }
427 }
428
429 impl<'a, 'tcx> Context<'a, 'tcx> {
430     fn new(tcx: &'a ty::ctxt<'tcx>,
431            krate: &'a ast::Crate,
432            exported_items: &'a ExportedItems) -> Context<'a, 'tcx> {
433         // We want to own the lint store, so move it out of the session.
434         let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
435                                       LintStore::new());
436
437         Context {
438             tcx: tcx,
439             krate: krate,
440             exported_items: exported_items,
441             lints: lint_store,
442             level_stack: vec![],
443             node_levels: RefCell::new(FnvHashMap()),
444         }
445     }
446
447     /// Get the overall compiler `Session` object.
448     pub fn sess(&'a self) -> &'a Session {
449         &self.tcx.sess
450     }
451
452     /// Get the level of `lint` at the current position of the lint
453     /// traversal.
454     pub fn current_level(&self, lint: &'static Lint) -> Level {
455         self.lints.levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
456     }
457
458     fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
459         let (level, src) = match self.lints.levels.get(&LintId::of(lint)) {
460             None => return,
461             Some(&(Warn, src)) => {
462                 let lint_id = LintId::of(builtin::WARNINGS);
463                 (self.lints.get_level_source(lint_id).0, src)
464             }
465             Some(&pair) => pair,
466         };
467
468         raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
469     }
470
471     /// Emit a lint at the appropriate level, with no associated span.
472     pub fn lint(&self, lint: &'static Lint, msg: &str) {
473         self.lookup_and_emit(lint, None, msg);
474     }
475
476     /// Emit a lint at the appropriate level, for a particular span.
477     pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
478         self.lookup_and_emit(lint, Some(span), msg);
479     }
480
481     /// Merge the lints specified by any lint attributes into the
482     /// current lint context, call the provided function, then reset the
483     /// lints in effect to their previous state.
484     fn with_lint_attrs<F>(&mut self,
485                           attrs: &[ast::Attribute],
486                           f: F) where
487         F: FnOnce(&mut Context),
488     {
489         // Parse all of the lint attributes, and then add them all to the
490         // current dictionary of lint information. Along the way, keep a history
491         // of what we changed so we can roll everything back after invoking the
492         // specified closure
493         let mut pushed = 0;
494
495         for result in gather_attrs(attrs) {
496             let v = match result {
497                 Err(span) => {
498                     self.tcx.sess.span_err(span, "malformed lint attribute");
499                     continue;
500                 }
501                 Ok((lint_name, level, span)) => {
502                     match self.lints.find_lint(lint_name.get(), &self.tcx.sess, Some(span)) {
503                         Some(lint_id) => vec![(lint_id, level, span)],
504                         None => {
505                             match self.lints.lint_groups.get(lint_name.get()) {
506                                 Some(&(ref v, _)) => v.iter()
507                                                       .map(|lint_id: &LintId|
508                                                            (*lint_id, level, span))
509                                                       .collect(),
510                                 None => {
511                                     self.span_lint(builtin::UNKNOWN_LINTS, span,
512                                                format!("unknown `{}` attribute: `{}`",
513                                                        level.as_str(), lint_name).as_slice());
514                                     continue;
515                                 }
516                             }
517                         }
518                     }
519                 }
520             };
521
522             for (lint_id, level, span) in v {
523                 let now = self.lints.get_level_source(lint_id).0;
524                 if now == Forbid && level != Forbid {
525                     let lint_name = lint_id.as_str();
526                     self.tcx.sess.span_err(span,
527                                            &format!("{}({}) overruled by outer forbid({})",
528                                                    level.as_str(), lint_name,
529                                                    lint_name)[]);
530                 } else if now != level {
531                     let src = self.lints.get_level_source(lint_id).1;
532                     self.level_stack.push((lint_id, (now, src)));
533                     pushed += 1;
534                     self.lints.set_level(lint_id, (level, Node(span)));
535                 }
536             }
537         }
538
539         run_lints!(self, enter_lint_attrs, attrs);
540         f(self);
541         run_lints!(self, exit_lint_attrs, attrs);
542
543         // rollback
544         for _ in 0..pushed {
545             let (lint, lvlsrc) = self.level_stack.pop().unwrap();
546             self.lints.set_level(lint, lvlsrc);
547         }
548     }
549
550     fn visit_ids<F>(&mut self, f: F) where
551         F: FnOnce(&mut ast_util::IdVisitor<Context>)
552     {
553         let mut v = ast_util::IdVisitor {
554             operation: self,
555             pass_through_items: false,
556             visited_outermost: false,
557         };
558         f(&mut v);
559     }
560 }
561
562 impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
563     fn visit_item(&mut self, it: &ast::Item) {
564         self.with_lint_attrs(&it.attrs[], |cx| {
565             run_lints!(cx, check_item, it);
566             cx.visit_ids(|v| v.visit_item(it));
567             visit::walk_item(cx, it);
568         })
569     }
570
571     fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
572         self.with_lint_attrs(&it.attrs[], |cx| {
573             run_lints!(cx, check_foreign_item, it);
574             visit::walk_foreign_item(cx, it);
575         })
576     }
577
578     fn visit_pat(&mut self, p: &ast::Pat) {
579         run_lints!(self, check_pat, p);
580         visit::walk_pat(self, p);
581     }
582
583     fn visit_expr(&mut self, e: &ast::Expr) {
584         run_lints!(self, check_expr, e);
585         visit::walk_expr(self, e);
586     }
587
588     fn visit_stmt(&mut self, s: &ast::Stmt) {
589         run_lints!(self, check_stmt, s);
590         visit::walk_stmt(self, s);
591     }
592
593     fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v ast::FnDecl,
594                 body: &'v ast::Block, span: Span, id: ast::NodeId) {
595         match fk {
596             visit::FkMethod(_, _, m) => {
597                 self.with_lint_attrs(&m.attrs[], |cx| {
598                     run_lints!(cx, check_fn, fk, decl, body, span, id);
599                     cx.visit_ids(|v| {
600                         v.visit_fn(fk, decl, body, span, id);
601                     });
602                     visit::walk_fn(cx, fk, decl, body, span);
603                 })
604             },
605             _ => {
606                 run_lints!(self, check_fn, fk, decl, body, span, id);
607                 visit::walk_fn(self, fk, decl, body, span);
608             }
609         }
610     }
611
612     fn visit_ty_method(&mut self, t: &ast::TypeMethod) {
613         self.with_lint_attrs(&t.attrs[], |cx| {
614             run_lints!(cx, check_ty_method, t);
615             visit::walk_ty_method(cx, t);
616         })
617     }
618
619     fn visit_struct_def(&mut self,
620                         s: &ast::StructDef,
621                         ident: ast::Ident,
622                         g: &ast::Generics,
623                         id: ast::NodeId) {
624         run_lints!(self, check_struct_def, s, ident, g, id);
625         visit::walk_struct_def(self, s);
626         run_lints!(self, check_struct_def_post, s, ident, g, id);
627     }
628
629     fn visit_struct_field(&mut self, s: &ast::StructField) {
630         self.with_lint_attrs(&s.node.attrs[], |cx| {
631             run_lints!(cx, check_struct_field, s);
632             visit::walk_struct_field(cx, s);
633         })
634     }
635
636     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) {
637         self.with_lint_attrs(&v.node.attrs[], |cx| {
638             run_lints!(cx, check_variant, v, g);
639             visit::walk_variant(cx, v, g);
640             run_lints!(cx, check_variant_post, v, g);
641         })
642     }
643
644     // FIXME(#10894) should continue recursing
645     fn visit_ty(&mut self, t: &ast::Ty) {
646         run_lints!(self, check_ty, t);
647     }
648
649     fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
650         run_lints!(self, check_ident, sp, id);
651     }
652
653     fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
654         run_lints!(self, check_mod, m, s, n);
655         visit::walk_mod(self, m);
656     }
657
658     fn visit_local(&mut self, l: &ast::Local) {
659         run_lints!(self, check_local, l);
660         visit::walk_local(self, l);
661     }
662
663     fn visit_block(&mut self, b: &ast::Block) {
664         run_lints!(self, check_block, b);
665         visit::walk_block(self, b);
666     }
667
668     fn visit_arm(&mut self, a: &ast::Arm) {
669         run_lints!(self, check_arm, a);
670         visit::walk_arm(self, a);
671     }
672
673     fn visit_decl(&mut self, d: &ast::Decl) {
674         run_lints!(self, check_decl, d);
675         visit::walk_decl(self, d);
676     }
677
678     fn visit_expr_post(&mut self, e: &ast::Expr) {
679         run_lints!(self, check_expr_post, e);
680     }
681
682     fn visit_generics(&mut self, g: &ast::Generics) {
683         run_lints!(self, check_generics, g);
684         visit::walk_generics(self, g);
685     }
686
687     fn visit_trait_item(&mut self, m: &ast::TraitItem) {
688         run_lints!(self, check_trait_method, m);
689         visit::walk_trait_item(self, m);
690     }
691
692     fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>) {
693         run_lints!(self, check_opt_lifetime_ref, sp, lt);
694     }
695
696     fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime) {
697         run_lints!(self, check_lifetime_ref, lt);
698     }
699
700     fn visit_lifetime_def(&mut self, lt: &ast::LifetimeDef) {
701         run_lints!(self, check_lifetime_def, lt);
702     }
703
704     fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
705         run_lints!(self, check_explicit_self, es);
706         visit::walk_explicit_self(self, es);
707     }
708
709     fn visit_mac(&mut self, mac: &ast::Mac) {
710         run_lints!(self, check_mac, mac);
711         visit::walk_mac(self, mac);
712     }
713
714     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
715         run_lints!(self, check_path, p, id);
716         visit::walk_path(self, p);
717     }
718
719     fn visit_attribute(&mut self, attr: &ast::Attribute) {
720         run_lints!(self, check_attribute, attr);
721     }
722 }
723
724 // Output any lints that were previously added to the session.
725 impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> {
726     fn visit_id(&mut self, id: ast::NodeId) {
727         match self.tcx.sess.lints.borrow_mut().remove(&id) {
728             None => {}
729             Some(lints) => {
730                 for (lint_id, span, msg) in lints {
731                     self.span_lint(lint_id.lint, span, &msg[])
732                 }
733             }
734         }
735     }
736 }
737
738 // This lint pass is defined here because it touches parts of the `Context`
739 // that we don't want to expose. It records the lint level at certain AST
740 // nodes, so that the variant size difference check in trans can call
741 // `raw_emit_lint`.
742
743 struct GatherNodeLevels;
744
745 impl LintPass for GatherNodeLevels {
746     fn get_lints(&self) -> LintArray {
747         lint_array!()
748     }
749
750     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
751         match it.node {
752             ast::ItemEnum(..) => {
753                 let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES);
754                 let lvlsrc = cx.lints.get_level_source(lint_id);
755                 match lvlsrc {
756                     (lvl, _) if lvl != Allow => {
757                         cx.node_levels.borrow_mut()
758                             .insert((it.id, lint_id), lvlsrc);
759                     },
760                     _ => { }
761                 }
762             },
763             _ => { }
764         }
765     }
766 }
767
768 /// Perform lint checking on a crate.
769 ///
770 /// Consumes the `lint_store` field of the `Session`.
771 pub fn check_crate(tcx: &ty::ctxt,
772                    exported_items: &ExportedItems) {
773
774     // If this is a feature-staged build of rustc then flip several lints to 'forbid'
775     tcx.sess.lint_store.borrow_mut().maybe_stage_features(&tcx.sess);
776
777     let krate = tcx.map.krate();
778     let mut cx = Context::new(tcx, krate, exported_items);
779
780     // Visit the whole crate.
781     cx.with_lint_attrs(&krate.attrs[], |cx| {
782         cx.visit_id(ast::CRATE_NODE_ID);
783         cx.visit_ids(|v| {
784             v.visited_outermost = true;
785             visit::walk_crate(v, krate);
786         });
787
788         // since the root module isn't visited as an item (because it isn't an
789         // item), warn for it here.
790         run_lints!(cx, check_crate, krate);
791
792         visit::walk_crate(cx, krate);
793     });
794
795     // If we missed any lints added to the session, then there's a bug somewhere
796     // in the iteration code.
797     for (id, v) in &*tcx.sess.lints.borrow() {
798         for &(lint, span, ref msg) in v {
799             tcx.sess.span_bug(span,
800                               format!("unprocessed lint {} at {}: {}",
801                                       lint.as_str(), tcx.map.node_to_string(*id), *msg).as_slice())
802         }
803     }
804
805     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
806 }