auto merge of #11471 : ktt3ja/rust/issue-11380, r=alexcrichton

Dead code pass now explicitly checks for `#[allow(dead_code)]` and
`#[lang=".."]` attributes on items and marks them as live if they have
those attributes. The former is done so that if we want to suppress
warnings for a group of dead functions, we only have to annotate the
"root" of the call chain.

Close #11380 and #11440.
This commit is contained in:
bors 2014-01-12 11:16:32 -08:00
commit f42440bd73
3 changed files with 80 additions and 13 deletions

View File

@ -12,20 +12,23 @@
// closely. The idea is that all reachable symbols are live, codes called
// from live codes are live, and everything else is dead.
use middle::lint::{allow, contains_lint, DeadCode};
use middle::privacy;
use middle::ty;
use middle::typeck;
use middle::privacy;
use middle::lint::DeadCode;
use std::hashmap::HashSet;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{local_def, def_id_of_def, is_local};
use syntax::attr;
use syntax::codemap;
use syntax::parse::token;
use syntax::visit::Visitor;
use syntax::visit;
pub static DEAD_CODE_LINT_STR: &'static str = "dead_code";
// Any local node that may call something in its body block should be
// explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that
@ -196,26 +199,57 @@ impl Visitor<()> for MarkSymbolVisitor {
}
}
// This visitor is used to mark the implemented methods of a trait. Since we
// can not be sure if such methods are live or dead, we simply mark them
// as live.
struct TraitMethodSeeder {
fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
contains_lint(attrs, allow, DEAD_CODE_LINT_STR)
|| attr::contains_name(attrs, "lang")
}
// This visitor seeds items that
// 1) We want to explicitly consider as live:
// * Item annotated with #[allow(dead_code)]
// - This is done so that if we want to suppress warnings for a
// group of dead functions, we only have to annotate the "root".
// For example, if both `f` and `g` are dead and `f` calls `g`,
// then annotating `f` with `#[allow(dead_code)]` will suppress
// warning for both `f` and `g`.
// * Item annotated with #[lang=".."]
// - This is because lang items are always callable from elsewhere.
// or
// 2) We are not sure to be live or not
// * Implementation of a trait method
struct LifeSeeder {
worklist: ~[ast::NodeId],
}
impl Visitor<()> for TraitMethodSeeder {
impl Visitor<()> for LifeSeeder {
fn visit_item(&mut self, item: &ast::Item, _: ()) {
if has_allow_dead_code_or_lang_attr(item.attrs) {
self.worklist.push(item.id);
}
match item.node {
ast::ItemImpl(_, Some(ref _trait_ref), _, ref methods) => {
for method in methods.iter() {
self.worklist.push(method.id);
}
}
ast::ItemMod(..) | ast::ItemFn(..) => {
_ => ()
}
visit::walk_item(self, item, ());
}
fn visit_fn(&mut self, fk: &visit::FnKind,
_: &ast::FnDecl, block: &ast::Block,
_: codemap::Span, id: ast::NodeId, _: ()) {
// Check for method here because methods are not ast::Item
match *fk {
visit::FkMethod(_, _, method) => {
if has_allow_dead_code_or_lang_attr(method.attrs) {
self.worklist.push(id);
}
}
_ => ()
}
visit::walk_block(self, block, ());
}
}
@ -244,12 +278,12 @@ fn create_and_seed_worklist(tcx: ty::ctxt,
}
// Seed implemeneted trait methods
let mut trait_method_seeder = TraitMethodSeeder {
let mut life_seeder = LifeSeeder {
worklist: worklist
};
visit::walk_crate(&mut trait_method_seeder, crate, ());
visit::walk_crate(&mut life_seeder, crate, ());
return trait_method_seeder.worklist;
return life_seeder.worklist;
}
fn find_live(tcx: ty::ctxt,

View File

@ -34,6 +34,7 @@
//! Context itself, span_lint should be used instead of add_lint.
use driver::session;
use middle::dead::DEAD_CODE_LINT_STR;
use middle::privacy;
use middle::trans::adt; // for `adt::is_ffi_safe`
use middle::ty;
@ -293,7 +294,7 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
default: warn
}),
("dead_code",
(DEAD_CODE_LINT_STR,
LintSpec {
lint: DeadCode,
desc: "detect piece of code that will never be used",
@ -531,6 +532,8 @@ impl<'a> Context<'a> {
}
}
// Check that every lint from the list of attributes satisfies `f`.
// Return true if that's the case. Otherwise return false.
pub fn each_lint(sess: session::Session,
attrs: &[ast::Attribute],
f: |@ast::MetaItem, level, @str| -> bool)
@ -564,6 +567,25 @@ pub fn each_lint(sess: session::Session,
true
}
// Check from a list of attributes if it contains the appropriate
// `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
pub fn contains_lint(attrs: &[ast::Attribute],
level: level, lintname: &'static str) -> bool {
let level_name = level_to_str(level);
for attr in attrs.iter().filter(|m| level_name == m.name()) {
if attr.meta_item_list().is_none() {
continue
}
let list = attr.meta_item_list().unwrap();
for meta_item in list.iter() {
if lintname == meta_item.name() {
return true;
}
}
}
false
}
fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
match e.node {
ast::ExprWhile(cond, _) => {

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[no_std];
#[allow(unused_variable)];
#[deny(dead_code)];
@ -85,3 +86,13 @@ fn foo() { //~ ERROR: code is never used
fn bar() { //~ ERROR: code is never used
foo();
}
// Code with #[allow(dead_code)] should be marked live (and thus anything it
// calls is marked live)
#[allow(dead_code)]
fn g() { h(); }
fn h() {}
// Similarly, lang items are live
#[lang="fail_"]
fn fail(_: *u8, _: *u8, _: uint) -> ! { loop {} }