Change:
Currently if you have some dead_code and you want to preserve it, you have to annotate every function you want to preserve and all the dead code that function calls. I think we should consider annotated code to be alive, and then all the code that touches to be alive as well.
cc sounds like a cool idea to me.
By "annotate", do you mean putting an attribute above the function you want to preserve?
yes.

Discussion:
// closely. The idea is that all reachable symbols are live, codes called // from live codes are live, and everything else is dead. <ins> use middle::lint::{allow, contains_lint, DeadCode}; use middle::privacy; </ins> use middle::ty; use middle::typeck; <del> use middle::privacy; use middle::lint::DeadCode; </del> use std::hashmap::HashSet; use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, def_id_of_def, is_local}; <ins> use syntax::attr; </ins> use syntax::codemap; use syntax::parse::token; use syntax::visit::Visitor; use syntax::visit; <ins> pub static DEAD_CODE_LINT_STR: &'static str = "dead_code"; </ins> // 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

} } <del> // 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 { </del> <ins> 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 { </ins> worklist: ~[ast::NodeId], } <del> impl Visitor<()> for TraitMethodSeeder { </del> <ins> impl Visitor<()> for LifeSeeder { </ins> fn visit_item(&mut self, item: &ast::Item, _: ()) { <ins> if has_allow_dead_code_or_lang_attr(item.attrs) { self.worklist.push(item.id); } </ins> match item.node { ast::ItemImpl(_, Some(ref _trait_ref), _, ref methods) => { for method in methods.iter() { self.worklist.push(method.id); } } <del> ast::ItemMod(..) | ast::ItemFn(..) => { visit::walk_item(self, item, ()); </del> <ins> _ => () } 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); } </ins> } _ => () } <ins> visit::walk_block(self, block, ()); </ins> } }

} // Seed implemeneted trait methods <del> let mut trait_method_seeder = TraitMethodSeeder { </del> <ins> let mut life_seeder = LifeSeeder { </ins> worklist: worklist }; <del> visit::walk_crate(&mut trait_method_seeder, crate, ()); </del> <ins> visit::walk_crate(&mut life_seeder, crate, ()); </ins> <del> return trait_method_seeder.worklist; </del> <ins> return life_seeder.worklist; </ins> } fn find_live(tcx: ty::ctxt,

//! Context itself, span_lint should be used instead of add_lint. use driver::session; <ins> use middle::dead::DEAD_CODE_LINT_STR; </ins> use middle::privacy; use middle::trans::adt; // for `adt::is_ffi_safe` use middle::ty;

default: warn }), <del> ("dead_code", </del> <ins> (DEAD_CODE_LINT_STR, </ins> LintSpec { lint: DeadCode, desc: "detect piece of code that will never be used",

} } <ins> // Check that every lint from the list of attributes satisfies `f`. // Return true if that's the case. Otherwise return false. </ins> pub fn each_lint(sess: session::Session, attrs: &[ast::Attribute], f: |@ast::MetaItem, level, @str| -> bool)

true } <ins> // 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 } </ins> fn check_while_true_expr(cx: &Context, e: &ast::Expr) { match e.node { ast::ExprWhile(cond, _) => {

// option. This file may not be copied, modified, or distributed // except according to those terms. <ins> #[no_std]; </ins> #[allow(unused_variable)]; #[deny(dead_code)];

fn bar() { //~ ERROR: code is never used foo(); } <ins> // 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 {} } </ins>