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