1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 //! A 'lint' check is a kind of miscellaneous constraint that a user _might_
12 //! want to enforce, but might reasonably want to permit as well, on a
13 //! module-by-module basis. They contrast with static constraints enforced by
14 //! other phases of the compiler, which are generally required to hold in order
15 //! to compile the program at all.
17 //! The lint checking is all consolidated into one pass which runs just before
18 //! translation to LLVM bytecode. Throughout compilation, lint warnings can be
19 //! added via the `add_lint` method on the Session structure. This requires a
20 //! span and an id of the node that the lint is being added to. The lint isn't
21 //! actually emitted at that time because it is unknown what the actual lint
22 //! level at that location is.
24 //! To actually emit lint warnings/errors, a separate pass is used just before
25 //! translation. A context keeps track of the current state of all lint levels.
26 //! Upon entering a node of the ast which can modify the lint settings, the
27 //! previous lint state is pushed onto a stack and the ast is then recursed
28 //! upon. As the ast is traversed, this keeps track of the current lint level
29 //! for all lint attributes.
31 //! Most of the lints built into `rustc` are structs implementing `LintPass`,
32 //! and are defined within `builtin.rs`. To add a new lint you can define such
33 //! a struct and add it to the `builtin_lints!` macro invocation in this file.
34 //! `LintPass` itself is not a subtrait of `Default`, but the `builtin_lints!`
35 //! macro requires `Default` (usually via `deriving`).
37 //! Some lints are defined elsewhere in the compiler and work by calling
38 //! `add_lint()` on the overall `Session` object.
40 //! If you're adding lints to the `Context` infrastructure itself, defined in
41 //! this file, use `span_lint` instead of `add_lint`.
43 #![allow(non_camel_case_types)]
46 use middle::privacy::ExportedItems;
48 use middle::typeck::astconv::AstConv;
49 use middle::typeck::infer;
50 use driver::session::Session;
52 use std::collections::HashMap;
55 use std::to_str::ToStr;
56 use std::cell::RefCell;
57 use std::default::Default;
59 use std::tuple::Tuple2;
61 use syntax::ast_util::IdVisitingOperation;
62 use syntax::attr::AttrMetaMethods;
64 use syntax::codemap::Span;
65 use syntax::visit::{Visitor, FnKind};
66 use syntax::{ast, ast_util, visit};
69 macro_rules! lint_initializer (
70 ($name:ident, $level:ident, $desc:expr) => (
72 name: stringify!($name),
73 default_level: ::rustc::lint::$level,
80 macro_rules! declare_lint (
81 // FIXME(#14660): deduplicate
82 (pub $name:ident, $level:ident, $desc:expr) => (
83 pub static $name: &'static ::rustc::lint::Lint
84 = &lint_initializer!($name, $level, $desc);
86 ($name:ident, $level:ident, $desc:expr) => (
87 static $name: &'static ::rustc::lint::Lint
88 = &lint_initializer!($name, $level, $desc);
93 macro_rules! lint_array ( ($( $lint:expr ),*) => (
95 static array: LintArray = &[ $( $lint ),* ];
102 /// Specification of a single lint.
104 /// An identifier for the lint, written with underscores,
105 /// e.g. "unused_imports". This identifies the lint in
106 /// attributes and in command-line arguments. On the
107 /// command line, underscores become dashes.
108 pub name: &'static str,
110 /// Default level for the lint.
111 pub default_level: Level,
113 /// Description of the lint or the issue it detects,
114 /// e.g. "imports that are never used"
115 pub desc: &'static str,
118 type LintArray = &'static [&'static Lint];
120 /// Trait for types providing lint checks. Each `check` method checks a single
121 /// syntax node, and should not invoke methods recursively (unlike `Visitor`).
122 /// By default they do nothing.
124 // FIXME: eliminate the duplication with `Visitor`. But this also
125 // contains a few lint-specific methods with no equivalent in `Visitor`.
127 /// Get descriptions of the lints this `LintPass` object can emit.
129 /// NB: there is no enforcement that the object only emits lints it registered.
130 /// And some `rustc` internal `LintPass`es register lints to be emitted by other
131 /// parts of the compiler. If you want enforced access restrictions for your
132 /// `Lint`, make it a private `static` item in its own module.
133 fn get_lints(&self) -> LintArray;
135 fn check_crate(&mut self, _: &Context, _: &ExportedItems, _: &ast::Crate) { }
136 fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { }
137 fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { }
138 fn check_view_item(&mut self, _: &Context, _: &ast::ViewItem) { }
139 fn check_foreign_item(&mut self, _: &Context, _: &ast::ForeignItem) { }
140 fn check_item(&mut self, _: &Context, _: &ast::Item) { }
141 fn check_local(&mut self, _: &Context, _: &ast::Local) { }
142 fn check_block(&mut self, _: &Context, _: &ast::Block) { }
143 fn check_stmt(&mut self, _: &Context, _: &ast::Stmt) { }
144 fn check_arm(&mut self, _: &Context, _: &ast::Arm) { }
145 fn check_pat(&mut self, _: &Context, _: &ast::Pat) { }
146 fn check_decl(&mut self, _: &Context, _: &ast::Decl) { }
147 fn check_expr(&mut self, _: &Context, _: &ast::Expr) { }
148 fn check_expr_post(&mut self, _: &Context, _: &ast::Expr) { }
149 fn check_ty(&mut self, _: &Context, _: &ast::Ty) { }
150 fn check_generics(&mut self, _: &Context, _: &ast::Generics) { }
151 fn check_fn(&mut self, _: &Context,
152 _: &FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
153 fn check_ty_method(&mut self, _: &Context, _: &ast::TypeMethod) { }
154 fn check_trait_method(&mut self, _: &Context, _: &ast::TraitMethod) { }
155 fn check_struct_def(&mut self, _: &Context,
156 _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
157 fn check_struct_def_post(&mut self, _: &Context,
158 _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
159 fn check_struct_field(&mut self, _: &Context, _: &ast::StructField) { }
160 fn check_variant(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { }
161 fn check_opt_lifetime_ref(&mut self, _: &Context, _: Span, _: &Option<ast::Lifetime>) { }
162 fn check_lifetime_ref(&mut self, _: &Context, _: &ast::Lifetime) { }
163 fn check_lifetime_decl(&mut self, _: &Context, _: &ast::Lifetime) { }
164 fn check_explicit_self(&mut self, _: &Context, _: &ast::ExplicitSelf) { }
165 fn check_mac(&mut self, _: &Context, _: &ast::Mac) { }
166 fn check_path(&mut self, _: &Context, _: &ast::Path, _: ast::NodeId) { }
167 fn check_attribute(&mut self, _: &Context, _: &ast::Attribute) { }
169 /// Called when entering a syntax node that can have lint attributes such
170 /// as `#[allow(...)]`. Called with *all* the attributes of that node.
171 fn enter_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
173 /// Counterpart to `enter_lint_attrs`.
174 fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { }
177 type LintPassObject = Box<LintPass + 'static>;
179 /// Identifies a lint known to the compiler.
182 // Identity is based on pointer equality of this field.
186 impl PartialEq for LintId {
187 fn eq(&self, other: &LintId) -> bool {
188 (self.lint as *Lint) == (other.lint as *Lint)
192 impl Eq for LintId { }
194 impl<S: hash::Writer> Hash<S> for LintId {
195 fn hash(&self, state: &mut S) {
196 let ptr = self.lint as *Lint;
202 pub fn of(lint: &'static Lint) -> LintId {
208 pub fn as_str(&self) -> &'static str {
213 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
215 Allow, Warn, Deny, Forbid
219 pub fn as_str(self) -> &'static str {
228 pub fn from_str(x: &str) -> Option<Level> {
230 "allow" => Some(Allow),
231 "warn" => Some(Warn),
232 "deny" => Some(Deny),
233 "forbid" => Some(Forbid),
239 // this is public for the lints that run in trans
240 #[deriving(PartialEq)]
241 pub enum LintSource {
247 pub type LevelSource = (Level, LintSource);
250 /// Trait objects for each lint pass.
251 lint_objects: Vec<RefCell<LintPassObject>>,
253 /// Lints indexed by name.
254 lints_by_name: HashMap<&'static str, LintId>,
256 /// Current levels of each lint, and where they were set.
257 levels: HashMap<LintId, LevelSource>,
259 /// Context we're checking in (used to access fields like sess).
262 /// When recursing into an attributed node of the ast which modifies lint
263 /// levels, this stack keeps track of the previous lint levels of whatever
265 level_stack: Vec<(LintId, LevelSource)>,
267 /// Level of lints for certain NodeIds, stored here because the body of
268 /// the lint needs to run in trans.
269 node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
272 /// Convenience macro for calling a `LintPass` method on every pass in the context.
273 macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => (
274 for obj in $cx.lint_objects.iter() {
275 obj.borrow_mut().$f($cx, $($args),*);
279 /// Emit a lint as a `span_warn` or `span_err` (or not at all)
280 /// according to `level`. This lives outside of `Context` so
281 /// it can be used by checks in trans that run after the main
282 /// lint phase is finished.
283 pub fn emit_lint(sess: &Session, lint: &'static Lint,
284 lvlsrc: LevelSource, span: Span, msg: &str) {
285 let (level, source) = lvlsrc;
286 if level == Allow { return }
289 let msg = match source {
291 format!("{}, #[{}({})] on by default", msg,
292 level_to_str(level), lint_str)
295 format!("{} [-{} {}]", msg,
297 Warn => 'W', Deny => 'D', Forbid => 'F',
299 }, lint.name.replace("_", "-"))
308 Warn => { sess.span_warn(span, msg.as_slice()); }
309 Deny | Forbid => { sess.span_err(span, msg.as_slice()); }
313 for span in note.move_iter() {
314 sess.span_note(span, "lint level defined here");
318 impl<'a> Context<'a> {
319 fn get_level_source(&self, lint: LintId) -> LevelSource {
320 match self.levels.find(&lint) {
322 None => (Allow, Default),
326 fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
327 if lvlsrc.val0() == Allow {
328 self.levels.remove(&lint);
330 self.levels.insert(lint, lvlsrc);
334 fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
335 let (level, src) = match self.levels.find(&LintId::of(lint)) {
338 => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src),
342 emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
346 * Merge the lints specified by any lint attributes into the
347 * current lint context, call the provided function, then reset the
348 * lints in effect to their previous state.
350 fn with_lint_attrs(&mut self,
351 attrs: &[ast::Attribute],
353 // Parse all of the lint attributes, and then add them all to the
354 // current dictionary of lint information. Along the way, keep a history
355 // of what we changed so we can roll everything back after invoking the
357 let lint_attrs = self.gather_lint_attrs(attrs);
359 for (lint_id, level, span) in lint_attrs.move_iter() {
360 let now = self.get_level_source(lint_id).val0();
361 if now == Forbid && level != Forbid {
362 let lint_name = lint_id.as_str();
363 self.tcx.sess.span_err(span,
364 format!("{}({}) overruled by outer forbid({})",
365 level.as_str(), lint_name, lint_name).as_slice());
366 } else if now != level {
367 let src = self.get_level_source(lint_id).val1();
368 self.level_stack.push((lint_id, (now, src)));
370 self.set_level(lint_id, (level, Node(span)));
374 run_lints!(self, enter_lint_attrs, attrs);
376 run_lints!(self, exit_lint_attrs, attrs);
379 for _ in range(0, pushed) {
380 let (lint, lvlsrc) = self.level_stack.pop().unwrap();
381 self.set_level(lint, lvlsrc);
385 fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
386 let mut v = ast_util::IdVisitor {
388 pass_through_items: false,
389 visited_outermost: false,
394 fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvlsrc: LevelSource) {
395 self.node_levels.borrow_mut().insert((id, lint), lvlsrc);
398 fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> {
399 // Doing this as an iterator is messy due to multiple borrowing.
400 // Allocating and copying these should be quick.
401 let mut out = vec!();
402 for attr in attrs.iter() {
403 let level = match Level::from_str(attr.name().get()) {
408 attr::mark_used(attr);
410 let meta = attr.node.value;
411 let metas = match meta.node {
412 ast::MetaList(_, ref metas) => metas,
414 self.tcx.sess.span_err(meta.span, "malformed lint attribute");
419 for meta in metas.iter() {
421 ast::MetaWord(ref lint_name) => {
422 match self.lints_by_name.find_equiv(lint_name) {
423 Some(lint_id) => out.push((*lint_id, level, meta.span)),
425 None => self.span_lint(builtin::unrecognized_lint,
427 format!("unknown `{}` attribute: `{}`",
428 level.as_str(), lint_name).as_slice()),
431 _ => self.tcx.sess.span_err(meta.span, "malformed lint attribute"),
439 impl<'a> AstConv for Context<'a>{
440 fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
442 fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
443 ty::lookup_item_type(self.tcx, id)
446 fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
447 ty::lookup_trait_def(self.tcx, id)
450 fn ty_infer(&self, _span: Span) -> ty::t {
451 infer::new_infer_ctxt(self.tcx).next_ty_var()
455 impl<'a> Visitor<()> for Context<'a> {
456 fn visit_item(&mut self, it: &ast::Item, _: ()) {
457 self.with_lint_attrs(it.attrs.as_slice(), |cx| {
458 run_lints!(cx, check_item, it);
459 cx.visit_ids(|v| v.visit_item(it, ()));
460 visit::walk_item(cx, it, ());
464 fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
465 self.with_lint_attrs(it.attrs.as_slice(), |cx| {
466 run_lints!(cx, check_foreign_item, it);
467 visit::walk_foreign_item(cx, it, ());
471 fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
472 self.with_lint_attrs(i.attrs.as_slice(), |cx| {
473 run_lints!(cx, check_view_item, i);
474 cx.visit_ids(|v| v.visit_view_item(i, ()));
475 visit::walk_view_item(cx, i, ());
479 fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
480 run_lints!(self, check_pat, p);
481 visit::walk_pat(self, p, ());
484 fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
485 run_lints!(self, check_expr, e);
486 visit::walk_expr(self, e, ());
489 fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
490 run_lints!(self, check_stmt, s);
491 visit::walk_stmt(self, s, ());
494 fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl,
495 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
497 visit::FkMethod(_, _, m) => {
498 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
499 run_lints!(cx, check_fn, fk, decl, body, span, id);
501 v.visit_fn(fk, decl, body, span, id, ());
503 visit::walk_fn(cx, fk, decl, body, span, ());
507 run_lints!(self, check_fn, fk, decl, body, span, id);
508 visit::walk_fn(self, fk, decl, body, span, ());
513 fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
514 self.with_lint_attrs(t.attrs.as_slice(), |cx| {
515 run_lints!(cx, check_ty_method, t);
516 visit::walk_ty_method(cx, t, ());
520 fn visit_struct_def(&mut self,
526 run_lints!(self, check_struct_def, s, ident, g, id);
527 visit::walk_struct_def(self, s, ());
528 run_lints!(self, check_struct_def_post, s, ident, g, id);
531 fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
532 self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
533 run_lints!(cx, check_struct_field, s);
534 visit::walk_struct_field(cx, s, ());
538 fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
539 self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
540 run_lints!(cx, check_variant, v, g);
541 visit::walk_variant(cx, v, g, ());
545 // FIXME(#10894) should continue recursing
546 fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
547 run_lints!(self, check_ty, t);
550 fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
551 run_lints!(self, check_ident, sp, id);
554 fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) {
555 run_lints!(self, check_mod, m, s, n);
556 visit::walk_mod(self, m, ());
559 fn visit_local(&mut self, l: &ast::Local, _: ()) {
560 run_lints!(self, check_local, l);
561 visit::walk_local(self, l, ());
564 fn visit_block(&mut self, b: &ast::Block, _: ()) {
565 run_lints!(self, check_block, b);
566 visit::walk_block(self, b, ());
569 fn visit_arm(&mut self, a: &ast::Arm, _: ()) {
570 run_lints!(self, check_arm, a);
571 visit::walk_arm(self, a, ());
574 fn visit_decl(&mut self, d: &ast::Decl, _: ()) {
575 run_lints!(self, check_decl, d);
576 visit::walk_decl(self, d, ());
579 fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) {
580 run_lints!(self, check_expr_post, e);
583 fn visit_generics(&mut self, g: &ast::Generics, _: ()) {
584 run_lints!(self, check_generics, g);
585 visit::walk_generics(self, g, ());
588 fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) {
589 run_lints!(self, check_trait_method, m);
590 visit::walk_trait_method(self, m, ());
593 fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>, _: ()) {
594 run_lints!(self, check_opt_lifetime_ref, sp, lt);
597 fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) {
598 run_lints!(self, check_lifetime_ref, lt);
601 fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) {
602 run_lints!(self, check_lifetime_decl, lt);
605 fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) {
606 run_lints!(self, check_explicit_self, es);
607 visit::walk_explicit_self(self, es, ());
610 fn visit_mac(&mut self, mac: &ast::Mac, _: ()) {
611 run_lints!(self, check_mac, mac);
612 visit::walk_mac(self, mac, ());
615 fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) {
616 run_lints!(self, check_path, p, id);
617 visit::walk_path(self, p, ());
620 fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
621 run_lints!(self, check_attribute, attr);
625 // Output any lints that were previously added to the session.
626 impl<'a> IdVisitingOperation for Context<'a> {
627 fn visit_id(&self, id: ast::NodeId) {
628 match self.tcx.sess.lints.borrow_mut().pop(&id) {
631 for (lint_id, span, msg) in lints.move_iter() {
632 self.span_lint(lint_id.lint, span, msg.as_slice())
639 fn builtin_lints() -> Vec<Box<LintPass>> {
640 macro_rules! builtin_lints (( $($name:ident),*, ) => (
643 let obj: builtin::$name = Default::default();
644 box obj as LintPassObject
650 WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
651 RawPointerDeriving, UnusedAttribute, PathStatement,
652 UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
653 NonSnakeCaseFunctions, NonUppercaseStatics,
654 NonUppercasePatternStatics, UppercaseVariables,
655 UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
656 UnnecessaryAllocation, MissingDoc, Stability,
658 GatherNodeLevels, HardwiredLints,
662 /// Get specs for all builtin lints. Used for `-W help`.
663 pub fn builtin_lint_specs() -> Vec<&'static Lint> {
664 builtin_lints().move_iter()
665 .flat_map(|x| x.get_lints().iter().map(|&y| y))
669 pub fn check_crate(tcx: &ty::ctxt,
670 exported_items: &ExportedItems,
671 krate: &ast::Crate) {
672 let lints = builtin_lints().move_iter().map(|x| RefCell::new(x)).collect();
674 let mut cx = Context {
676 lints_by_name: HashMap::new(),
677 levels: HashMap::new(),
679 level_stack: Vec::new(),
680 node_levels: RefCell::new(HashMap::new()),
683 // Index the lints by name, and set the default levels.
684 for obj in cx.lint_objects.iter() {
685 for &lint in obj.borrow_mut().get_lints().iter() {
686 let id = LintId::of(lint);
687 if !cx.lints_by_name.insert(lint.name, id) {
688 cx.tcx.sess.err(format!("duplicate specification of lint {}",
689 lint.name).as_slice());
691 if lint.default_level != Allow {
692 cx.levels.insert(id, (lint.default_level, Default));
697 // Set command line lint levels.
698 for &(ref lint_name, level) in tcx.sess.opts.lint_opts.iter() {
699 match cx.lints_by_name.find_equiv(&lint_name.as_slice()) {
700 Some(&lint_id) => cx.set_level(lint_id, (level, CommandLine)),
701 None => cx.tcx.sess.err(format!("unknown {} flag: {}",
702 level.as_str(), lint_name).as_slice()),
706 tcx.sess.abort_if_errors();
708 // Visit the whole crate.
709 cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
710 cx.visit_id(ast::CRATE_NODE_ID);
712 v.visited_outermost = true;
713 visit::walk_crate(v, krate, ());
716 // since the root module isn't visited as an item (because it isn't an
717 // item), warn for it here.
718 run_lints!(cx, check_crate, exported_items, krate);
720 visit::walk_crate(cx, krate, ());
723 // If we missed any lints added to the session, then there's a bug somewhere
724 // in the iteration code.
725 for (id, v) in tcx.sess.lints.borrow().iter() {
726 for &(lint, span, ref msg) in v.iter() {
727 tcx.sess.span_bug(span,
728 format!("unprocessed lint {} at {}: {}",
729 lint.as_str(), tcx.map.node_to_str(*id), *msg)
734 tcx.sess.abort_if_errors();
735 *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap();