about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-10-02 11:29:29 -0700
committerAlex Crichton <alex@alexcrichton.com>2013-10-03 10:25:14 -0700
commitc0388cd69166f22c818eab21c40c0a37cc1d70a8 (patch)
tree17b06ba76cdf19081d05e0655d3c5cb60d014fb3
parentccd9a963f75074da506c05fece1e3c965e742c51 (diff)
downloadrust-c0388cd69166f22c818eab21c40c0a37cc1d70a8.tar.gz
rust-c0388cd69166f22c818eab21c40c0a37cc1d70a8.zip
Rewrite lint passes with less visitor cruft
This purges about 500 lines of visitor cruft from lint passes. All lints are
handled in a much more sane way at this point. The other huge bonus of this
commit is that there are no more @-boxes in the lint passes, fixing the 500MB
memory regression seen when the lint passes were refactored.

Closes #8589
-rw-r--r--src/librustc/middle/astencode.rs4
-rw-r--r--src/librustc/middle/lint.rs1305
-rw-r--r--src/libsyntax/ast_util.rs40
3 files changed, 423 insertions, 926 deletions
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 902f90eb7cd..b6a782c09b7 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -880,13 +880,13 @@ fn encode_side_tables_for_ii(ecx: &e::EncodeContext,
     // Because the ast visitor uses @IdVisitingOperation, I can't pass in
     // ecx directly, but /I/ know that it'll be fine since the lifetime is
     // tied to the CrateContext that lives throughout this entire section.
-    ast_util::visit_ids_for_inlined_item(ii, @SideTableEncodingIdVisitor {
+    ast_util::visit_ids_for_inlined_item(ii, &SideTableEncodingIdVisitor {
         ecx_ptr: unsafe {
             cast::transmute(ecx)
         },
         new_ebml_w: new_ebml_w,
         maps: maps,
-    } as @ast_util::IdVisitingOperation);
+    });
     ebml_w.end_tag();
 }
 
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index d1c88fb7d64..a44223fba83 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -8,6 +8,30 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! A 'lint' check is a kind of miscellaneous constraint that a user _might_
+//! want to enforce, but might reasonably want to permit as well, on a
+//! module-by-module basis. They contrast with static constraints enforced by
+//! other phases of the compiler, which are generally required to hold in order
+//! to compile the program at all.
+//!
+//! The lint checking is all consolidated into one pass which runs just before
+//! translation to LLVM bytecode. Throughout compilation, lint warnings can be
+//! added via the `add_lint` method on the Session structure. This requires a
+//! span and an id of the node that the lint is being added to. The lint isn't
+//! actually emitted at that time because it is unknown what the actual lint
+//! level at that location is.
+//!
+//! To actually emit lint warnings/errors, a separate pass is used just before
+//! translation. A context keeps track of the current state of all lint levels.
+//! Upon entering a node of the ast which can modify the lint settings, the
+//! previous lint state is pushed onto a stack and the ast is then recursed
+//! upon.  As the ast is traversed, this keeps track of the current lint level
+//! for all lint attributes.
+//!
+//! To add a new lint warning, all you need to do is to either invoke `add_lint`
+//! on the session at the appropriate time, or write a few linting functions and
+//! modify the Context visitor appropriately. If you're adding lints from the
+//! Context itself, span_lint should be used instead of add_lint.
 
 use driver::session;
 use middle::ty;
@@ -35,43 +59,6 @@ use syntax::parse::token;
 use syntax::{ast, ast_util, visit};
 use syntax::visit::Visitor;
 
