Implement a lint mode to detect unnecessary allocations

This commit is contained in:
Alex Crichton 2013-05-18 03:07:28 -04:00
parent 26babaafcd
commit 074799b4c5
2 changed files with 73 additions and 3 deletions

View File

@ -79,6 +79,7 @@ pub enum lint {
unused_variable,
dead_assignment,
unused_mut,
unnecessary_allocation,
}
pub fn level_to_str(lv: level) -> &'static str {
@ -242,6 +243,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
desc: "detect mut variables which don't need to be mutable",
default: warn
}),
("unnecessary_allocation",
LintSpec {
lint: unnecessary_allocation,
desc: "detects unnecessary allocations that can be eliminated",
default: warn
}),
];
/*
@ -881,6 +889,67 @@ fn lint_session(cx: @mut Context) -> visit::vt<()> {
})
}
fn lint_unnecessary_allocations(cx: @mut Context) -> visit::vt<()> {
// If the expression `e` has an allocated type, but `t` dictates that it's
// something like a slice (doesn't need allocation), emit a warning with the
// specified span.
//
// Currently, this only applies to string and vector literals with sigils in
// front. Those can have the sigil removed to get a borrowed pointer
// automatically.
fn check(cx: @mut Context, e: @ast::expr, t: ty::t) {
match e.node {
ast::expr_vstore(e2, ast::expr_vstore_uniq) |
ast::expr_vstore(e2, ast::expr_vstore_box) => {
match e2.node {
ast::expr_lit(@codemap::spanned{
node: ast::lit_str(*), _}) |
ast::expr_vec(*) => {}
_ => return
}
}
_ => return
}
match ty::get(t).sty {
ty::ty_estr(ty::vstore_slice(*)) |
ty::ty_evec(_, ty::vstore_slice(*)) => {
cx.span_lint(unnecessary_allocation,
e.span, "unnecessary allocation, the sigil can be \
removed");
}
_ => ()
}
}
let visit_expr: @fn(@ast::expr) = |e| {
match e.node {
ast::expr_call(c, ref args, _) => {
let t = ty::node_id_to_type(cx.tcx, c.id);
let s = ty::ty_fn_sig(t);
for vec::each2(*args, s.inputs) |e, t| {
check(cx, *e, *t);
}
}
ast::expr_method_call(_, _, _, ref args, _) => {
let t = ty::node_id_to_type(cx.tcx, e.callee_id);
let s = ty::ty_fn_sig(t);
for vec::each2(*args, s.inputs) |e, t| {
check(cx, *e, *t);
}
}
_ => {}
}
};
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
})
}
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
let cx = @mut Context {
dict: @get_lint_dict(),
@ -908,6 +977,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
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));
// type inference doesn't like this being declared below, we need to tell it
// what the type of this first function is...

View File

@ -18,7 +18,7 @@ use ast;
use codemap::span;
use ext::base::*;
use ext::base;
use ext::build::mk_uniq_str;
use ext::build::mk_base_str;
pub fn expand_syntax_ext(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree])
-> base::MacResult {
@ -29,8 +29,8 @@ pub fn expand_syntax_ext(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree])
// Option<str> rather than just an maybe-empty string.
let e = match os::getenv(var) {
None => mk_uniq_str(cx, sp, ~""),
Some(ref s) => mk_uniq_str(cx, sp, copy *s)
None => mk_base_str(cx, sp, ~""),
Some(ref s) => mk_base_str(cx, sp, copy *s)
};
MRExpr(e)
}