Mark allowed dead code and lang items as live
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.
This commit is contained in:
parent
1fda761e9c
commit
deb3ca53a8
|
@ -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,
|
||||||
|
|
|
@ -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, _) => {
|
||||||
|
|
|
@ -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 {} }
|
||||||
|
|
Loading…
Reference in a new issue