Skip to content

Commit f42440b

Browse files
committed
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.
2 parents 1fda761 + deb3ca5 commit f42440b

File tree

3 files changed

+80
-13
lines changed

3 files changed

+80
-13
lines changed

src/librustc/middle/dead.rs

+46-12
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,23 @@
1212
// closely. The idea is that all reachable symbols are live, codes called
1313
// from live codes are live, and everything else is dead.
1414

15+
use middle::lint::{allow, contains_lint, DeadCode};
16+
use middle::privacy;
1517
use middle::ty;
1618
use middle::typeck;
17-
use middle::privacy;
18-
use middle::lint::DeadCode;
1919

2020
use std::hashmap::HashSet;
2121
use syntax::ast;
2222
use syntax::ast_map;
2323
use syntax::ast_util::{local_def, def_id_of_def, is_local};
24+
use syntax::attr;
2425
use syntax::codemap;
2526
use syntax::parse::token;
2627
use syntax::visit::Visitor;
2728
use syntax::visit;
2829

30+
pub static DEAD_CODE_LINT_STR: &'static str = "dead_code";
31+
2932
// Any local node that may call something in its body block should be
3033
// explored. For example, if it's a live NodeItem that is a
3134
// function, then we should explore its block to check for codes that
@@ -196,26 +199,57 @@ impl Visitor<()> for MarkSymbolVisitor {
196199
}
197200
}
198201

199-
// This visitor is used to mark the implemented methods of a trait. Since we
200-
// can not be sure if such methods are live or dead, we simply mark them
201-
// as live.
202-
struct TraitMethodSeeder {
202+
fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
203+
contains_lint(attrs, allow, DEAD_CODE_LINT_STR)
204+
|| attr::contains_name(attrs, "lang")
205+
}
206+
207+
// This visitor seeds items that
208+
// 1) We want to explicitly consider as live:
209+
// * Item annotated with #[allow(dead_code)]
210+
// - This is done so that if we want to suppress warnings for a
211+
// group of dead functions, we only have to annotate the "root".
212+
// For example, if both `f` and `g` are dead and `f` calls `g`,
213+
// then annotating `f` with `#[allow(dead_code)]` will suppress
214+
// warning for both `f` and `g`.
215+
// * Item annotated with #[lang=".."]
216+
// - This is because lang items are always callable from elsewhere.
217+
// or
218+
// 2) We are not sure to be live or not
219+
// * Implementation of a trait method
220+
struct LifeSeeder {
203221
worklist: ~[ast::NodeId],
204222
}
205223

206-
impl Visitor<()> for TraitMethodSeeder {
224+
impl Visitor<()> for LifeSeeder {
207225
fn visit_item(&mut self, item: &ast::Item, _: ()) {
226+
if has_allow_dead_code_or_lang_attr(item.attrs) {
227+
self.worklist.push(item.id);
228+
}
208229
match item.node {
209230
ast::ItemImpl(_, Some(ref _trait_ref), _, ref methods) => {
210231
for method in methods.iter() {
211232
self.worklist.push(method.id);
212233
}
213234
}
214-
ast::ItemMod(..) | ast::ItemFn(..) => {
215-
visit::walk_item(self, item, ());
235+
_ => ()
236+
}
237+
visit::walk_item(self, item, ());
238+
}
239+
240+
fn visit_fn(&mut self, fk: &visit::FnKind,
241+
_: &ast::FnDecl, block: &ast::Block,
242+
_: codemap::Span, id: ast::NodeId, _: ()) {
243+
// Check for method here because methods are not ast::Item
244+
match *fk {
245+
visit::FkMethod(_, _, method) => {
246+
if has_allow_dead_code_or_lang_attr(method.attrs) {
247+
self.worklist.push(id);
248+
}
216249
}
217250
_ => ()
218251
}
252+
visit::walk_block(self, block, ());
219253
}
220254
}
221255

@@ -244,12 +278,12 @@ fn create_and_seed_worklist(tcx: ty::ctxt,
244278
}
245279

246280
// Seed implemeneted trait methods
247-
let mut trait_method_seeder = TraitMethodSeeder {
281+
let mut life_seeder = LifeSeeder {
248282
worklist: worklist
249283
};
250-
visit::walk_crate(&mut trait_method_seeder, crate, ());
284+
visit::walk_crate(&mut life_seeder, crate, ());
251285

252-
return trait_method_seeder.worklist;
286+
return life_seeder.worklist;
253287
}
254288

255289
fn find_live(tcx: ty::ctxt,

src/librustc/middle/lint.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
//! Context itself, span_lint should be used instead of add_lint.
3535
3636
use driver::session;
37+
use middle::dead::DEAD_CODE_LINT_STR;
3738
use middle::privacy;
3839
use middle::trans::adt; // for `adt::is_ffi_safe`
3940
use middle::ty;
@@ -293,7 +294,7 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
293294
default: warn
294295
}),
295296

296-
("dead_code",
297+
(DEAD_CODE_LINT_STR,
297298
LintSpec {
298299
lint: DeadCode,
299300
desc: "detect piece of code that will never be used",
@@ -531,6 +532,8 @@ impl<'a> Context<'a> {
531532
}
532533
}
533534

535+
// Check that every lint from the list of attributes satisfies `f`.
536+
// Return true if that's the case. Otherwise return false.
534537
pub fn each_lint(sess: session::Session,
535538
attrs: &[ast::Attribute],
536539
f: |@ast::MetaItem, level, @str| -> bool)
@@ -564,6 +567,25 @@ pub fn each_lint(sess: session::Session,
564567
true
565568
}
566569

570+
// Check from a list of attributes if it contains the appropriate
571+
// `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
572+
pub fn contains_lint(attrs: &[ast::Attribute],
573+
level: level, lintname: &'static str) -> bool {
574+
let level_name = level_to_str(level);
575+
for attr in attrs.iter().filter(|m| level_name == m.name()) {
576+
if attr.meta_item_list().is_none() {
577+
continue
578+
}
579+
let list = attr.meta_item_list().unwrap();
580+
for meta_item in list.iter() {
581+
if lintname == meta_item.name() {
582+
return true;
583+
}
584+
}
585+
}
586+
false
587+
}
588+
567589
fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
568590
match e.node {
569591
ast::ExprWhile(cond, _) => {

src/test/compile-fail/lint-dead-code-1.rs

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#[no_std];
1112
#[allow(unused_variable)];
1213
#[deny(dead_code)];
1314

@@ -85,3 +86,13 @@ fn foo() { //~ ERROR: code is never used
8586
fn bar() { //~ ERROR: code is never used
8687
foo();
8788
}
89+
90+
// Code with #[allow(dead_code)] should be marked live (and thus anything it
91+
// calls is marked live)
92+
#[allow(dead_code)]
93+
fn g() { h(); }
94+
fn h() {}
95+
96+
// Similarly, lang items are live
97+
#[lang="fail_"]
98+
fn fail(_: *u8, _: *u8, _: uint) -> ! { loop {} }

0 commit comments

Comments
 (0)