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