]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/context.rs
08fba2dc56feefdbc5009d1234a9e95d7fa68265
[rust.git] / src / librustc / lint / context.rs
1 // Copyright 2012-2015 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 dep_graph::DepNode;
29 use middle::privacy::AccessLevels;
30 use middle::ty;
31 use session::{early_error, Session};
32 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
33 use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
34 use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
35 use lint::builtin;
36 use util::nodemap::FnvHashMap;
37
38 use std::cell::RefCell;
39 use std::cmp;
40 use std::mem;
41 use syntax::ast_util::{self, IdVisitingOperation};
42 use syntax::attr::{self, AttrMetaMethods};
43 use syntax::codemap::Span;
44 use syntax::errors::{self, DiagnosticBuilder};
45 use syntax::parse::token::InternedString;
46 use syntax::ast;
47 use syntax::attr::ThinAttributesExt;
48 use rustc_front::hir;
49 use rustc_front::util;
50 use rustc_front::intravisit as hir_visit;
51 use syntax::visit as ast_visit;
52
53 /// Information about the registered lints.
54 ///
55 /// This is basically the subset of `Context` that we can
56 /// build early in the compile pipeline.
57 pub struct LintStore {
58     /// Registered lints. The bool is true if the lint was
59     /// added by a plugin.
60     lints: Vec<(&'static Lint, bool)>,
61
62     /// Trait objects for each lint pass.
63     /// This is only `None` while iterating over the objects. See the definition
64     /// of run_lints.
65     early_passes: Option<Vec<EarlyLintPassObject>>,
66     late_passes: Option<Vec<LateLintPassObject>>,
67
68     /// Lints indexed by name.
69     by_name: FnvHashMap<String, TargetLint>,
70
71     /// Current levels of each lint, and where they were set.
72     levels: FnvHashMap<LintId, LevelSource>,
73
74     /// Map of registered lint groups to what lints they expand to. The bool
75     /// is true if the lint group was added by a plugin.
76     lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>,
77
78     /// Maximum level a lint can be
79     lint_cap: Option<Level>,
80 }
81
82 /// The targed of the `by_name` map, which accounts for renaming/deprecation.
83 enum TargetLint {
84     /// A direct lint target
85     Id(LintId),
86
87     /// Temporary renaming, used for easing migration pain; see #16545
88     Renamed(String, LintId),
89
90     /// Lint with this name existed previously, but has been removed/deprecated.
91     /// The string argument is the reason for removal.
92     Removed(String),
93 }
94
95 enum FindLintError {
96     NotFound,
97     Removed
98 }
99
100 impl LintStore {
101     fn get_level_source(&self, lint: LintId) -> LevelSource {
102         match self.levels.get(&lint) {
103             Some(&s) => s,
104             None => (Allow, Default),
105         }
106     }
107
108     fn set_level(&mut self, lint: LintId, mut lvlsrc: LevelSource) {
109         if let Some(cap) = self.lint_cap {
110             lvlsrc.0 = cmp::min(lvlsrc.0, cap);
111         }
112         if lvlsrc.0 == Allow {
113             self.levels.remove(&lint);
114         } else {
115             self.levels.insert(lint, lvlsrc);
116         }
117     }
118
119     pub fn new() -> LintStore {
120         LintStore {
121             lints: vec!(),
122             early_passes: Some(vec!()),
123             late_passes: Some(vec!()),
124             by_name: FnvHashMap(),
125             levels: FnvHashMap(),
126             lint_groups: FnvHashMap(),
127             lint_cap: None,
128         }
129     }
130
131     pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
132         &self.lints
133     }
134
135     pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
136         self.lint_groups.iter().map(|(k, v)| (*k,
137                                               v.0.clone(),
138                                               v.1)).collect()
139     }
140
141     pub fn register_early_pass(&mut self,
142                                sess: Option<&Session>,
143                                from_plugin: bool,
144                                pass: EarlyLintPassObject) {
145         self.push_pass(sess, from_plugin, &pass);
146         self.early_passes.as_mut().unwrap().push(pass);
147     }
148
149     pub fn register_late_pass(&mut self,
150                               sess: Option<&Session>,
151                               from_plugin: bool,
152                               pass: LateLintPassObject) {
153         self.push_pass(sess, from_plugin, &pass);
154         self.late_passes.as_mut().unwrap().push(pass);
155     }
156
157     // Helper method for register_early/late_pass
158     fn push_pass<P: LintPass + ?Sized + 'static>(&mut self,
159                                         sess: Option<&Session>,
160                                         from_plugin: bool,
161                                         pass: &Box<P>) {
162         for &lint in pass.get_lints() {
163             self.lints.push((*lint, from_plugin));
164
165             let id = LintId::of(*lint);
166             if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
167                 let msg = format!("duplicate specification of lint {}", lint.name_lower());
168                 match (sess, from_plugin) {
169                     // We load builtin lints first, so a duplicate is a compiler bug.
170                     // Use early_error when handling -W help with no crate.
171                     (None, _) => early_error(errors::ColorConfig::Auto, &msg[..]),
172                     (Some(sess), false) => sess.bug(&msg[..]),
173
174                     // A duplicate name from a plugin is a user error.
175                     (Some(sess), true)  => sess.err(&msg[..]),
176                 }
177             }
178
179             if lint.default_level != Allow {
180                 self.levels.insert(id, (lint.default_level, Default));
181             }
182         }
183     }
184
185     pub fn register_group(&mut self, sess: Option<&Session>,
186                           from_plugin: bool, name: &'static str,
187                           to: Vec<LintId>) {
188         let new = self.lint_groups.insert(name, (to, from_plugin)).is_none();
189
190         if !new {
191             let msg = format!("duplicate specification of lint group {}", name);
192             match (sess, from_plugin) {
193                 // We load builtin lints first, so a duplicate is a compiler bug.
194                 // Use early_error when handling -W help with no crate.
195                 (None, _) => early_error(errors::ColorConfig::Auto, &msg[..]),
196                 (Some(sess), false) => sess.bug(&msg[..]),
197
198                 // A duplicate name from a plugin is a user error.
199                 (Some(sess), true)  => sess.err(&msg[..]),
200             }
201         }
202     }
203
204     pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
205         let target = match self.by_name.get(new_name) {
206             Some(&Id(lint_id)) => lint_id.clone(),
207             _ => panic!("invalid lint renaming of {} to {}", old_name, new_name)
208         };
209         self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
210     }
211
212     pub fn register_removed(&mut self, name: &str, reason: &str) {
213         self.by_name.insert(name.into(), Removed(reason.into()));
214     }
215
216     #[allow(unused_variables)]
217     fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>)
218                  -> Result<LintId, FindLintError>
219     {
220         match self.by_name.get(lint_name) {
221             Some(&Id(lint_id)) => Ok(lint_id),
222             Some(&Renamed(ref new_name, lint_id)) => {
223                 let warning = format!("lint {} has been renamed to {}",
224                                       lint_name, new_name);
225                 match span {
226                     Some(span) => sess.span_warn(span, &warning[..]),
227                     None => sess.warn(&warning[..]),
228                 };
229                 Ok(lint_id)
230             },
231             Some(&Removed(ref reason)) => {
232                 let warning = format!("lint {} has been removed: {}", lint_name, reason);
233                 match span {
234                     Some(span) => sess.span_warn(span, &warning[..]),
235                     None => sess.warn(&warning[..])
236                 }
237                 Err(FindLintError::Removed)
238             },
239             None => Err(FindLintError::NotFound)
240         }
241     }
242
243     pub fn process_command_line(&mut self, sess: &Session) {
244         for &(ref lint_name, level) in &sess.opts.lint_opts {
245             match self.find_lint(&lint_name[..], sess, None) {
246                 Ok(lint_id) => self.set_level(lint_id, (level, CommandLine)),
247                 Err(_) => {
248                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
249                                                  .collect::<FnvHashMap<&'static str,
250                                                                        Vec<LintId>>>()
251                                                  .get(&lint_name[..]) {
252                         Some(v) => {
253                             v.iter()
254                              .map(|lint_id: &LintId|
255                                      self.set_level(*lint_id, (level, CommandLine)))
256                              .collect::<Vec<()>>();
257                         }
258                         None => sess.err(&format!("unknown {} flag: {}",
259                                                  level.as_str(), lint_name)),
260                     }
261                 }
262             }
263         }
264
265         self.lint_cap = sess.opts.lint_cap;
266         if let Some(cap) = self.lint_cap {
267             for level in self.levels.iter_mut().map(|p| &mut (p.1).0) {
268                 *level = cmp::min(*level, cap);
269             }
270         }
271     }
272 }
273
274 /// Context for lint checking after type checking.
275 pub struct LateContext<'a, 'tcx: 'a> {
276     /// Type context we're checking in.
277     pub tcx: &'a ty::ctxt<'tcx>,
278
279     /// The crate being checked.
280     pub krate: &'a hir::Crate,
281
282     /// Items accessible from the crate being checked.
283     pub access_levels: &'a AccessLevels,
284
285     /// The store of registered lints.
286     lints: LintStore,
287
288     /// When recursing into an attributed node of the ast which modifies lint
289     /// levels, this stack keeps track of the previous lint levels of whatever
290     /// was modified.
291     level_stack: Vec<(LintId, LevelSource)>,
292
293     /// Level of lints for certain NodeIds, stored here because the body of
294     /// the lint needs to run in trans.
295     node_levels: RefCell<FnvHashMap<(ast::NodeId, LintId), LevelSource>>,
296 }
297
298 /// Context for lint checking of the AST, after expansion, before lowering to
299 /// HIR.
300 pub struct EarlyContext<'a> {
301     /// Type context we're checking in.
302     pub sess: &'a Session,
303
304     /// The crate being checked.
305     pub krate: &'a ast::Crate,
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
316 /// Convenience macro for calling a `LintPass` method on every pass in the context.
317 macro_rules! run_lints { ($cx:expr, $f:ident, $ps:ident, $($args:expr),*) => ({
318     // Move the vector of passes out of `$cx` so that we can
319     // iterate over it mutably while passing `$cx` to the methods.
320     let mut passes = $cx.mut_lints().$ps.take().unwrap();
321     for obj in &mut passes {
322         obj.$f($cx, $($args),*);
323     }
324     $cx.mut_lints().$ps = Some(passes);
325 }) }
326
327 /// Parse the lint attributes into a vector, with `Err`s for malformed lint
328 /// attributes. Writing this as an iterator is an enormous mess.
329 // See also the hir version just below.
330 pub fn gather_attrs(attrs: &[ast::Attribute])
331                     -> Vec<Result<(InternedString, Level, Span), Span>> {
332     let mut out = vec!();
333     for attr in attrs {
334         let level = match Level::from_str(&attr.name()) {
335             None => continue,
336             Some(lvl) => lvl,
337         };
338
339         attr::mark_used(attr);
340
341         let meta = &attr.node.value;
342         let metas = match meta.node {
343             ast::MetaList(_, ref metas) => metas,
344             _ => {
345                 out.push(Err(meta.span));
346                 continue;
347             }
348         };
349
350         for meta in metas {
351             out.push(match meta.node {
352                 ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
353                 _ => Err(meta.span),
354             });
355         }
356     }
357     out
358 }
359
360 /// Emit a lint as a warning or an error (or not at all)
361 /// according to `level`.
362 ///
363 /// This lives outside of `Context` so it can be used by checks
364 /// in trans that run after the main lint pass is finished. Most
365 /// lints elsewhere in the compiler should call
366 /// `Session::add_lint()` instead.
367 pub fn raw_emit_lint(sess: &Session,
368                      lints: &LintStore,
369                      lint: &'static Lint,
370                      lvlsrc: LevelSource,
371                      span: Option<Span>,
372                      msg: &str) {
373     raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit();
374 }
375
376 pub fn raw_struct_lint<'a>(sess: &'a Session,
377                            lints: &LintStore,
378                            lint: &'static Lint,
379                            lvlsrc: LevelSource,
380                            span: Option<Span>,
381                            msg: &str)
382                            -> DiagnosticBuilder<'a> {
383     let (mut level, source) = lvlsrc;
384     if level == Allow {
385         return sess.diagnostic().struct_dummy();
386     }
387
388     let name = lint.name_lower();
389     let mut def = None;
390     let msg = match source {
391         Default => {
392             format!("{}, #[{}({})] on by default", msg,
393                     level.as_str(), name)
394         },
395         CommandLine => {
396             format!("{} [-{} {}]", msg,
397                     match level {
398                         Warn => 'W', Deny => 'D', Forbid => 'F',
399                         Allow => panic!()
400                     }, name.replace("_", "-"))
401         },
402         Node(src) => {
403             def = Some(src);
404             msg.to_string()
405         }
406     };
407
408     // For purposes of printing, we can treat forbid as deny.
409     if level == Forbid { level = Deny; }
410
411     let mut err = match (level, span) {
412         (Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]),
413         (Warn, None)     => sess.struct_warn(&msg[..]),
414         (Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]),
415         (Deny, None)     => sess.struct_err(&msg[..]),
416         _ => sess.bug("impossible level in raw_emit_lint"),
417     };
418
419     // Check for future incompatibility lints and issue a stronger warning.
420     let future_incompat_lints = &lints.lint_groups[builtin::FUTURE_INCOMPATIBLE];
421     let this_id = LintId::of(lint);
422     if future_incompat_lints.0.iter().any(|&id| id == this_id) {
423         let msg = "this lint will become a HARD ERROR in a future release!";
424         if let Some(sp) = span {
425             err.span_note(sp, msg);
426         } else {
427             err.note(msg);
428         }
429     }
430
431     if let Some(span) = def {
432         err.span_note(span, "lint level defined here");
433     }
434
435     err
436 }
437
438 pub trait LintContext: Sized {
439     fn sess(&self) -> &Session;
440     fn lints(&self) -> &LintStore;
441     fn mut_lints(&mut self) -> &mut LintStore;
442     fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)>;
443     fn enter_attrs(&mut self, attrs: &[ast::Attribute]);
444     fn exit_attrs(&mut self, attrs: &[ast::Attribute]);
445
446     /// Get the level of `lint` at the current position of the lint
447     /// traversal.
448     fn current_level(&self, lint: &'static Lint) -> Level {
449         self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
450     }
451
452     fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
453         self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls {
454             &(Warn, src) => {
455                 let lint_id = LintId::of(builtin::WARNINGS);
456                 (self.lints().get_level_source(lint_id).0, src)
457             }
458             _ => *ls
459         })
460     }
461
462     fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
463         let (level, src) = match self.level_src(lint) {
464             None => return,
465             Some(pair) => pair,
466         };
467
468         raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg);
469     }
470
471     fn lookup(&self,
472               lint: &'static Lint,
473               span: Option<Span>,
474               msg: &str)
475               -> DiagnosticBuilder {
476         let (level, src) = match self.level_src(lint) {
477             None => return self.sess().diagnostic().struct_dummy(),
478             Some(pair) => pair,
479         };
480
481         raw_struct_lint(&self.sess(), self.lints(), lint, (level, src), span, msg)
482     }
483
484     /// Emit a lint at the appropriate level, for a particular span.
485     fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
486         self.lookup_and_emit(lint, Some(span), msg);
487     }
488
489     fn struct_span_lint(&self,
490                         lint: &'static Lint,
491                         span: Span,
492                         msg: &str)
493                         -> DiagnosticBuilder {
494         self.lookup(lint, Some(span), msg)
495     }
496
497     /// Emit a lint and note at the appropriate level, for a particular span.
498     fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
499                       note_span: Span, note: &str) {
500         let mut err = self.lookup(lint, Some(span), msg);
501         if self.current_level(lint) != Level::Allow {
502             if note_span == span {
503                 err.fileline_note(note_span, note);
504             } else {
505                 err.span_note(note_span, note);
506             }
507         }
508         err.emit();
509     }
510
511     /// Emit a lint and help at the appropriate level, for a particular span.
512     fn span_lint_help(&self, lint: &'static Lint, span: Span,
513                       msg: &str, help: &str) {
514         let mut err = self.lookup(lint, Some(span), msg);
515         self.span_lint(lint, span, msg);
516         if self.current_level(lint) != Level::Allow {
517             err.span_help(span, help);
518         }
519         err.emit();
520     }
521
522     /// Emit a lint at the appropriate level, with no associated span.
523     fn lint(&self, lint: &'static Lint, msg: &str) {
524         self.lookup_and_emit(lint, None, msg);
525     }
526
527     /// Merge the lints specified by any lint attributes into the
528     /// current lint context, call the provided function, then reset the
529     /// lints in effect to their previous state.
530     fn with_lint_attrs<F>(&mut self,
531                           attrs: &[ast::Attribute],
532                           f: F)
533         where F: FnOnce(&mut Self),
534     {
535         // Parse all of the lint attributes, and then add them all to the
536         // current dictionary of lint information. Along the way, keep a history
537         // of what we changed so we can roll everything back after invoking the
538         // specified closure
539         let mut pushed = 0;
540
541         for result in gather_attrs(attrs) {
542             let v = match result {
543                 Err(span) => {
544                     span_err!(self.sess(), span, E0452,
545                               "malformed lint attribute");
546                     continue;
547                 }
548                 Ok((lint_name, level, span)) => {
549                     match self.lints().find_lint(&lint_name, &self.sess(), Some(span)) {
550                         Ok(lint_id) => vec![(lint_id, level, span)],
551                         Err(FindLintError::NotFound) => {
552                             match self.lints().lint_groups.get(&lint_name[..]) {
553                                 Some(&(ref v, _)) => v.iter()
554                                                       .map(|lint_id: &LintId|
555                                                            (*lint_id, level, span))
556                                                       .collect(),
557                                 None => {
558                                     self.span_lint(builtin::UNKNOWN_LINTS, span,
559                                                    &format!("unknown `{}` attribute: `{}`",
560                                                             level.as_str(), lint_name));
561                                     continue;
562                                 }
563                             }
564                         },
565                         Err(FindLintError::Removed) => { continue; }
566                     }
567                 }
568             };
569
570             for (lint_id, level, span) in v {
571                 let now = self.lints().get_level_source(lint_id).0;
572                 if now == Forbid && level != Forbid {
573                     let lint_name = lint_id.as_str();
574                     span_err!(self.sess(), span, E0453,
575                               "{}({}) overruled by outer forbid({})",
576                               level.as_str(), lint_name,
577                               lint_name);
578                 } else if now != level {
579                     let src = self.lints().get_level_source(lint_id).1;
580                     self.level_stack().push((lint_id, (now, src)));
581                     pushed += 1;
582                     self.mut_lints().set_level(lint_id, (level, Node(span)));
583                 }
584             }
585         }
586
587         self.enter_attrs(attrs);
588         f(self);
589         self.exit_attrs(attrs);
590
591         // rollback
592         for _ in 0..pushed {
593             let (lint, lvlsrc) = self.level_stack().pop().unwrap();
594             self.mut_lints().set_level(lint, lvlsrc);
595         }
596     }
597 }
598
599
600 impl<'a> EarlyContext<'a> {
601     fn new(sess: &'a Session,
602            krate: &'a ast::Crate) -> EarlyContext<'a> {
603         // We want to own the lint store, so move it out of the session. Remember
604         // to put it back later...
605         let lint_store = mem::replace(&mut *sess.lint_store.borrow_mut(),
606                                       LintStore::new());
607         EarlyContext {
608             sess: sess,
609             krate: krate,
610             lints: lint_store,
611             level_stack: vec![],
612         }
613     }
614
615     fn visit_ids<F>(&mut self, f: F)
616         where F: FnOnce(&mut ast_util::IdVisitor<EarlyContext>)
617     {
618         let mut v = ast_util::IdVisitor {
619             operation: self,
620             visited_outermost: false,
621         };
622         f(&mut v);
623     }
624 }
625
626 impl<'a, 'tcx> LateContext<'a, 'tcx> {
627     fn new(tcx: &'a ty::ctxt<'tcx>,
628            krate: &'a hir::Crate,
629            access_levels: &'a AccessLevels) -> LateContext<'a, 'tcx> {
630         // We want to own the lint store, so move it out of the session.
631         let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
632                                       LintStore::new());
633
634         LateContext {
635             tcx: tcx,
636             krate: krate,
637             access_levels: access_levels,
638             lints: lint_store,
639             level_stack: vec![],
640             node_levels: RefCell::new(FnvHashMap()),
641         }
642     }
643
644     fn visit_ids<F>(&mut self, f: F)
645         where F: FnOnce(&mut util::IdVisitor<LateContext>)
646     {
647         let mut v = util::IdVisitor::new(self);
648         f(&mut v);
649     }
650 }
651
652 impl<'a, 'tcx> LintContext for LateContext<'a, 'tcx> {
653     /// Get the overall compiler `Session` object.
654     fn sess(&self) -> &Session {
655         &self.tcx.sess
656     }
657
658     fn lints(&self) -> &LintStore {
659         &self.lints
660     }
661
662     fn mut_lints(&mut self) -> &mut LintStore {
663         &mut self.lints
664     }
665
666     fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> {
667         &mut self.level_stack
668     }
669
670     fn enter_attrs(&mut self, attrs: &[ast::Attribute]) {
671         debug!("late context: enter_attrs({:?})", attrs);
672         run_lints!(self, enter_lint_attrs, late_passes, attrs);
673     }
674
675     fn exit_attrs(&mut self, attrs: &[ast::Attribute]) {
676         debug!("late context: exit_attrs({:?})", attrs);
677         run_lints!(self, exit_lint_attrs, late_passes, attrs);
678     }
679 }
680
681 impl<'a> LintContext for EarlyContext<'a> {
682     /// Get the overall compiler `Session` object.
683     fn sess(&self) -> &Session {
684         &self.sess
685     }
686
687     fn lints(&self) -> &LintStore {
688         &self.lints
689     }
690
691     fn mut_lints(&mut self) -> &mut LintStore {
692         &mut self.lints
693     }
694
695     fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> {
696         &mut self.level_stack
697     }
698
699     fn enter_attrs(&mut self, attrs: &[ast::Attribute]) {
700         debug!("early context: exit_attrs({:?})", attrs);
701         run_lints!(self, enter_lint_attrs, early_passes, attrs);
702     }
703
704     fn exit_attrs(&mut self, attrs: &[ast::Attribute]) {
705         debug!("early context: exit_attrs({:?})", attrs);
706         run_lints!(self, exit_lint_attrs, early_passes, attrs);
707     }
708 }
709
710 impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
711     /// Because lints are scoped lexically, we want to walk nested
712     /// items in the context of the outer item, so enable
713     /// deep-walking.
714     fn visit_nested_item(&mut self, item: hir::ItemId) {
715         self.visit_item(self.tcx.map.expect_item(item.id))
716     }
717
718     fn visit_item(&mut self, it: &hir::Item) {
719         self.with_lint_attrs(&it.attrs, |cx| {
720             run_lints!(cx, check_item, late_passes, it);
721             cx.visit_ids(|v| v.visit_item(it));
722             hir_visit::walk_item(cx, it);
723         })
724     }
725
726     fn visit_foreign_item(&mut self, it: &hir::ForeignItem) {
727         self.with_lint_attrs(&it.attrs, |cx| {
728             run_lints!(cx, check_foreign_item, late_passes, it);
729             hir_visit::walk_foreign_item(cx, it);
730         })
731     }
732
733     fn visit_pat(&mut self, p: &hir::Pat) {
734         run_lints!(self, check_pat, late_passes, p);
735         hir_visit::walk_pat(self, p);
736     }
737
738     fn visit_expr(&mut self, e: &hir::Expr) {
739         self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| {
740             run_lints!(cx, check_expr, late_passes, e);
741             hir_visit::walk_expr(cx, e);
742         })
743     }
744
745     fn visit_stmt(&mut self, s: &hir::Stmt) {
746         // statement attributes are actually just attributes on one of
747         // - item
748         // - local
749         // - expression
750         // so we keep track of lint levels there
751         run_lints!(self, check_stmt, late_passes, s);
752         hir_visit::walk_stmt(self, s);
753     }
754
755     fn visit_fn(&mut self, fk: hir_visit::FnKind<'v>, decl: &'v hir::FnDecl,
756                 body: &'v hir::Block, span: Span, id: ast::NodeId) {
757         run_lints!(self, check_fn, late_passes, fk, decl, body, span, id);
758         hir_visit::walk_fn(self, fk, decl, body, span);
759     }
760
761     fn visit_variant_data(&mut self,
762                         s: &hir::VariantData,
763                         name: ast::Name,
764                         g: &hir::Generics,
765                         item_id: ast::NodeId,
766                         _: Span) {
767         run_lints!(self, check_struct_def, late_passes, s, name, g, item_id);
768         hir_visit::walk_struct_def(self, s);
769         run_lints!(self, check_struct_def_post, late_passes, s, name, g, item_id);
770     }
771
772     fn visit_struct_field(&mut self, s: &hir::StructField) {
773         self.with_lint_attrs(&s.node.attrs, |cx| {
774             run_lints!(cx, check_struct_field, late_passes, s);
775             hir_visit::walk_struct_field(cx, s);
776         })
777     }
778
779     fn visit_variant(&mut self, v: &hir::Variant, g: &hir::Generics, item_id: ast::NodeId) {
780         self.with_lint_attrs(&v.node.attrs, |cx| {
781             run_lints!(cx, check_variant, late_passes, v, g);
782             hir_visit::walk_variant(cx, v, g, item_id);
783             run_lints!(cx, check_variant_post, late_passes, v, g);
784         })
785     }
786
787     fn visit_ty(&mut self, t: &hir::Ty) {
788         run_lints!(self, check_ty, late_passes, t);
789         hir_visit::walk_ty(self, t);
790     }
791
792     fn visit_name(&mut self, sp: Span, name: ast::Name) {
793         run_lints!(self, check_name, late_passes, sp, name);
794     }
795
796     fn visit_mod(&mut self, m: &hir::Mod, s: Span, n: ast::NodeId) {
797         run_lints!(self, check_mod, late_passes, m, s, n);
798         hir_visit::walk_mod(self, m);
799     }
800
801     fn visit_local(&mut self, l: &hir::Local) {
802         self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| {
803             run_lints!(cx, check_local, late_passes, l);
804             hir_visit::walk_local(cx, l);
805         })
806     }
807
808     fn visit_block(&mut self, b: &hir::Block) {
809         run_lints!(self, check_block, late_passes, b);
810         hir_visit::walk_block(self, b);
811     }
812
813     fn visit_arm(&mut self, a: &hir::Arm) {
814         run_lints!(self, check_arm, late_passes, a);
815         hir_visit::walk_arm(self, a);
816     }
817
818     fn visit_decl(&mut self, d: &hir::Decl) {
819         run_lints!(self, check_decl, late_passes, d);
820         hir_visit::walk_decl(self, d);
821     }
822
823     fn visit_expr_post(&mut self, e: &hir::Expr) {
824         run_lints!(self, check_expr_post, late_passes, e);
825     }
826
827     fn visit_generics(&mut self, g: &hir::Generics) {
828         run_lints!(self, check_generics, late_passes, g);
829         hir_visit::walk_generics(self, g);
830     }
831
832     fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {
833         self.with_lint_attrs(&trait_item.attrs, |cx| {
834             run_lints!(cx, check_trait_item, late_passes, trait_item);
835             cx.visit_ids(|v| v.visit_trait_item(trait_item));
836             hir_visit::walk_trait_item(cx, trait_item);
837         });
838     }
839
840     fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) {
841         self.with_lint_attrs(&impl_item.attrs, |cx| {
842             run_lints!(cx, check_impl_item, late_passes, impl_item);
843             cx.visit_ids(|v| v.visit_impl_item(impl_item));
844             hir_visit::walk_impl_item(cx, impl_item);
845         });
846     }
847
848     fn visit_lifetime(&mut self, lt: &hir::Lifetime) {
849         run_lints!(self, check_lifetime, late_passes, lt);
850     }
851
852     fn visit_lifetime_def(&mut self, lt: &hir::LifetimeDef) {
853         run_lints!(self, check_lifetime_def, late_passes, lt);
854     }
855
856     fn visit_explicit_self(&mut self, es: &hir::ExplicitSelf) {
857         run_lints!(self, check_explicit_self, late_passes, es);
858         hir_visit::walk_explicit_self(self, es);
859     }
860
861     fn visit_path(&mut self, p: &hir::Path, id: ast::NodeId) {
862         run_lints!(self, check_path, late_passes, p, id);
863         hir_visit::walk_path(self, p);
864     }
865
866     fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) {
867         run_lints!(self, check_path_list_item, late_passes, item);
868         hir_visit::walk_path_list_item(self, prefix, item);
869     }
870
871     fn visit_attribute(&mut self, attr: &ast::Attribute) {
872         run_lints!(self, check_attribute, late_passes, attr);
873     }
874 }
875
876 impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
877     fn visit_item(&mut self, it: &ast::Item) {
878         self.with_lint_attrs(&it.attrs, |cx| {
879             run_lints!(cx, check_item, early_passes, it);
880             cx.visit_ids(|v| v.visit_item(it));
881             ast_visit::walk_item(cx, it);
882         })
883     }
884
885     fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
886         self.with_lint_attrs(&it.attrs, |cx| {
887             run_lints!(cx, check_foreign_item, early_passes, it);
888             ast_visit::walk_foreign_item(cx, it);
889         })
890     }
891
892     fn visit_pat(&mut self, p: &ast::Pat) {
893         run_lints!(self, check_pat, early_passes, p);
894         ast_visit::walk_pat(self, p);
895     }
896
897     fn visit_expr(&mut self, e: &ast::Expr) {
898         run_lints!(self, check_expr, early_passes, e);
899         ast_visit::walk_expr(self, e);
900     }
901
902     fn visit_stmt(&mut self, s: &ast::Stmt) {
903         run_lints!(self, check_stmt, early_passes, s);
904         ast_visit::walk_stmt(self, s);
905     }
906
907     fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, decl: &'v ast::FnDecl,
908                 body: &'v ast::Block, span: Span, id: ast::NodeId) {
909         run_lints!(self, check_fn, early_passes, fk, decl, body, span, id);
910         ast_visit::walk_fn(self, fk, decl, body, span);
911     }
912
913     fn visit_variant_data(&mut self,
914                         s: &ast::VariantData,
915                         ident: ast::Ident,
916                         g: &ast::Generics,
917                         item_id: ast::NodeId,
918                         _: Span) {
919         run_lints!(self, check_struct_def, early_passes, s, ident, g, item_id);
920         ast_visit::walk_struct_def(self, s);
921         run_lints!(self, check_struct_def_post, early_passes, s, ident, g, item_id);
922     }
923
924     fn visit_struct_field(&mut self, s: &ast::StructField) {
925         self.with_lint_attrs(&s.node.attrs, |cx| {
926             run_lints!(cx, check_struct_field, early_passes, s);
927             ast_visit::walk_struct_field(cx, s);
928         })
929     }
930
931     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, item_id: ast::NodeId) {
932         self.with_lint_attrs(&v.node.attrs, |cx| {
933             run_lints!(cx, check_variant, early_passes, v, g);
934             ast_visit::walk_variant(cx, v, g, item_id);
935             run_lints!(cx, check_variant_post, early_passes, v, g);
936         })
937     }
938
939     fn visit_ty(&mut self, t: &ast::Ty) {
940         run_lints!(self, check_ty, early_passes, t);
941         ast_visit::walk_ty(self, t);
942     }
943
944     fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
945         run_lints!(self, check_ident, early_passes, sp, id);
946     }
947
948     fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
949         run_lints!(self, check_mod, early_passes, m, s, n);
950         ast_visit::walk_mod(self, m);
951     }
952
953     fn visit_local(&mut self, l: &ast::Local) {
954         run_lints!(self, check_local, early_passes, l);
955         ast_visit::walk_local(self, l);
956     }
957
958     fn visit_block(&mut self, b: &ast::Block) {
959         run_lints!(self, check_block, early_passes, b);
960         ast_visit::walk_block(self, b);
961     }
962
963     fn visit_arm(&mut self, a: &ast::Arm) {
964         run_lints!(self, check_arm, early_passes, a);
965         ast_visit::walk_arm(self, a);
966     }
967
968     fn visit_decl(&mut self, d: &ast::Decl) {
969         run_lints!(self, check_decl, early_passes, d);
970         ast_visit::walk_decl(self, d);
971     }
972
973     fn visit_expr_post(&mut self, e: &ast::Expr) {
974         run_lints!(self, check_expr_post, early_passes, e);
975     }
976
977     fn visit_generics(&mut self, g: &ast::Generics) {
978         run_lints!(self, check_generics, early_passes, g);
979         ast_visit::walk_generics(self, g);
980     }
981
982     fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
983         self.with_lint_attrs(&trait_item.attrs, |cx| {
984             run_lints!(cx, check_trait_item, early_passes, trait_item);
985             cx.visit_ids(|v| v.visit_trait_item(trait_item));
986             ast_visit::walk_trait_item(cx, trait_item);
987         });
988     }
989
990     fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
991         self.with_lint_attrs(&impl_item.attrs, |cx| {
992             run_lints!(cx, check_impl_item, early_passes, impl_item);
993             cx.visit_ids(|v| v.visit_impl_item(impl_item));
994             ast_visit::walk_impl_item(cx, impl_item);
995         });
996     }
997
998     fn visit_lifetime(&mut self, lt: &ast::Lifetime) {
999         run_lints!(self, check_lifetime, early_passes, lt);
1000     }
1001
1002     fn visit_lifetime_def(&mut self, lt: &ast::LifetimeDef) {
1003         run_lints!(self, check_lifetime_def, early_passes, lt);
1004     }
1005
1006     fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
1007         run_lints!(self, check_explicit_self, early_passes, es);
1008         ast_visit::walk_explicit_self(self, es);
1009     }
1010
1011     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
1012         run_lints!(self, check_path, early_passes, p, id);
1013         ast_visit::walk_path(self, p);
1014     }
1015
1016     fn visit_path_list_item(&mut self, prefix: &ast::Path, item: &ast::PathListItem) {
1017         run_lints!(self, check_path_list_item, early_passes, item);
1018         ast_visit::walk_path_list_item(self, prefix, item);
1019     }
1020
1021     fn visit_attribute(&mut self, attr: &ast::Attribute) {
1022         run_lints!(self, check_attribute, early_passes, attr);
1023     }
1024 }
1025
1026 // Output any lints that were previously added to the session.
1027 impl<'a, 'tcx> IdVisitingOperation for LateContext<'a, 'tcx> {
1028     fn visit_id(&mut self, id: ast::NodeId) {
1029         match self.sess().lints.borrow_mut().remove(&id) {
1030             None => {}
1031             Some(lints) => {
1032                 debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints);
1033                 for (lint_id, span, msg) in lints {
1034                     self.span_lint(lint_id.lint, span, &msg[..])
1035                 }
1036             }
1037         }
1038     }
1039 }
1040 impl<'a> IdVisitingOperation for EarlyContext<'a> {
1041     fn visit_id(&mut self, id: ast::NodeId) {
1042         match self.sess.lints.borrow_mut().remove(&id) {
1043             None => {}
1044             Some(lints) => {
1045                 for (lint_id, span, msg) in lints {
1046                     self.span_lint(lint_id.lint, span, &msg[..])
1047                 }
1048             }
1049         }
1050     }
1051 }
1052
1053 // This lint pass is defined here because it touches parts of the `LateContext`
1054 // that we don't want to expose. It records the lint level at certain AST
1055 // nodes, so that the variant size difference check in trans can call
1056 // `raw_emit_lint`.
1057
1058 pub struct GatherNodeLevels;
1059
1060 impl LintPass for GatherNodeLevels {
1061     fn get_lints(&self) -> LintArray {
1062         lint_array!()
1063     }
1064 }
1065
1066 impl LateLintPass for GatherNodeLevels {
1067     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
1068         match it.node {
1069             hir::ItemEnum(..) => {
1070                 let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES);
1071                 let lvlsrc = cx.lints.get_level_source(lint_id);
1072                 match lvlsrc {
1073                     (lvl, _) if lvl != Allow => {
1074                         cx.node_levels.borrow_mut()
1075                             .insert((it.id, lint_id), lvlsrc);
1076                     },
1077                     _ => { }
1078                 }
1079             },
1080             _ => { }
1081         }
1082     }
1083 }
1084
1085 /// Perform lint checking on a crate.
1086 ///
1087 /// Consumes the `lint_store` field of the `Session`.
1088 pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) {
1089     let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck);
1090
1091     let krate = tcx.map.krate();
1092     let mut cx = LateContext::new(tcx, krate, access_levels);
1093
1094     // Visit the whole crate.
1095     cx.with_lint_attrs(&krate.attrs, |cx| {
1096         cx.visit_id(ast::CRATE_NODE_ID);
1097         cx.visit_ids(|v| {
1098             hir_visit::walk_crate(v, krate);
1099         });
1100
1101         // since the root module isn't visited as an item (because it isn't an
1102         // item), warn for it here.
1103         run_lints!(cx, check_crate, late_passes, krate);
1104
1105         hir_visit::walk_crate(cx, krate);
1106     });
1107
1108     // If we missed any lints added to the session, then there's a bug somewhere
1109     // in the iteration code.
1110     for (id, v) in tcx.sess.lints.borrow().iter() {
1111         for &(lint, span, ref msg) in v {
1112             tcx.sess.span_bug(span,
1113                               &format!("unprocessed lint {} at {}: {}",
1114                                        lint.as_str(), tcx.map.node_to_string(*id), *msg))
1115         }
1116     }
1117
1118     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
1119 }
1120
1121 pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
1122     let mut cx = EarlyContext::new(sess, krate);
1123
1124     // Visit the whole crate.
1125     cx.with_lint_attrs(&krate.attrs, |cx| {
1126         cx.visit_id(ast::CRATE_NODE_ID);
1127         cx.visit_ids(|v| {
1128             v.visited_outermost = true;
1129             ast_visit::walk_crate(v, krate);
1130         });
1131
1132         // since the root module isn't visited as an item (because it isn't an
1133         // item), warn for it here.
1134         run_lints!(cx, check_crate, early_passes, krate);
1135
1136         ast_visit::walk_crate(cx, krate);
1137     });
1138
1139     // Put the lint store back in the session.
1140     mem::replace(&mut *sess.lint_store.borrow_mut(), cx.lints);
1141
1142     // If we missed any lints added to the session, then there's a bug somewhere
1143     // in the iteration code.
1144     for (_, v) in sess.lints.borrow().iter() {
1145         for &(lint, span, ref msg) in v {
1146             sess.span_bug(span,
1147                           &format!("unprocessed lint {}: {}",
1148                                    lint.as_str(), *msg))
1149         }
1150     }
1151 }