Prevent refcount cycles during linting

Shaves off ~600MB of memory while compiling rustc
This commit is contained in:
Alex Crichton 2013-05-27 22:21:29 -05:00
parent e6a838d051
commit ae7df57c5a
2 changed files with 192 additions and 160 deletions

View File

@ -299,7 +299,7 @@ struct Context {
// 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: ~[visit::vt<()>],
visitors: ~[visit::vt<@mut Context>],
}
impl Context {
@ -416,20 +416,20 @@ impl Context {
}
}
fn add_lint(&mut self, v: visit::vt<()>) {
fn add_lint(&mut self, v: visit::vt<@mut Context>) {
self.visitors.push(item_stopping_visitor(v));
}
fn process(&self, n: AttributedNode) {
fn process(@mut self, n: AttributedNode) {
match n {
Item(it) => {
for self.visitors.each |v| {
visit::visit_item(it, (), *v);
visit::visit_item(it, self, *v);
}
}
Crate(c) => {
for self.visitors.each |v| {
visit::visit_crate(c, (), *v);
visit::visit_crate(c, self, *v);
}
}
// Can't use visit::visit_method_helper because the
@ -439,7 +439,7 @@ impl Context {
let fk = visit::fk_method(copy m.ident, &m.generics, m);
for self.visitors.each |v| {
visit::visit_fn(&fk, &m.decl, &m.body, m.span, m.id,
(), *v);
self, *v);
}
}
}
@ -499,9 +499,9 @@ fn ty_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
visit::mk_vt(@visit::Visitor {visit_ty: |_t, _e, _v| { },.. **v})
}
fn lint_while_true(cx: @mut Context) -> visit::vt<()> {
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: |e: @ast::expr| {
fn lint_while_true() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_expr: |e, cx: @mut Context, vt| {
match e.node {
ast::expr_while(cond, _) => {
match cond.node {
@ -517,12 +517,13 @@ fn lint_while_true(cx: @mut Context) -> visit::vt<()> {
}
_ => ()
}
visit::visit_expr(e, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn lint_type_limits(cx: @mut Context) -> visit::vt<()> {
fn lint_type_limits() -> visit::vt<@mut Context> {
fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
min: T, max: T) -> bool {
match binop {
@ -568,7 +569,7 @@ fn lint_type_limits(cx: @mut Context) -> visit::vt<()> {
}
}
fn check_limits(cx: @mut Context, binop: ast::binop, l: &ast::expr,
fn check_limits(cx: &Context, binop: ast::binop, l: &ast::expr,
r: &ast::expr) -> bool {
let (lit, expr, swap) = match (&l.node, &r.node) {
(&ast::expr_lit(_), _) => (l, r, true),
@ -621,26 +622,26 @@ fn lint_type_limits(cx: @mut Context) -> visit::vt<()> {
}
}
let visit_expr: @fn(@ast::expr) = |e| {
match e.node {
ast::expr_binary(ref binop, @ref l, @ref r) => {
if is_comparison(*binop)
&& !check_limits(cx, *binop, l, r) {
cx.span_lint(type_limits, e.span,
"comparison is useless due to type limits");
visit::mk_vt(@visit::Visitor {
visit_expr: |e, cx: @mut Context, vt| {
match e.node {
ast::expr_binary(ref binop, @ref l, @ref r) => {
if is_comparison(*binop)
&& !check_limits(cx, *binop, l, r) {
cx.span_lint(type_limits, e.span,
"comparison is useless due to type limits");
}
}
_ => ()
}
_ => ()
}
};
visit::visit_expr(e, cx, vt);
},
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn check_item_default_methods(cx: @mut Context, item: @ast::item) {
fn check_item_default_methods(cx: &Context, item: @ast::item) {
match item.node {
ast::item_trait(_, _, ref methods) => {
for methods.each |method| {
@ -657,9 +658,9 @@ fn check_item_default_methods(cx: @mut Context, item: @ast::item) {
}
}
fn check_item_ctypes(cx: @mut Context, it: @ast::item) {
fn check_item_ctypes(cx: &Context, it: @ast::item) {
fn check_foreign_fn(cx: @mut Context, decl: &ast::fn_decl) {
fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
let tys = vec::map(decl.inputs, |a| a.ty );
for vec::each(vec::append_one(tys, decl.output)) |ty| {
match ty.node {
@ -699,7 +700,7 @@ fn check_item_ctypes(cx: @mut Context, it: @ast::item) {
}
}
fn check_type_for_lint(cx: @mut Context, lint: lint, span: span, ty: ty::t) {
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;
@ -726,13 +727,13 @@ fn check_type_for_lint(cx: @mut Context, lint: lint, span: span, ty: ty::t) {
}
}
fn check_type(cx: @mut Context, span: span, ty: ty::t) {
fn check_type(cx: &Context, span: span, ty: ty::t) {
for [managed_heap_memory, owned_heap_memory, heap_memory].each |lint| {
check_type_for_lint(cx, *lint, span, ty);
}
}
fn check_item_heap(cx: @mut Context, it: @ast::item) {
fn check_item_heap(cx: &Context, it: @ast::item) {
match it.node {
ast::item_fn(*) |
ast::item_ty(*) |
@ -756,19 +757,20 @@ fn check_item_heap(cx: @mut Context, it: @ast::item) {
}
}
fn lint_heap(cx: @mut Context) -> visit::vt<()> {
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: |e| {
fn lint_heap() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_expr: |e, cx: @mut Context, vt| {
let ty = ty::expr_ty(cx.tcx, e);
check_type(cx, e.span, ty);
visit::visit_expr(e, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn lint_path_statement(cx: @mut Context) -> visit::vt<()> {
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_stmt: |s| {
fn lint_path_statement() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_stmt: |s, cx: @mut Context, vt| {
match s.node {
ast::stmt_semi(
@ast::expr { node: ast::expr_path(_), _ },
@ -779,12 +781,13 @@ fn lint_path_statement(cx: @mut Context) -> visit::vt<()> {
}
_ => ()
}
visit::visit_stmt(s, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn check_item_non_camel_case_types(cx: @mut Context, it: @ast::item) {
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);
assert!(!ident.is_empty());
@ -808,7 +811,7 @@ fn check_item_non_camel_case_types(cx: @mut Context, it: @ast::item) {
}
}
fn check_case(cx: @mut Context, ident: ast::ident, span: span) {
fn check_case(cx: &Context, ident: ast::ident, span: span) {
if !is_camel_case(cx.tcx, ident) {
cx.span_lint(non_camel_case_types, span,
"type, variant, or trait should have \
@ -831,27 +834,26 @@ fn check_item_non_camel_case_types(cx: @mut Context, it: @ast::item) {
}
}
fn lint_unused_unsafe(cx: @mut Context) -> visit::vt<()> {
let visit_expr: @fn(@ast::expr) = |e| {
match e.node {
ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
if !cx.tcx.used_unsafe.contains(&blk.node.id) {
cx.span_lint(unused_unsafe, blk.span,
"unnecessary `unsafe` block");
fn lint_unused_unsafe() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_expr: |e, cx: @mut Context, vt| {
match e.node {
ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
if !cx.tcx.used_unsafe.contains(&blk.node.id) {
cx.span_lint(unused_unsafe, blk.span,
"unnecessary `unsafe` block");
}
}
_ => ()
}
_ => ()
}
};
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
visit::visit_expr(e, cx, vt);
},
.. *visit::default_visitor()
})
}
fn lint_unused_mut(cx: @mut Context) -> visit::vt<()> {
let check_pat: @fn(@ast::pat) = |p| {
fn lint_unused_mut() -> visit::vt<@mut Context> {
fn check_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, _, _| {
@ -866,37 +868,48 @@ fn lint_unused_mut(cx: @mut Context) -> visit::vt<()> {
};
cx.span_lint(unused_mut, p.span, msg);
}
};
}
let visit_fn_decl: @fn(&ast::fn_decl) = |fd| {
fn visit_fn_decl(cx: &Context, fd: &ast::fn_decl) {
for fd.inputs.each |arg| {
if arg.is_mutbl {
check_pat(arg.pat);
check_pat(cx, arg.pat);
}
}
};
}
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_local: |l| {
visit::mk_vt(@visit::Visitor {
visit_local: |l, cx: @mut Context, vt| {
if l.node.is_mutbl {
check_pat(l.node.pat);
check_pat(cx, l.node.pat);
}
visit::visit_local(l, cx, vt);
},
visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
visit_ty_method: |tm| visit_fn_decl(&tm.decl),
visit_struct_method: |sm| visit_fn_decl(&sm.decl),
visit_trait_method: |tm| {
visit_fn: |a, fd, b, c, d, cx, vt| {
visit_fn_decl(cx, fd);
visit::visit_fn(a, fd, b, c, d, cx, vt);
},
visit_ty_method: |tm, cx, vt| {
visit_fn_decl(cx, &tm.decl);
visit::visit_ty_method(tm, cx, vt);
},
visit_struct_method: |sm, cx, vt| {
visit_fn_decl(cx, &sm.decl);
visit::visit_struct_method(sm, cx, vt);
},
visit_trait_method: |tm, cx, vt| {
match *tm {
ast::required(ref tm) => visit_fn_decl(&tm.decl),
ast::provided(m) => visit_fn_decl(&m.decl),
ast::required(ref tm) => visit_fn_decl(cx, &tm.decl),
ast::provided(m) => visit_fn_decl(cx, &m.decl)
}
visit::visit_trait_method(tm, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn lint_session(cx: @mut Context) -> visit::vt<()> {
ast_util::id_visitor(|id| {
fn lint_session() -> visit::vt<@mut Context> {
ast_util::id_visitor(|id, cx: @mut Context| {
match cx.tcx.sess.lints.pop(&id) {
None => {},
Some(l) => {
@ -908,10 +921,10 @@ fn lint_session(cx: @mut Context) -> visit::vt<()> {
})
}
fn lint_unnecessary_allocations(cx: @mut Context) -> visit::vt<()> {
fn lint_unnecessary_allocations() -> visit::vt<@mut Context> {
// Warn if string and vector literals with sigils are immediately borrowed.
// Those can have the sigil removed.
fn check(cx: @mut Context, e: @ast::expr) {
fn check(cx: &Context, e: @ast::expr) {
match e.node {
ast::expr_vstore(e2, ast::expr_vstore_uniq) |
ast::expr_vstore(e2, ast::expr_vstore_box) => {
@ -938,19 +951,18 @@ fn lint_unnecessary_allocations(cx: @mut Context) -> visit::vt<()> {
}
}
let visit_expr: @fn(@ast::expr) = |e| {
check(cx, e);
};
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
visit::mk_vt(@visit::Visitor {
visit_expr: |e, cx: @mut Context, vt| {
check(cx, e);
visit::visit_expr(e, cx, vt);
},
.. *visit::default_visitor()
})
}
fn lint_missing_struct_doc(cx: @mut Context) -> visit::vt<()> {
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_struct_field: |field| {
fn lint_missing_struct_doc() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_struct_field: |field, cx: @mut Context, vt| {
let relevant = match field.node.kind {
ast::named_field(_, vis) => vis != ast::private,
ast::unnamed_field => false,
@ -969,14 +981,16 @@ fn lint_missing_struct_doc(cx: @mut Context) -> visit::vt<()> {
for a field.");
}
}
visit::visit_struct_field(field, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
fn lint_missing_trait_doc(cx: @mut Context) -> visit::vt<()> {
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_trait_method: |method| {
fn lint_missing_trait_doc() -> visit::vt<@mut Context> {
visit::mk_vt(@visit::Visitor {
visit_trait_method: |method, cx: @mut Context, vt| {
let mut has_doc = false;
let span = match copy *method {
ast::required(m) => {
@ -1006,8 +1020,9 @@ fn lint_missing_trait_doc(cx: @mut Context) -> visit::vt<()> {
cx.span_lint(missing_trait_doc, span, "missing documentation \
for a method.");
}
visit::visit_trait_method(method, cx, vt);
},
.. *visit::default_simple_visitor()
.. *visit::default_visitor()
})
}
@ -1031,39 +1046,33 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
}
// Register each of the lint passes with the context
cx.add_lint(lint_while_true(cx));
cx.add_lint(lint_path_statement(cx));
cx.add_lint(lint_heap(cx));
cx.add_lint(lint_type_limits(cx));
cx.add_lint(lint_unused_unsafe(cx));
cx.add_lint(lint_unused_mut(cx));
cx.add_lint(lint_session(cx));
cx.add_lint(lint_unnecessary_allocations(cx));
cx.add_lint(lint_missing_struct_doc(cx));
cx.add_lint(lint_missing_trait_doc(cx));
// type inference doesn't like this being declared below, we need to tell it
// what the type of this first function is...
let visit_item:
@fn(@ast::item, @mut Context, visit::vt<@mut Context>) =
|it, cx, vt| {
do cx.with_lint_attrs(it.attrs) {
check_item_ctypes(cx, it);
check_item_non_camel_case_types(cx, it);
check_item_default_methods(cx, it);
check_item_heap(cx, it);
cx.process(Item(it));
visit::visit_item(it, cx, vt);
}
};
cx.add_lint(lint_while_true());
cx.add_lint(lint_path_statement());
cx.add_lint(lint_heap());
cx.add_lint(lint_type_limits());
cx.add_lint(lint_unused_unsafe());
cx.add_lint(lint_unused_mut());
cx.add_lint(lint_session());
cx.add_lint(lint_unnecessary_allocations());
cx.add_lint(lint_missing_struct_doc());
cx.add_lint(lint_missing_trait_doc());
// Actually perform the lint checks (iterating the ast)
do cx.with_lint_attrs(crate.node.attrs) {
cx.process(Crate(crate));
visit::visit_crate(crate, cx, visit::mk_vt(@visit::Visitor {
visit_item: visit_item,
visit_item: |it, cx: @mut Context, vt| {
do cx.with_lint_attrs(it.attrs) {
check_item_ctypes(cx, it);
check_item_non_camel_case_types(cx, it);
check_item_default_methods(cx, it);
check_item_heap(cx, it);
cx.process(Item(it));
visit::visit_item(it, cx, vt);
}
},
visit_fn: |fk, decl, body, span, id, cx, vt| {
match *fk {
visit::fk_method(_, _, m) => {

View File

@ -386,82 +386,103 @@ pub impl id_range {
}
}
pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
let visit_generics: @fn(&Generics) = |generics| {
pub fn id_visitor<T: Copy>(vfn: @fn(node_id, T)) -> visit::vt<T> {
let visit_generics: @fn(&Generics, T) = |generics, t| {
for generics.ty_params.each |p| {
vfn(p.id);
vfn(p.id, t);
}
for generics.lifetimes.each |p| {
vfn(p.id);
vfn(p.id, t);
}
};
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_mod: |_m, _sp, id| vfn(id),
visit::mk_vt(@visit::Visitor {
visit_mod: |m, sp, id, t, vt| {
vfn(id, t);
visit::visit_mod(m, sp, id, t, vt);
},
visit_view_item: |vi| {
visit_view_item: |vi, t, vt| {
match vi.node {
view_item_extern_mod(_, _, id) => vfn(id),
view_item_extern_mod(_, _, id) => vfn(id, t),
view_item_use(ref vps) => {
for vps.each |vp| {
match vp.node {
view_path_simple(_, _, id) => vfn(id),
view_path_glob(_, id) => vfn(id),
view_path_simple(_, _, id) => vfn(id, t),
view_path_glob(_, id) => vfn(id, t),
view_path_list(_, ref paths, id) => {
vfn(id);
vfn(id, t);
for paths.each |p| {
vfn(p.node.id);
vfn(p.node.id, t);
}
}
}
}
}
}
visit::visit_view_item(vi, t, vt);
},
visit_foreign_item: |ni| vfn(ni.id),
visit_foreign_item: |ni, t, vt| {
vfn(ni.id, t);
visit::visit_foreign_item(ni, t, vt);
},
visit_item: |i| {
vfn(i.id);
visit_item: |i, t, vt| {
vfn(i.id, t);
match i.node {
item_enum(ref enum_definition, _) =>
for (*enum_definition).variants.each |v| { vfn(v.node.id); },
for (*enum_definition).variants.each |v| { vfn(v.node.id, t); },
_ => ()
}
visit::visit_item(i, t, vt);
},
visit_local: |l| vfn(l.node.id),
visit_block: |b| vfn(b.node.id),
visit_stmt: |s| vfn(ast_util::stmt_id(s)),
visit_arm: |_| {},
visit_pat: |p| vfn(p.id),
visit_decl: |_| {},
visit_expr: |e| {
vfn(e.callee_id);
vfn(e.id);
visit_local: |l, t, vt| {
vfn(l.node.id, t);
visit::visit_local(l, t, vt);
},
visit_block: |b, t, vt| {
vfn(b.node.id, t);
visit::visit_block(b, t, vt);
},
visit_stmt: |s, t, vt| {
vfn(ast_util::stmt_id(s), t);
visit::visit_stmt(s, t, vt);
},
visit_pat: |p, t, vt| {
vfn(p.id, t);
visit::visit_pat(p, t, vt);
},
visit_expr_post: |_| {},
visit_expr: |e, t, vt| {
vfn(e.callee_id, t);
vfn(e.id, t);
visit::visit_expr(e, t, vt);
},
visit_ty: |t| {
match t.node {
ty_path(_, id) => vfn(id),
visit_ty: |ty, t, vt| {
match ty.node {
ty_path(_, id) => vfn(id, t),
_ => { /* fall through */ }
}
visit::visit_ty(ty, t, vt);
},
visit_generics: visit_generics,
visit_generics: |generics, t, vt| {
visit_generics(generics, t);
visit::visit_generics(generics, t, vt);
},
visit_fn: |fk, d, _, _, id| {
vfn(id);
visit_fn: |fk, d, a, b, id, t, vt| {
vfn(id, t);
match *fk {
visit::fk_item_fn(_, generics, _, _) => {
visit_generics(generics);
visit_generics(generics, t);
}
visit::fk_method(_, generics, m) => {
vfn(m.self_id);
visit_generics(generics);
vfn(m.self_id, t);
visit_generics(generics, t);
}
visit::fk_anon(_) |
visit::fk_fn_block => {
@ -469,20 +490,22 @@ pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
}
for d.inputs.each |arg| {
vfn(arg.id)
vfn(arg.id, t)
}
visit::visit_fn(fk, d, a, b, id, t, vt);
},
visit_ty_method: |_| {},
visit_trait_method: |_| {},
visit_struct_def: |_, _, _, _| {},
visit_struct_field: |f| vfn(f.node.id),
visit_struct_method: |_| {}
visit_struct_field: |f, t, vt| {
vfn(f.node.id, t);
visit::visit_struct_field(f, t, vt);
},
.. *visit::default_visitor()
})
}
pub fn visit_ids_for_inlined_item(item: &inlined_item, vfn: @fn(node_id)) {
item.accept((), id_visitor(vfn));
item.accept((), id_visitor(|id, ()| vfn(id)));
}
pub fn compute_id_range(visit_ids_fn: &fn(@fn(node_id))) -> id_range {