From c0388cd69166f22c818eab21c40c0a37cc1d70a8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 2 Oct 2013 11:29:29 -0700 Subject: [PATCH] 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 --- src/librustc/middle/astencode.rs | 4 +- src/librustc/middle/lint.rs | 1305 +++++++++--------------------- src/libsyntax/ast_util.rs | 40 +- 3 files changed, 423 insertions(+), 926 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 902f90eb7cd5c..b6a782c09b79d 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 d1c88fb7d646a..a44223fba83a8 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> 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)) { + 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(&mut self, binop: ast::BinOp, v: T, - min: T, max: T) -> bool { + fn is_valid(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 e697368009caa..2c01f246c5f3d 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(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,