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