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