-/**
- * A 'lint' check is a kind of miscellaneous constraint that a user _might_
- * want to enforce, but might reasonably want to permit as well, on a
- * module-by-module basis. They contrast with static constraints enforced by
- * other phases of the compiler, which are generally required to hold in order
- * to compile the program at all.
- *
- * The lint checking is all consolidated into one pass which runs just before
- * translation to LLVM bytecode. Throughout compilation, lint warnings can be
- * added via the `add_lint` method on the Session structure. This requires a
- * span and an id of the node that the lint is being added to. The lint isn't
- * actually emitted at that time because it is unknown what the actual lint
- * level at that location is.
- *
- * To actually emit lint warnings/errors, a separate pass is used just before
- * translation. A context keeps track of the current state of all lint levels.
- * Upon entering a node of the ast which can modify the lint settings, the
- * previous lint state is pushed onto a stack and the ast is then recursed upon.
- * As the ast is traversed, this keeps track of the current lint level for all
- * lint attributes.
- *
- * At each node of the ast which can modify lint attributes, all known lint
- * passes are also applied.  Each lint pass is a visit::Visitor implementator.
- * The visitors are constructed via the lint_*() functions below. There are
- * also some lint checks which operate directly on ast nodes (such as
- * @ast::item), and those are organized as check_item_*(). Each visitor added
- * to the lint context is modified to stop once it reaches a node which could
- * alter the lint levels. This means that everything is looked at once and
- * only once by every lint pass.
- *
- * With this all in place, to add a new lint warning, all you need to do is to
- * either invoke `add_lint` on the session at the appropriate time, or write a
- * lint pass in this module which is just an ast visitor. The context used when
- * traversing the ast has a `span_lint` method which only needs the span of the
- * item that's being warned about.
- */
-
 #[deriving(Clone, Eq)]
 pub enum lint {
     ctypes,
@@ -133,12 +120,6 @@ impl Ord for LintSpec {
 
 pub type LintDict = HashMap<&'static str, LintSpec>;
 
-enum AttributedNode<'self> {
-    Item(@ast::item),
-    Method(&'self ast::method),
-    Crate(&'self ast::Crate),
-}
-
 #[deriving(Eq)]
 enum LintSource {
     Node(Span),
@@ -335,90 +316,30 @@ pub fn get_lint_dict() -> LintDict {
     return map;
 }
 
-trait OuterLint {
-    fn process_item(@mut self, i:@ast::item, e:@mut Context);
-    fn process_fn(@mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                  b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context);
-
-    // Returned inner variant will not proceed past subitems.
-    // Supports decomposition of simple lints into subitem-traversing
-    // outer lint visitor and subitem-stopping inner lint visitor.
-    fn inner_variant(@mut self) -> @mut InnerLint;
-}
-
-trait InnerLint {
-    fn descend_item(@mut self, i:&ast::item, e:@mut Context);
-    fn descend_crate(@mut self, crate: &ast::Crate, env: @mut Context);
-    fn descend_fn(@mut self,
-                  function_kind: &visit::fn_kind,
-                  function_declaration: &ast::fn_decl,
-                  function_body: &ast::Block,
-                  sp: Span,
-                  id: ast::NodeId,
-                  env: @mut Context);
-}
-
-impl<V:Visitor<@mut Context>> InnerLint for V {
-    fn descend_item(@mut self, i:&ast::item, e:@mut Context) {
-        visit::walk_item(self, i, e);
-    }
-    fn descend_crate(@mut self, crate: &ast::Crate, env: @mut Context) {
-        visit::walk_crate(self, crate, env);
-    }
-    fn descend_fn(@mut self, fk: &visit::fn_kind, fd: &ast::fn_decl, fb: &ast::Block,
-                  sp: Span, id: ast::NodeId, env: @mut Context) {
-        visit::walk_fn(self, fk, fd, fb, sp, id, env);
-    }
-}
-
-enum AnyVisitor {
-    // This is a pair so every visitor can visit every node. When a lint pass
-    // is registered, another visitor is created which stops at all items
-    // which can alter the attributes of the ast. This "item stopping visitor"
-    // is the second element of the pair, while the original visitor is the
-    // first element. This means that when visiting a node, the original
-    // recursive call can use the original visitor's method, although the
-    // recursing visitor supplied to the method is the item stopping visitor.
-    OldVisitor(@mut OuterLint, @mut InnerLint),
-    NewVisitor(@mut visit::Visitor<()>),
-}
-
 struct Context {
     // All known lint modes (string versions)
     dict: @LintDict,
     // Current levels of each lint warning
-    curr: SmallIntMap<(level, LintSource)>,
+    cur: SmallIntMap<(level, LintSource)>,
     // context we're checking in (used to access fields like sess)
     tcx: ty::ctxt,
-    // Just a simple flag if we're currently recursing into a trait
-    // implementation. This is only used by the lint_missing_doc() pass
-    in_trait_impl: bool,
-    // Another flag for doc lint emissions. Does some parent of the current node
-    // have the doc(hidden) attribute? Treating this as allow(missing_doc) would
-    // play badly with forbid(missing_doc) when it shouldn't.
-    doc_hidden: bool,
+
     // When recursing into an attributed node of the ast which modifies lint
     // levels, this stack keeps track of the previous lint levels of whatever
     // was modified.
     lint_stack: ~[(lint, level, LintSource)],
-    // Each of these visitors represents a lint pass. A number of the lint
-    // attributes are registered by adding a visitor to iterate over the ast.
-    // Others operate directly on @ast::item structures (or similar). Finally,
-    // others still are added to the Session object via `add_lint`, and these
-    // are all passed with the lint_session visitor.
-    visitors: ~[AnyVisitor],
 }
 
 impl Context {
     fn get_level(&self, lint: lint) -> level {
-        match self.curr.find(&(lint as uint)) {
+        match self.cur.find(&(lint as uint)) {
           Some(&(lvl, _)) => lvl,
           None => allow
         }
     }
 
     fn get_source(&self, lint: lint) -> LintSource {
-        match self.curr.find(&(lint as uint)) {
+        match self.cur.find(&(lint as uint)) {
           Some(&(_, src)) => src,
           None => Default
         }
@@ -426,9 +347,9 @@ impl Context {
 
     fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
         if level == allow {
-            self.curr.remove(&(lint as uint));
+            self.cur.remove(&(lint as uint));
         } else {
-            self.curr.insert(lint as uint, (level, src));
+            self.cur.insert(lint as uint, (level, src));
         }
     }
 
@@ -442,7 +363,7 @@ impl Context {
     }
 
     fn span_lint(&self, lint: lint, span: Span, msg: &str) {
-        let (level, src) = match self.curr.find(&(lint as uint)) {
+        let (level, src) = match self.cur.find(&(lint as uint)) {
             None => { return }
             Some(&(warn, src)) => (self.get_level(warnings), src),
             Some(&pair) => pair,
@@ -479,7 +400,8 @@ impl Context {
      * current lint context, call the provided function, then reset the
      * lints in effect to their previous state.
      */
-    fn with_lint_attrs(@mut self, attrs: &[ast::Attribute], f: &fn()) {
+    fn with_lint_attrs(&mut self, attrs: &[ast::Attribute],
+                       f: &fn(&mut Context)) {
         // Parse all of the lint attributes, and then add them all to the
         // current dictionary of lint information. Along the way, keep a history
         // of what we changed so we can roll everything back after invoking the
@@ -513,99 +435,22 @@ impl Context {
             true
         };
 
-        // detect doc(hidden)
-        let mut doc_hidden = do attrs.iter().any |attr| {
-            "doc" == attr.name() &&
-                match attr.meta_item_list() {
-                    Some(l) => attr::contains_name(l, "hidden"),
-                    None    => false // not of the form #[doc(...)]
-                }
-        };
-
-        if doc_hidden && !self.doc_hidden {
-            self.doc_hidden = true;
-        } else {
-            doc_hidden = false;
-        }
-
-        f();
+        f(self);
 
         // rollback
-        if doc_hidden && self.doc_hidden {
-            self.doc_hidden = false;
-        }
         do pushed.times {
             let (lint, lvl, src) = self.lint_stack.pop();
             self.set_level(lint, lvl, src);
         }
     }
 
-    fn add_old_lint(&mut self, v: @mut OuterLint) {
-        self.visitors.push(OldVisitor(v, v.inner_variant()));
-    }
-
-    fn add_lint(&mut self, v: @mut visit::Visitor<()>) {
-        self.visitors.push(NewVisitor(v));
-    }
-
-    fn process(@mut self, n: AttributedNode) {
-        // see comment of the `visitors` field in the struct for why there's a
-        // pair instead of just one visitor.
-        match n {
-            Item(it) => {
-                for visitor in self.visitors.iter() {
-                    match *visitor {
-                        OldVisitor(orig, stopping) => {
-                            orig.process_item(it, self);
-                            stopping.descend_item(it, self);
-                        }
-                        NewVisitor(new_visitor) => {
-                            let new_visitor = new_visitor;
-                            new_visitor.visit_item(it, ());
-                        }
-                    }
-                }
-            }
-            Crate(c) => {
-                for visitor in self.visitors.iter() {
-                    match *visitor {
-                        OldVisitor(_, stopping) => {
-                            stopping.descend_crate(c, self)
-                        }
-                        NewVisitor(new_visitor) => {
-                            let mut new_visitor = new_visitor;
-                            visit::walk_crate(&mut new_visitor, c, ())
-                        }
-                    }
-                }
-            }
-            // Can't use visit::walk_method_helper because the
-            // item_stopping_visitor has overridden visit_fn(&fk_method(... ))
-            // to be a no-op, so manually invoke visit_fn.
-            Method(m) => {
-                for visitor in self.visitors.iter() {
-                    match *visitor {
-                        OldVisitor(orig, stopping) => {
-                            let fk = visit::fk_method(m.ident, &m.generics, m);
-                            orig.process_fn(&fk, &m.decl, &m.body, m.span, m.id, self);
-                            stopping.descend_fn(&fk, &m.decl, &m.body, m.span, m.id, self);
-                        }
-                        NewVisitor(new_visitor) => {
-                            let fk = visit::fk_method(m.ident,
-                                                      &m.generics,
-                                                      m);
-                            let new_visitor = new_visitor;
-                            new_visitor.visit_fn(&fk,
-                                                 &m.decl,
-                                                 &m.body,
-                                                 m.span,
-                                                 m.id,
-                                                 ())
-                        }
-                    }
-                }
-            }
-        }
+    fn visit_ids(&self, f: &fn(&mut ast_util::IdVisitor<Context>)) {
+        let mut v = ast_util::IdVisitor {
+            operation: self,
+            pass_through_items: false,
+            visited_outermost: false,
+        };
+        f(&mut v);
     }
 }
 
@@ -641,124 +486,36 @@ pub fn each_lint(sess: session::Session,
     true
 }
 
-trait SubitemStoppableVisitor : Visitor<@mut Context> {
-    fn is_running_on_items(&mut self) -> bool;
-
-    fn visit_item_action(&mut self, _i:@ast::item, _e:@mut Context) {
-        // fill in with particular action without recursion if desired
-    }
-
-    fn visit_fn_action(&mut self, _fk:&visit::fn_kind, _fd:&ast::fn_decl,
-                       _b:&ast::Block, _s:Span, _n:ast::NodeId, _e:@mut Context) {
-        // fill in with particular action without recursion if desired
-    }
-
-    // The two OVERRIDE methods:
-    //
-    //   OVERRIDE_visit_item
-    //   OVERRIDE_visit_fn
-    //
-    // *must* be included as initial reimplementations of the standard
-    // default behavior of visit_item and visit_fn for every impl of
-    // Visitor, in order to recreate the effect of having two variant
-    // Outer/Inner behaviors of lint visitors.  (See earlier versions
-    // of this module to see what the original encoding was of this
-    // emulated behavior.)
-
-    fn OVERRIDE_visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        if self.is_running_on_items() {
-            self.visit_item_action(i, e);
-            visit::walk_item(self, i, e);
-        }
-    }
-
-    fn OVERRIDE_visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        if self.is_running_on_items() {
-            self.visit_fn_action(fk, fd, b, s, n, e);
-            visit::walk_fn(self, fk, fd, b, s, n, e);
-        } else {
-            match *fk {
-                visit::fk_method(*) => {}
-                _ => {
-                    self.visit_fn_action(fk, fd, b, s, n, e);
-                    visit::walk_fn(self, fk, fd, b, s, n, e);
-                }
-            }
-        }
-    }
-}
-
-struct WhileTrueLintVisitor { stopping_on_items: bool }
-
-
-impl SubitemStoppableVisitor for WhileTrueLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
-
-impl Visitor<@mut Context> for WhileTrueLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_expr(&mut self, e:@ast::Expr, cx:@mut Context) {
-
-            match e.node {
-                ast::ExprWhile(cond, _) => {
-                    match cond.node {
-                        ast::ExprLit(@codemap::Spanned {
-                            node: ast::lit_bool(true), _}) =>
-                        {
-                            cx.span_lint(while_true, e.span,
-                                         "denote infinite loops with \
-                                          loop { ... }");
-                        }
-                        _ => ()
-                    }
+fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
+    match e.node {
+        ast::ExprWhile(cond, _) => {
+            match cond.node {
+                ast::ExprLit(@codemap::Spanned {
+                    node: ast::lit_bool(true), _}) =>
+                {
+                    cx.span_lint(while_true, e.span,
+                                 "denote infinite loops with loop { ... }");
                 }
                 _ => ()
             }
-            visit::walk_expr(self, e, cx);
+        }
+        _ => ()
     }
 }
 
-macro_rules! outer_lint_boilerplate_impl(
-    ($Visitor:ident) =>
-    (
-        impl OuterLint for $Visitor {
-            fn process_item(@mut self, i:@ast::item, e:@mut Context) {
-                self.visit_item_action(i, e);
-            }
-            fn process_fn(@mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                          b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-                self.visit_fn_action(fk, fd, b, s, n, e);
-            }
-            fn inner_variant(@mut self) -> @mut InnerLint {
-                @mut $Visitor { stopping_on_items: true } as @mut InnerLint
+fn check_type_limits(cx: &Context, e: &ast::Expr) {
+    return match e.node {
+        ast::ExprBinary(_, binop, l, r) => {
+            if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
+                cx.span_lint(type_limits, e.span,
+                             "comparison is useless due to type limits");
             }
         }
-    ))
-
-outer_lint_boilerplate_impl!(WhileTrueLintVisitor)
-
-fn lint_while_true() -> @mut OuterLint {
-    @mut WhileTrueLintVisitor{ stopping_on_items: false } as @mut OuterLint
-}
-
-struct TypeLimitsLintVisitor { stopping_on_items: bool }
-
-impl SubitemStoppableVisitor for TypeLimitsLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
+        _ => ()
+    };
 
-impl TypeLimitsLintVisitor {
-    fn is_valid<T:cmp::Ord>(&mut self, binop: ast::BinOp, v: T,
-            min: T, max: T) -> bool {
+    fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
+                            min: T, max: T) -> bool {
         match binop {
             ast::BiLt => v <= max,
             ast::BiLe => v < max,
@@ -769,7 +526,7 @@ impl TypeLimitsLintVisitor {
         }
     }
 
-    fn rev_binop(&mut self, binop: ast::BinOp) -> ast::BinOp {
+    fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
         match binop {
             ast::BiLt => ast::BiGt,
             ast::BiLe => ast::BiGe,
@@ -781,7 +538,7 @@ impl TypeLimitsLintVisitor {
 
     // for int & uint, be conservative with the warnings, so that the
     // warnings are consistent between 32- and 64-bit platforms
-    fn int_ty_range(&mut self, int_ty: ast::int_ty) -> (i64, i64) {
+    fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
         match int_ty {
             ast::ty_i =>    (i64::min_value,        i64::max_value),
             ast::ty_i8 =>   (i8::min_value  as i64, i8::max_value  as i64),
@@ -791,7 +548,7 @@ impl TypeLimitsLintVisitor {
         }
     }
 
-    fn uint_ty_range(&mut self, uint_ty: ast::uint_ty) -> (u64, u64) {
+    fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
         match uint_ty {
             ast::ty_u =>   (u64::min_value,         u64::max_value),
             ast::ty_u8 =>  (u8::min_value   as u64, u8::max_value   as u64),
@@ -801,12 +558,8 @@ impl TypeLimitsLintVisitor {
         }
     }
 
-    fn check_limits(&mut self,
-                    cx: &Context,
-                    binop: ast::BinOp,
-                    l: @ast::Expr,
-                    r: @ast::Expr)
-                    -> bool {
+    fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
+                    l: &ast::Expr, r: &ast::Expr) -> bool {
         let (lit, expr, swap) = match (&l.node, &r.node) {
             (&ast::ExprLit(_), _) => (l, r, true),
             (_, &ast::ExprLit(_)) => (r, l, false),
@@ -814,16 +567,12 @@ impl TypeLimitsLintVisitor {
         };
         // Normalize the binop so that the literal is always on the RHS in
         // the comparison
-        let norm_binop = if swap {
-            self.rev_binop(binop)
-        } else {
-            binop
-        };
-        match ty::get(ty::expr_ty(cx.tcx, expr)).sty {
+        let norm_binop = if swap { rev_binop(binop) } else { binop };
+        match ty::get(ty::expr_ty(tcx, expr)).sty {
             ty::ty_int(int_ty) => {
-                let (min, max) = self.int_ty_range(int_ty);
+                let (min, max) = int_ty_range(int_ty);
                 let lit_val: i64 = match lit.node {
-                    ast::ExprLit(@li) => match li.node {
+                    ast::ExprLit(li) => match li.node {
                         ast::lit_int(v, _) => v,
                         ast::lit_uint(v, _) => v as i64,
                         ast::lit_int_unsuffixed(v) => v,
@@ -831,12 +580,12 @@ impl TypeLimitsLintVisitor {
                     },
                     _ => fail2!()
                 };
-                self.is_valid(norm_binop, lit_val, min, max)
+                is_valid(norm_binop, lit_val, min, max)
             }
             ty::ty_uint(uint_ty) => {
-                let (min, max): (u64, u64) = self.uint_ty_range(uint_ty);
+                let (min, max): (u64, u64) = uint_ty_range(uint_ty);
                 let lit_val: u64 = match lit.node {
-                    ast::ExprLit(@li) => match li.node {
+                    ast::ExprLit(li) => match li.node {
                         ast::lit_int(v, _) => v as u64,
                         ast::lit_uint(v, _) => v,
                         ast::lit_int_unsuffixed(v) => v as u64,
@@ -844,13 +593,13 @@ impl TypeLimitsLintVisitor {
                     },
                     _ => fail2!()
                 };
-                self.is_valid(norm_binop, lit_val, min, max)
+                is_valid(norm_binop, lit_val, min, max)
             }
             _ => true
         }
     }
 
-    fn is_comparison(&mut self, binop: ast::BinOp) -> bool {
+    fn is_comparison(binop: ast::BinOp) -> bool {
         match binop {
             ast::BiEq | ast::BiLt | ast::BiLe |
             ast::BiNe | ast::BiGe | ast::BiGt => true,
@@ -859,38 +608,6 @@ impl TypeLimitsLintVisitor {
     }
 }
 
-impl Visitor<@mut Context> for TypeLimitsLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_expr(&mut self, e:@ast::Expr, cx:@mut Context) {
-
-            match e.node {
-                ast::ExprBinary(_, ref binop, l, r) => {
-                    if self.is_comparison(*binop)
-                        && !self.check_limits(cx, *binop, l, r) {
-                        cx.span_lint(type_limits, e.span,
-                                     "comparison is useless due to type limits");
-                    }
-                }
-                _ => ()
-            }
-            visit::walk_expr(self, e, cx);
-    }
-}
-
-outer_lint_boilerplate_impl!(TypeLimitsLintVisitor)
-
-fn lint_type_limits() -> @mut OuterLint {
-    @mut TypeLimitsLintVisitor{ stopping_on_items: false } as @mut OuterLint
-}
-
 fn check_item_ctypes(cx: &Context, it: &ast::item) {
     fn check_ty(cx: &Context, ty: &ast::Ty) {
         match ty.node {
@@ -936,133 +653,75 @@ fn check_item_ctypes(cx: &Context, it: &ast::item) {
     }
 }
 
-fn check_type_for_lint(cx: &Context, lint: lint, span: Span, ty: ty::t) {
-    if cx.get_level(lint) == allow { return }
-
-    let mut n_box = 0;
-    let mut n_uniq = 0;
-    ty::fold_ty(cx.tcx, ty, |t| {
-        match ty::get(t).sty {
-          ty::ty_box(_) => n_box += 1,
-          ty::ty_uniq(_) => n_uniq += 1,
-          _ => ()
-        };
-        t
-    });
-
-    if n_uniq > 0 && lint != managed_heap_memory {
-        let s = ty_to_str(cx.tcx, ty);
-        let m = ~"type uses owned (~ type) pointers: " + s;
-        cx.span_lint(lint, span, m);
-    }
+fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
+    let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
+    for &lint in xs.iter() {
+        if cx.get_level(lint) == allow { continue }
+
+        let mut n_box = 0;
+        let mut n_uniq = 0;
+        ty::fold_ty(cx.tcx, ty, |t| {
+            match ty::get(t).sty {
+              ty::ty_box(_) => n_box += 1,
+              ty::ty_uniq(_) => n_uniq += 1,
+              _ => ()
+            };
+            t
+        });
 
-    if n_box > 0 && lint != owned_heap_memory {
-        let s = ty_to_str(cx.tcx, ty);
-        let m = ~"type uses managed (@ type) pointers: " + s;
-        cx.span_lint(lint, span, m);
-    }
-}
+        if n_uniq > 0 && lint != managed_heap_memory {
+            let s = ty_to_str(cx.tcx, ty);
+            let m = format!("type uses owned (~ type) pointers: {}", s);
+            cx.span_lint(lint, span, m);
+        }
 
-fn check_type(cx: &Context, span: Span, ty: ty::t) {
-    let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
-    for lint in xs.iter() {
-        check_type_for_lint(cx, *lint, span, ty);
+        if n_box > 0 && lint != owned_heap_memory {
+            let s = ty_to_str(cx.tcx, ty);
+            let m = format!("type uses managed (@ type) pointers: {}", s);
+            cx.span_lint(lint, span, m);
+        }
     }
 }
 
-fn check_item_heap(cx: &Context, it: &ast::item) {
+fn check_heap_item(cx: &Context, it: &ast::item) {
     match it.node {
-      ast::item_fn(*) |
-      ast::item_ty(*) |
-      ast::item_enum(*) |
-      ast::item_struct(*) => check_type(cx, it.span,
-                                        ty::node_id_to_type(cx.tcx,
-                                                            it.id)),
-      _ => ()
+        ast::item_fn(*) |
+        ast::item_ty(*) |
+        ast::item_enum(*) |
+        ast::item_struct(*) => check_heap_type(cx, it.span,
+                                               ty::node_id_to_type(cx.tcx,
+                                                                   it.id)),
+        _ => ()
     }
 
     // If it's a struct, we also have to check the fields' types
     match it.node {
         ast::item_struct(struct_def, _) => {
             for struct_field in struct_def.fields.iter() {
-                check_type(cx, struct_field.span,
-                           ty::node_id_to_type(cx.tcx,
-                                               struct_field.node.id));
+                check_heap_type(cx, struct_field.span,
+                                ty::node_id_to_type(cx.tcx,
+                                                    struct_field.node.id));
             }
         }
         _ => ()
     }
 }
 
-struct HeapLintVisitor { stopping_on_items: bool }
-
-impl SubitemStoppableVisitor for HeapLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
-
-impl Visitor<@mut Context> for HeapLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_expr(&mut self, e:@ast::Expr, cx:@mut Context) {
-            let ty = ty::expr_ty(cx.tcx, e);
-            check_type(cx, e.span, ty);
-            visit::walk_expr(self, e, cx);
-    }
-}
-
-outer_lint_boilerplate_impl!(HeapLintVisitor)
-
-fn lint_heap() -> @mut OuterLint {
-    @mut HeapLintVisitor { stopping_on_items: false } as @mut OuterLint
-}
-
-struct PathStatementLintVisitor {
-    stopping_on_items: bool
-}
-
-impl SubitemStoppableVisitor for PathStatementLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
+fn check_heap_expr(cx: &Context, e: &ast::Expr) {
+    let ty = ty::expr_ty(cx.tcx, e);
+    check_heap_type(cx, e.span, ty);
 }
 
-impl Visitor<@mut Context> for PathStatementLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_stmt(&mut self, s:@ast::Stmt, cx:@mut Context) {
-            match s.node {
-                ast::StmtSemi(
-                    @ast::Expr { node: ast::ExprPath(_), _ },
-                    _
-                ) => {
-                    cx.span_lint(path_statement, s.span,
-                                 "path statement with no effect");
-                }
-                _ => ()
-            }
-            visit::walk_stmt(self, s, cx);
-
+fn check_path_statement(cx: &Context, s: &ast::Stmt) {
+    match s.node {
+        ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), _ }, _) => {
+            cx.span_lint(path_statement, s.span,
+                         "path statement with no effect");
+        }
+        _ => ()
     }
 }
 
-outer_lint_boilerplate_impl!(PathStatementLintVisitor)
-
-fn lint_path_statement() -> @mut OuterLint {
-    @mut PathStatementLintVisitor{ stopping_on_items: false } as @mut OuterLint
-}
-
 fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
     fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
         let ident = cx.sess.str_of(ident);
@@ -1134,549 +793,393 @@ fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
     }
 }
 
-struct UnusedUnsafeLintVisitor { stopping_on_items: bool }
-
-impl SubitemStoppableVisitor for UnusedUnsafeLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
-
-impl Visitor<@mut Context> for UnusedUnsafeLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_expr(&mut self, e:@ast::Expr, cx:@mut Context) {
-
-            match e.node {
-                // Don't warn about generated blocks, that'll just pollute the
-                // output.
-                ast::ExprBlock(ref blk) => {
-                    if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
-                       !cx.tcx.used_unsafe.contains(&blk.id) {
-                        cx.span_lint(unused_unsafe, blk.span,
-                                     "unnecessary `unsafe` block");
-                    }
-                }
-                _ => ()
-            }
-            visit::walk_expr(self, e, cx);
-    }
-}
-
-outer_lint_boilerplate_impl!(UnusedUnsafeLintVisitor)
-
-fn lint_unused_unsafe() -> @mut OuterLint {
-    @mut UnusedUnsafeLintVisitor{ stopping_on_items: false } as @mut OuterLint
-}
-
-struct UnusedMutLintVisitor { stopping_on_items: bool }
-
-impl UnusedMutLintVisitor {
-    fn check_pat(&mut self, cx: &Context, p: @ast::Pat) {
-        let mut used = false;
-        let mut bindings = 0;
-        do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
-            used = used || cx.tcx.used_mut_nodes.contains(&id);
-            bindings += 1;
-        }
-        if !used {
-            let msg = if bindings == 1 {
-                "variable does not need to be mutable"
-            } else {
-                "variables do not need to be mutable"
-            };
-            cx.span_lint(unused_mut, p.span, msg);
-        }
-    }
-
-    fn visit_fn_decl(&mut self, cx: &Context, fd: &ast::fn_decl) {
-        for arg in fd.inputs.iter() {
-            if arg.is_mutbl {
-                self.check_pat(cx, arg.pat);
+fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
+    match e.node {
+        // Don't warn about generated blocks, that'll just pollute the
+        // output.
+        ast::ExprBlock(ref blk) => {
+            if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
+                !cx.tcx.used_unsafe.contains(&blk.id) {
+                cx.span_lint(unused_unsafe, blk.span,
+                             "unnecessary `unsafe` block");
             }
         }
+        _ => ()
     }
 }
 
-impl SubitemStoppableVisitor for UnusedMutLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-
-    fn visit_fn_action(&mut self, _a:&visit::fn_kind, fd:&ast::fn_decl,
-                       _b:&ast::Block, _c:Span, _d:ast::NodeId, cx:@mut Context) {
-            self.visit_fn_decl(cx, fd);
-    }
-}
-
-impl Visitor<@mut Context> for UnusedMutLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
+fn check_unused_mut_pat(cx: &Context, p: @ast::Pat) {
+    let mut used = false;
+    let mut bindings = 0;
+    do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
+        used = used || cx.tcx.used_mut_nodes.contains(&id);
+        bindings += 1;
     }
-
-
-    fn visit_local(&mut self, l:@ast::Local, cx:@mut Context) {
-            if l.is_mutbl {
-                self.check_pat(cx, l.pat);
-            }
-            visit::walk_local(self, l, cx);
-    }
-
-    fn visit_ty_method(&mut self, tm:&ast::TypeMethod, cx:@mut Context) {
-            self.visit_fn_decl(cx, &tm.decl);
-            visit::walk_ty_method(self, tm, cx);
-    }
-
-    fn visit_trait_method(&mut self, tm:&ast::trait_method, cx:@mut Context) {
-            match *tm {
-                ast::required(ref tm) => self.visit_fn_decl(cx, &tm.decl),
-                ast::provided(m) => self.visit_fn_decl(cx, &m.decl)
-            }
-            visit::walk_trait_method(self, tm, cx);
+    if !used {
+        let msg = if bindings == 1 {
+            "variable does not need to be mutable"
+        } else {
+            "variables do not need to be mutable"
+        };
+        cx.span_lint(unused_mut, p.span, msg);
     }
 }
 
-outer_lint_boilerplate_impl!(UnusedMutLintVisitor)
-
-fn lint_unused_mut() -> @mut OuterLint {
-    @mut UnusedMutLintVisitor{ stopping_on_items: false } as @mut OuterLint
-}
-
-struct LintReportingIdVisitor {
-    cx: @mut Context,
-}
-
-impl ast_util::IdVisitingOperation for LintReportingIdVisitor {
-    fn visit_id(&self, id: ast::NodeId) {
-        match self.cx.tcx.sess.lints.pop(&id) {
-            None => {}
-            Some(l) => {
-                for (lint, span, msg) in l.move_iter() {
-                    self.cx.span_lint(lint, span, msg)
-                }
-            }
+fn check_unused_mut_fn_decl(cx: &Context, fd: &ast::fn_decl) {
+    for arg in fd.inputs.iter() {
+        if arg.is_mutbl {
+            check_unused_mut_pat(cx, arg.pat);
         }
     }
 }
 
-fn lint_session(cx: @mut Context) -> @mut visit::Visitor<()> {
-    ast_util::id_visitor(@LintReportingIdVisitor {
-        cx: cx,
-    } as @ast_util::IdVisitingOperation, false)
-}
-
-struct UnnecessaryAllocationLintVisitor { stopping_on_items: bool }
-
-impl SubitemStoppableVisitor for UnnecessaryAllocationLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
-
-impl Visitor<@mut Context> for UnnecessaryAllocationLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
-    }
-
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
-
-    fn visit_expr(&mut self, e:@ast::Expr, cx:@mut Context) {
-            self.check(cx, e);
-            visit::walk_expr(self, e, cx);
-    }
-}
-
-impl UnnecessaryAllocationLintVisitor {
+fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
     // Warn if string and vector literals with sigils are immediately borrowed.
     // Those can have the sigil removed.
-    fn check(&mut self, cx: &Context, e: &ast::Expr) {
-        match e.node {
-            ast::ExprVstore(e2, ast::ExprVstoreUniq) |
-            ast::ExprVstore(e2, ast::ExprVstoreBox) => {
-                match e2.node {
-                    ast::ExprLit(@codemap::Spanned{
-                            node: ast::lit_str(*), _}) |
-                    ast::ExprVec(*) => {}
-                    _ => return
-                }
+    match e.node {
+        ast::ExprVstore(e2, ast::ExprVstoreUniq) |
+        ast::ExprVstore(e2, ast::ExprVstoreBox) => {
+            match e2.node {
+                ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
+                ast::ExprVec(*) => {}
+                _ => return
             }
-
-            _ => return
         }
 
-        match cx.tcx.adjustments.find_copy(&e.id) {
-            Some(@ty::AutoDerefRef(ty::AutoDerefRef {
-                autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
-                cx.span_lint(unnecessary_allocation,
-                             e.span, "unnecessary allocation, the sigil can be \
-                                      removed");
-            }
-
-            _ => ()
-        }
+        _ => return
     }
-}
 
-outer_lint_boilerplate_impl!(UnnecessaryAllocationLintVisitor)
+    match cx.tcx.adjustments.find_copy(&e.id) {
+        Some(@ty::AutoDerefRef(ty::AutoDerefRef {
+            autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
+            cx.span_lint(unnecessary_allocation, e.span,
+                         "unnecessary allocation, the sigil can be removed");
+        }
 
-fn lint_unnecessary_allocations() -> @mut OuterLint {
-    @mut UnnecessaryAllocationLintVisitor{ stopping_on_items: false } as @mut OuterLint
+        _ => ()
+    }
 }
 
-struct MissingDocLintVisitor { stopping_on_items: bool }
+struct MissingDocLintVisitor(ty::ctxt);
 
 impl MissingDocLintVisitor {
-    fn check_attrs(&mut self,
-                   cx: @mut Context,
-                   attrs: &[ast::Attribute],
-                   sp: Span,
-                   msg: &str) {
-        // If we're building a test harness, then warning about documentation is
-        // probably not really relevant right now
-        if cx.tcx.sess.opts.test { return }
-        // If we have doc(hidden), nothing to do
-        if cx.doc_hidden { return }
-        // If we're documented, nothing to do
-        if attrs.iter().any(|a| a.node.is_sugared_doc) { return }
-
-        // otherwise, warn!
-        cx.span_lint(missing_doc, sp, msg);
+    fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
+                   sp: Span, msg: ~str) {
+        if !attrs.iter().any(|a| a.node.is_sugared_doc) {
+            self.sess.add_lint(missing_doc, id, sp, msg);
+        }
     }
 
-    fn check_struct(&mut self, cx: @mut Context, sdef: @ast::struct_def) {
-                        for field in sdef.fields.iter() {
-        match field.node.kind {
-            ast::named_field(_, vis) if vis != ast::private => {
-                self.check_attrs(cx, field.node.attrs, field.span,
-                        "missing documentation for a field");
+    fn check_struct(&self, sdef: &ast::struct_def) {
+        for field in sdef.fields.iter() {
+            match field.node.kind {
+                ast::named_field(_, vis) if vis != ast::private => {
+                    self.check_attrs(field.node.attrs, field.node.id, field.span,
+                                     ~"missing documentation for a field");
                 }
                 ast::unnamed_field | ast::named_field(*) => {}
             }
         }
     }
-}
 
-impl Visitor<@mut Context> for MissingDocLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
+    fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
+        do attrs.iter().any |attr| {
+            "doc" == attr.name() &&
+                match attr.meta_item_list() {
+                    Some(l) => attr::contains_name(l, "hidden"),
+                    None    => false // not of the form #[doc(...)]
+                }
+        }
     }
+}
 
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
-    }
+impl Visitor<()> for MissingDocLintVisitor {
+    fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
+        if self.doc_hidden(m.attrs) { return }
 
-    fn visit_ty_method(&mut self, m:&ast::TypeMethod, cx:@mut Context) {
-            // All ty_method objects are linted about because they're part of a
-            // trait (no visibility)
-            self.check_attrs(cx, m.attrs, m.span,
-                        "missing documentation for a method");
-            visit::walk_ty_method(self, m, cx);
+        // All ty_method objects are linted about because they're part of a
+        // trait (no visibility)
+        self.check_attrs(m.attrs, m.id, m.span,
+                         ~"missing documentation for a method");
+        visit::walk_ty_method(self, m, ());
     }
-}
 
-impl SubitemStoppableVisitor for MissingDocLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-
-    fn visit_fn_action(&mut self, fk:&visit::fn_kind, _d:&ast::fn_decl,
-                       _b:&ast::Block, sp:Span, _id:ast::NodeId, cx:@mut Context) {
-
-            // Only warn about explicitly public methods. Soon implicit
-            // public-ness will hopefully be going away.
-            match *fk {
-                visit::fk_method(_, _, m) if m.vis == ast::public => {
-                    // If we're in a trait implementation, no need to duplicate
-                    // documentation
-                    if !cx.in_trait_impl {
-                        self.check_attrs(cx, m.attrs, sp,
-                                    "missing documentation for a method");
-                    }
+    fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
+                b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
+        // Only warn about explicitly public methods.
+        match *fk {
+            visit::fk_method(_, _, m) => {
+                if self.doc_hidden(m.attrs) {
+                    return;
+                }
+                // If we're in a trait implementation, no need to duplicate
+                // documentation
+                if m.vis == ast::public {
+                    self.check_attrs(m.attrs, id, sp,
+                                     ~"missing documentation for a method");
                 }
-
-                _ => {}
             }
+            _ => {}
+        }
+        visit::walk_fn(self, fk, d, b, sp, id, ());
     }
 
-    fn visit_item_action(&mut self, it:@ast::item, cx:@mut Context) {
-            if it.vis != ast::public {
-                return;
+    fn visit_item(&mut self, it: @ast::item, _: ()) {
+        // If we're building a test harness, then warning about documentation is
+        // probably not really relevant right now
+        if self.sess.opts.test { return }
+        if self.doc_hidden(it.attrs) { return }
+
+        match it.node {
+            ast::item_struct(sdef, _) if it.vis == ast::public => {
+                self.check_attrs(it.attrs, it.id, it.span,
+                                 ~"missing documentation for a struct");
+                self.check_struct(sdef);
             }
 
-            match it.node {
-                // Go ahead and match the fields here instead of using
-                // visit_struct_field while we have access to the enclosing
-                // struct's visibility
-                ast::item_struct(sdef, _) => {
-                    self.check_attrs(cx, it.attrs, it.span,
-                                "missing documentation for a struct");
-                    self.check_struct(cx, sdef);
-                }
-
-                ast::item_trait(*) => {
-                    self.check_attrs(cx, it.attrs, it.span,
-                                "missing documentation for a trait");
-                }
+            // Skip implementations because they inherit documentation from the
+            // trait (which was already linted)
+            ast::item_impl(_, Some(*), _, _) => return,
 
-                ast::item_fn(*) => {
-                    self.check_attrs(cx, it.attrs, it.span,
-                                "missing documentation for a function");
-                }
+            ast::item_trait(*) if it.vis == ast::public => {
+                self.check_attrs(it.attrs, it.id, it.span,
+                                 ~"missing documentation for a trait");
+            }
 
-                ast::item_enum(ref edef, _) => {
-                    self.check_attrs(cx, it.attrs, it.span,
-                                "missing documentation for an enum");
-                    for variant in edef.variants.iter() {
-                        if variant.node.vis == ast::private {
-                            continue;
-                        }
+            ast::item_fn(*) if it.vis == ast::public => {
+                self.check_attrs(it.attrs, it.id, it.span,
+                                 ~"missing documentation for a function");
+            }
 
-                        self.check_attrs(cx, variant.node.attrs, variant.span,
-                                        "missing documentation for a variant");
-                        match variant.node.kind {
-                            ast::struct_variant_kind(sdef) => {
-                                self.check_struct(cx, sdef);
-                            }
-                            _ => ()
+            ast::item_enum(ref edef, _) if it.vis == ast::public => {
+                self.check_attrs(it.attrs, it.id, it.span,
+                                 ~"missing documentation for an enum");
+                for variant in edef.variants.iter() {
+                    if variant.node.vis == ast::private { continue; }
+
+                    self.check_attrs(variant.node.attrs, variant.node.id,
+                                     variant.span,
+                                     ~"missing documentation for a variant");
+                    match variant.node.kind {
+                        ast::struct_variant_kind(sdef) => {
+                            self.check_struct(sdef);
                         }
+                        _ => ()
                     }
                 }
-
-                _ => {}
             }
-    }
-}
-
-outer_lint_boilerplate_impl!(MissingDocLintVisitor)
 
-fn lint_missing_doc() -> @mut OuterLint {
-    @mut MissingDocLintVisitor { stopping_on_items: false } as @mut OuterLint
+            _ => {}
+        }
+        visit::walk_item(self, it, ());
+    }
 }
 
 /// Checks for use of items with #[deprecated], #[experimental] and
 /// #[unstable] (or none of them) attributes.
-struct StabilityLintVisitor { stopping_on_items: bool }
-
-impl StabilityLintVisitor {
-    fn handle_def(&mut self, sp: Span, def: &ast::Def, cx: @mut Context) {
-        let id = ast_util::def_id_of_def(*def);
-
-        let stability = if ast_util::is_local(id) {
-            // this crate
-            match cx.tcx.items.find(&id.node) {
-                Some(ast_node) => {
-                    let s = do ast_node.with_attrs |attrs| {
-                        do attrs.map_move |a| {
-                            attr::find_stability(a.iter().map(|a| a.meta()))
-                        }
-                    };
-                    match s {
-                        Some(s) => s,
-
-                        // no possibility of having attributes
-                        // (e.g. it's a local variable), so just
-                        // ignore it.
-                        None => return
+fn check_stability(cx: &Context, e: &ast::Expr) {
+    let def = match e.node {
+        ast::ExprMethodCall(*) |
+        ast::ExprPath(*) |
+        ast::ExprStruct(*) => {
+            match cx.tcx.def_map.find(&e.id) {
+                Some(&def) => def,
+                None => return
+            }
+        }
+        _ => return
+    };
+
+    let id = ast_util::def_id_of_def(def);
+
+    let stability = if ast_util::is_local(id) {
+        // this crate
+        match cx.tcx.items.find(&id.node) {
+            Some(ast_node) => {
+                let s = do ast_node.with_attrs |attrs| {
+                    do attrs.map_move |a| {
+                        attr::find_stability(a.iter().map(|a| a.meta()))
                     }
+                };
+                match s {
+                    Some(s) => s,
+
+                    // no possibility of having attributes
+                    // (e.g. it's a local variable), so just
+                    // ignore it.
+                    None => return
                 }
-                _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
             }
-        } else {
-            // cross-crate
-
-            let mut s = None;
-            // run through all the attributes and take the first
-            // stability one.
-            do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
-                if s.is_none() {
-                    s = attr::find_stability(meta_items.move_iter())
-                }
+            _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
+        }
+    } else {
+        // cross-crate
+
+        let mut s = None;
+        // run through all the attributes and take the first
+        // stability one.
+        do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
+            if s.is_none() {
+                s = attr::find_stability(meta_items.move_iter())
             }
-            s
-        };
+        }
+        s
+    };
 
-        let (lint, label) = match stability {
-            // no stability attributes == Unstable
-            None => (unstable, "unmarked"),
-            Some(attr::Stability { level: attr::Unstable, _ }) => (unstable, "unstable"),
-            Some(attr::Stability { level: attr::Experimental, _ }) => {
-                (experimental, "experimental")
-            }
-            Some(attr::Stability { level: attr::Deprecated, _ }) => (deprecated, "deprecated"),
-            _ => return
-        };
+    let (lint, label) = match stability {
+        // no stability attributes == Unstable
+        None => (unstable, "unmarked"),
+        Some(attr::Stability { level: attr::Unstable, _ }) =>
+                (unstable, "unstable"),
+        Some(attr::Stability { level: attr::Experimental, _ }) =>
+                (experimental, "experimental"),
+        Some(attr::Stability { level: attr::Deprecated, _ }) =>
+                (deprecated, "deprecated"),
+        _ => return
+    };
 
-        let msg = match stability {
-            Some(attr::Stability { text: Some(ref s), _ }) => {
-                format!("use of {} item: {}", label, *s)
-            }
-            _ => format!("use of {} item", label)
-        };
+    let msg = match stability {
+        Some(attr::Stability { text: Some(ref s), _ }) => {
+            format!("use of {} item: {}", label, *s)
+        }
+        _ => format!("use of {} item", label)
+    };
 
-        cx.span_lint(lint, sp, msg);
-    }
+    cx.span_lint(lint, e.span, msg);
 }
 
-impl SubitemStoppableVisitor for StabilityLintVisitor {
-    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
-}
+impl Visitor<()> for Context {
+    fn visit_item(&mut self, it: @ast::item, _: ()) {
+        do self.with_lint_attrs(it.attrs) |cx| {
+            check_item_ctypes(cx, it);
+            check_item_non_camel_case_types(cx, it);
+            check_item_non_uppercase_statics(cx, it);
+            check_heap_item(cx, it);
+
+            do cx.visit_ids |v| {
+                v.visit_item(it, ());
+            }
 
-impl Visitor<@mut Context> for StabilityLintVisitor {
-    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
-        self.OVERRIDE_visit_item(i, e);
+            visit::walk_item(cx, it, ());
+        }
     }
 
-    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
-                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
-        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
+    fn visit_pat(&mut self, p: @ast::Pat, _: ()) {
+        check_pat_non_uppercase_statics(self, p);
+        visit::walk_pat(self, p, ());
     }
 
-    fn visit_expr(&mut self, ex: @ast::Expr, cx: @mut Context) {
-        match ex.node {
-            ast::ExprMethodCall(*) |
-            ast::ExprPath(*) |
-            ast::ExprStruct(*) => {
-                match cx.tcx.def_map.find(&ex.id) {
-                    Some(def) => self.handle_def(ex.span, def, cx),
-                    None => {}
-                }
-            }
-            _ => {}
-        }
+    fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
+        check_while_true_expr(self, e);
+        check_stability(self, e);
+        check_unused_unsafe(self, e);
+        check_unnecessary_allocation(self, e);
+        check_heap_expr(self, e);
+        check_type_limits(self, e);
 
-        visit::walk_expr(self, ex, cx)
+        visit::walk_expr(self, e, ());
     }
-}
 
-outer_lint_boilerplate_impl!(StabilityLintVisitor)
+    fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
+        check_path_statement(self, s);
 
-fn lint_stability() -> @mut OuterLint {
-    @mut StabilityLintVisitor { stopping_on_items: false } as @mut OuterLint
-}
+        visit::walk_stmt(self, s, ());
+    }
 
-struct LintCheckVisitor;
+    fn visit_ty_method(&mut self, tm: &ast::TypeMethod, _: ()) {
+        check_unused_mut_fn_decl(self, &tm.decl);
+        visit::walk_ty_method(self, tm, ());
+    }
 
-impl Visitor<@mut Context> for LintCheckVisitor {
+    fn visit_trait_method(&mut self, tm: &ast::trait_method, _: ()) {
+        match *tm {
+            ast::required(ref m) => check_unused_mut_fn_decl(self, &m.decl),
+            ast::provided(ref m) => check_unused_mut_fn_decl(self, &m.decl)
+        }
+        visit::walk_trait_method(self, tm, ());
+    }
 
-    fn visit_pat(&mut self, p:@ast::Pat, cx: @mut Context) {
-        check_pat_non_uppercase_statics(cx, p);
-        visit::walk_pat(self, p, cx);
+    fn visit_local(&mut self, l: @ast::Local, _: ()) {
+        if l.is_mutbl {
+            check_unused_mut_pat(self, l.pat);
+        }
+        visit::walk_local(self, l, ());
     }
 
-    fn visit_item(&mut self, it:@ast::item, cx: @mut Context) {
+    fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
+                body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
+        let recurse = |this: &mut Context| {
+            check_unused_mut_fn_decl(this, decl);
+            visit::walk_fn(this, fk, decl, body, span, id, ());
+        };
 
-                do cx.with_lint_attrs(it.attrs) {
-                    match it.node {
-                        ast::item_impl(_, Some(*), _, _) => {
-                            cx.in_trait_impl = true;
-                        }
-                        _ => {}
+        match *fk {
+            visit::fk_method(_, _, m) => {
+                do self.with_lint_attrs(m.attrs) |cx| {
+                    do cx.visit_ids |v| {
+                        v.visit_fn(fk, decl, body, span, id, ());
                     }
-                    check_item_ctypes(cx, it);
-                    check_item_non_camel_case_types(cx, it);
-                    check_item_non_uppercase_statics(cx, it);
-                    check_item_heap(cx, it);
-
-                    cx.process(Item(it));
-                    visit::walk_item(self, it, cx);
-                    cx.in_trait_impl = false;
+                    recurse(cx);
                 }
+            }
+            _ => recurse(self),
+        }
     }
+}
 
-    fn visit_fn(&mut self, fk:&visit::fn_kind, decl:&ast::fn_decl,
-                body:&ast::Block, span:Span, id:ast::NodeId, cx:@mut Context) {
-
-                match *fk {
-                    visit::fk_method(_, _, m) => {
-                        do cx.with_lint_attrs(m.attrs) {
-                            cx.process(Method(m));
-                            visit::walk_fn(self,
-                                               fk,
-                                               decl,
-                                               body,
-                                               span,
-                                               id,
-                                               cx);
-                        }
-                    }
-                    _ => {
-                        visit::walk_fn(self,
-                                           fk,
-                                           decl,
-                                           body,
-                                           span,
-                                           id,
-                                           cx);
-                    }
+impl ast_util::IdVisitingOperation for Context {
+    fn visit_id(&self, id: ast::NodeId) {
+        match self.tcx.sess.lints.pop(&id) {
+            None => {}
+            Some(l) => {
+                for (lint, span, msg) in l.move_iter() {
+                    self.span_lint(lint, span, msg)
                 }
+            }
+        }
     }
 }
 
 pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
-    let cx = @mut Context {
+    // This visitor contains more state than is currently maintained in Context,
+    // and there's no reason for the Context to keep track of this information
+    // really
+    let mut dox = MissingDocLintVisitor(tcx);
+    visit::walk_crate(&mut dox, crate, ());
+
+    let mut cx = Context {
         dict: @get_lint_dict(),
-        curr: SmallIntMap::new(),
+        cur: SmallIntMap::new(),
         tcx: tcx,
         lint_stack: ~[],
-        visitors: ~[],
-        in_trait_impl: false,
-        doc_hidden: false,
     };
 
-    // Install defaults.
+    // Install default lint levels, followed by the command line levels, and
+    // then actually visit the whole crate.
     for (_, spec) in cx.dict.iter() {
         cx.set_level(spec.lint, spec.default, Default);
     }
-
-    // Install command-line options, overriding defaults.
     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
         cx.set_level(lint, level, CommandLine);
     }
-
-    // Register each of the lint passes with the context
-    cx.add_old_lint(lint_while_true());
-    cx.add_old_lint(lint_path_statement());
-    cx.add_old_lint(lint_heap());
-    cx.add_old_lint(lint_type_limits());
-    cx.add_old_lint(lint_unused_unsafe());
-    cx.add_old_lint(lint_unused_mut());
-    cx.add_old_lint(lint_unnecessary_allocations());
-    cx.add_old_lint(lint_missing_doc());
-    cx.add_old_lint(lint_stability());
-    cx.add_lint(lint_session(cx));
-
-    // Actually perform the lint checks (iterating the ast)
-    do cx.with_lint_attrs(crate.attrs) {
-        cx.process(Crate(crate));
-
-        let mut visitor = LintCheckVisitor;
-
-        visit::walk_crate(&mut visitor, crate, cx);
+    do cx.with_lint_attrs(crate.attrs) |cx| {
+        do cx.visit_ids |v| {
+            v.visited_outermost = true;
+            visit::walk_crate(v, crate, ());
+        }
+        visit::walk_crate(cx, crate, ());
     }
 
     // If we missed any lints added to the session, then there's a bug somewhere
     // in the iteration code.
     for (id, v) in tcx.sess.lints.iter() {
-        for t in v.iter() {
-            match *t {
-                (lint, span, ref msg) =>
-                    tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: \
-                                                  {}",
-                                                 lint,
-                                                 ast_map::node_id_to_str(
-                                                 tcx.items,
-                                                 *id,
-                                                 token::get_ident_interner()),
-                                                 *msg))
-            }
+        for &(lint, span, ref msg) in v.iter() {
+            tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
+                                            lint,
+                                            ast_map::node_id_to_str(tcx.items,
+                                                *id,
+                                                token::get_ident_interner()),
+                                            *msg))
         }
     }
 
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index e697368009c..2c01f246c5f 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -397,27 +397,17 @@ impl id_range {
     }
 }
 
-pub fn id_visitor(operation: @IdVisitingOperation, pass_through_items: bool)
-                  -> @mut Visitor<()> {
-    let visitor = @mut IdVisitor {
-        operation: operation,
-        pass_through_items: pass_through_items,
-        visited_outermost: false,
-    };
-    visitor as @mut Visitor<()>
-}
-
 pub trait IdVisitingOperation {
     fn visit_id(&self, node_id: NodeId);
 }
 
-pub struct IdVisitor {
-    operation: @IdVisitingOperation,
+pub struct IdVisitor<'self, O> {
+    operation: &'self O,
     pass_through_items: bool,
     visited_outermost: bool,
 }
 
-impl IdVisitor {
+impl<'self, O: IdVisitingOperation> IdVisitor<'self, O> {
     fn visit_generics_helper(&self, generics: &Generics) {
         for type_parameter in generics.ty_params.iter() {
             self.operation.visit_id(type_parameter.id)
@@ -428,7 +418,7 @@ impl IdVisitor {
     }
 }
 
-impl Visitor<()> for IdVisitor {
+impl<'self, O: IdVisitingOperation> Visitor<()> for IdVisitor<'self, O> {
     fn visit_mod(&mut self,
                  module: &_mod,
                  _: Span,
@@ -601,10 +591,18 @@ impl Visitor<()> for IdVisitor {
         struct_def.ctor_id.map(|&ctor_id| self.operation.visit_id(ctor_id));
         visit::walk_struct_def(self, struct_def, ident, generics, id, ());
     }
+
+    fn visit_trait_method(&mut self, tm: &ast::trait_method, _: ()) {
+        match *tm {
+            ast::required(ref m) => self.operation.visit_id(m.id),
+            ast::provided(ref m) => self.operation.visit_id(m.id),
+        }
+        visit::walk_trait_method(self, tm, ());
+    }
 }
 
-pub fn visit_ids_for_inlined_item(item: &inlined_item,
-                                  operation: @IdVisitingOperation) {
+pub fn visit_ids_for_inlined_item<O: IdVisitingOperation>(item: &inlined_item,
+                                                          operation: &O) {
     let mut id_visitor = IdVisitor {
         operation: operation,
         pass_through_items: true,
@@ -623,18 +621,14 @@ impl IdVisitingOperation for IdRangeComputingVisitor {
     }
 }
 
-pub fn compute_id_range(visit_ids_fn: &fn(@IdVisitingOperation)) -> id_range {
+pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range {
     let result = @mut id_range::max();
-    visit_ids_fn(@IdRangeComputingVisitor {
+    visit_ids_for_inlined_item(item, &IdRangeComputingVisitor {
         result: result,
-    } as @IdVisitingOperation);
+    });
     *result
 }
 
-pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range {
-    compute_id_range(|f| visit_ids_for_inlined_item(item, f))
-}
-
 pub fn is_item_impl(item: @ast::item) -> bool {
     match item.node {
        item_impl(*) => true,