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 // closely. The idea is that all reachable symbols are live, codes called
// from live codes are live, and everything else is dead. // 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::ty;
use middle::typeck; use middle::typeck;
use middle::privacy;
use middle::lint::DeadCode;
use std::hashmap::HashSet; use std::hashmap::HashSet;
use syntax::ast; use syntax::ast;
use syntax::ast_map; use syntax::ast_map;
use syntax::ast_util::{local_def, def_id_of_def, is_local}; use syntax::ast_util::{local_def, def_id_of_def, is_local};
use syntax::attr;
use syntax::codemap; use syntax::codemap;
use syntax::parse::token; use syntax::parse::token;
use syntax::visit::Visitor; use syntax::visit::Visitor;
use syntax::visit; 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 // 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 // explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that // 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 fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
// can not be sure if such methods are live or dead, we simply mark them contains_lint(attrs, allow, DEAD_CODE_LINT_STR)
// as live. || attr::contains_name(attrs, "lang")
struct TraitMethodSeeder { }
// 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], worklist: ~[ast::NodeId],
} }
impl Visitor<()> for TraitMethodSeeder { impl Visitor<()> for LifeSeeder {
fn visit_item(&mut self, item: &ast::Item, _: ()) { 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 { match item.node {
ast::ItemImpl(_, Some(ref _trait_ref), _, ref methods) => { ast::ItemImpl(_, Some(ref _trait_ref), _, ref methods) => {
for method in methods.iter() { for method in methods.iter() {
self.worklist.push(method.id); self.worklist.push(method.id);
} }
} }
ast::ItemMod(..) | ast::ItemFn(..) => { _ => ()
}
visit::walk_item(self, item, ()); 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 // Seed implemeneted trait methods
let mut trait_method_seeder = TraitMethodSeeder { let mut life_seeder = LifeSeeder {
worklist: worklist 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, fn find_live(tcx: ty::ctxt,

View File

@ -34,6 +34,7 @@
//! Context itself, span_lint should be used instead of add_lint. //! Context itself, span_lint should be used instead of add_lint.
use driver::session; use driver::session;
use middle::dead::DEAD_CODE_LINT_STR;
use middle::privacy; use middle::privacy;
use middle::trans::adt; // for `adt::is_ffi_safe` use middle::trans::adt; // for `adt::is_ffi_safe`
use middle::ty; use middle::ty;
@ -293,7 +294,7 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
default: warn default: warn
}), }),
("dead_code", (DEAD_CODE_LINT_STR,
LintSpec { LintSpec {
lint: DeadCode, lint: DeadCode,
desc: "detect piece of code that will never be used", 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, pub fn each_lint(sess: session::Session,
attrs: &[ast::Attribute], attrs: &[ast::Attribute],
f: |@ast::MetaItem, level, @str| -> bool) f: |@ast::MetaItem, level, @str| -> bool)
@ -564,6 +567,25 @@ pub fn each_lint(sess: session::Session,
true 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) { fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
ast::ExprWhile(cond, _) => { ast::ExprWhile(cond, _) => {

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#[no_std];
#[allow(unused_variable)]; #[allow(unused_variable)];
#[deny(dead_code)]; #[deny(dead_code)];
@ -85,3 +86,13 @@ fn foo() { //~ ERROR: code is never used
fn bar() { //~ ERROR: code is never used fn bar() { //~ ERROR: code is never used
foo(); 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 {} }