Skip to content

Commit 2ec795b

Browse files
author
Jakub Wieczorek
committed
Add detection of unused enum variants
1 parent 9e3bf02 commit 2ec795b

File tree

4 files changed

+110
-39
lines changed

4 files changed

+110
-39
lines changed

src/librustc/middle/dead.rs

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,20 @@
1313
// from live codes are live, and everything else is dead.
1414

1515
use middle::def;
16-
use lint;
16+
use middle::pat_util;
1717
use middle::privacy;
1818
use middle::ty;
1919
use middle::typeck;
20+
use lint;
2021
use util::nodemap::NodeSet;
2122

2223
use std::collections::HashSet;
2324
use syntax::ast;
2425
use syntax::ast_map;
2526
use syntax::ast_util::{local_def, is_local, PostExpansionMethod};
26-
use syntax::attr::AttrMetaMethods;
27-
use syntax::attr;
27+
use syntax::attr::{mod, AttrMetaMethods};
2828
use syntax::codemap;
29-
use syntax::visit::Visitor;
30-
use syntax::visit;
29+
use syntax::visit::{mod, Visitor};
3130

3231
// Any local node that may call something in its body block should be
3332
// explored. For example, if it's a live NodeItem that is a
@@ -51,7 +50,8 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> {
5150
worklist: Vec<ast::NodeId>,
5251
tcx: &'a ty::ctxt<'tcx>,
5352
live_symbols: Box<HashSet<ast::NodeId>>,
54-
struct_has_extern_repr: bool
53+
struct_has_extern_repr: bool,
54+
ignore_paths: bool
5555
}
5656

5757
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
@@ -61,7 +61,8 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
6161
worklist: worklist,
6262
tcx: tcx,
6363
live_symbols: box HashSet::new(),
64-
struct_has_extern_repr: false
64+
struct_has_extern_repr: false,
65+
ignore_paths: false
6566
}
6667
}
6768

@@ -73,19 +74,18 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
7374
}
7475

7576
fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) {
76-
let def = match self.tcx.def_map.borrow().find(id) {
77-
Some(&def) => def,
78-
None => return
79-
};
80-
let def_id = match def {
81-
def::DefVariant(enum_id, _, _) => Some(enum_id),
82-
def::DefPrimTy(_) => None,
83-
_ => Some(def.def_id())
84-
};
85-
match def_id {
86-
Some(def_id) => self.check_def_id(def_id),
87-
None => (),
88-
}
77+
self.tcx.def_map.borrow().find(id).map(|def| {
78+
match def {
79+
&def::DefPrimTy(_) => (),
80+
&def::DefVariant(enum_id, variant_id, _) => {
81+
self.check_def_id(enum_id);
82+
self.check_def_id(variant_id);
83+
}
84+
_ => {
85+
self.check_def_id(def.def_id());
86+
}
87+
}
88+
});
8989
}
9090

9191
fn lookup_and_handle_method(&mut self, id: ast::NodeId,
@@ -275,22 +275,27 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
275275
}
276276

277277
fn visit_pat(&mut self, pat: &ast::Pat) {
278+
let def_map = &self.tcx.def_map;
278279
match pat.node {
279280
ast::PatStruct(_, ref fields, _) => {
280281
self.handle_field_pattern_match(pat, fields.as_slice());
281282
}
282-
ast::PatIdent(_, _, _) => {
283+
_ if pat_util::pat_is_const(def_map, pat) => {
283284
// it might be the only use of a static:
284285
self.lookup_and_handle_definition(&pat.id)
285286
}
286287
_ => ()
287288
}
288289

290+
self.ignore_paths = true;
289291
visit::walk_pat(self, pat);
292+
self.ignore_paths = false;
290293
}
291294

292295
fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId) {
293-
self.lookup_and_handle_definition(&id);
296+
if !self.ignore_paths {
297+
self.lookup_and_handle_definition(&id);
298+
}
294299
visit::walk_path(self, path);
295300
}
296301

@@ -330,15 +335,19 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
330335
// 2) We are not sure to be live or not
331336
// * Implementation of a trait method
332337
struct LifeSeeder {
333-
worklist: Vec<ast::NodeId> ,
338+
worklist: Vec<ast::NodeId>
334339
}
335340

336341
impl<'v> Visitor<'v> for LifeSeeder {
337342
fn visit_item(&mut self, item: &ast::Item) {
338-
if has_allow_dead_code_or_lang_attr(item.attrs.as_slice()) {
343+
let allow_dead_code = has_allow_dead_code_or_lang_attr(item.attrs.as_slice());
344+
if allow_dead_code {
339345
self.worklist.push(item.id);
340346
}
341347
match item.node {
348+
ast::ItemEnum(ref enum_def, _) if allow_dead_code => {
349+
self.worklist.extend(enum_def.variants.iter().map(|variant| variant.node.id));
350+
}
342351
ast::ItemImpl(_, Some(ref _trait_ref), _, ref impl_items) => {
343352
for impl_item in impl_items.iter() {
344353
match *impl_item {
@@ -415,16 +424,6 @@ fn find_live(tcx: &ty::ctxt,
415424
symbol_visitor.live_symbols
416425
}
417426

418-
fn should_warn(item: &ast::Item) -> bool {
419-
match item.node {
420-
ast::ItemStatic(..)
421-
| ast::ItemFn(..)
422-
| ast::ItemEnum(..)
423-
| ast::ItemStruct(..) => true,
424-
_ => false
425-
}
426-
}
427-
428427
fn get_struct_ctor_id(item: &ast::Item) -> Option<ast::NodeId> {
429428
match item.node {
430429
ast::ItemStruct(ref struct_def, _) => struct_def.ctor_id,
@@ -438,6 +437,18 @@ struct DeadVisitor<'a, 'tcx: 'a> {
438437
}
439438

440439
impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
440+
fn should_warn_about_item(&mut self, item: &ast::Item) -> bool {
441+
let should_warn = match item.node {
442+
ast::ItemStatic(..)
443+
| ast::ItemFn(..)
444+
| ast::ItemEnum(..)
445+
| ast::ItemStruct(..) => true,
446+
_ => false
447+
};
448+
let ctor_id = get_struct_ctor_id(item);
449+
should_warn && !self.symbol_is_live(item.id, ctor_id)
450+
}
451+
441452
fn should_warn_about_field(&mut self, node: &ast::StructField_) -> bool {
442453
let is_named = node.ident().is_some();
443454
let field_type = ty::node_id_to_type(self.tcx, node.id);
@@ -451,6 +462,11 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
451462
&& !has_allow_dead_code_or_lang_attr(node.attrs.as_slice())
452463
}
453464

465+
fn should_warn_about_variant(&mut self, variant: &ast::Variant_) -> bool {
466+
!self.symbol_is_live(variant.id, None)
467+
&& !has_allow_dead_code_or_lang_attr(variant.attrs.as_slice())
468+
}
469+
454470
// id := node id of an item's definition.
455471
// ctor_id := `Some` if the item is a struct_ctor (tuple struct),
456472
// `None` otherwise.
@@ -506,9 +522,19 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
506522

507523
impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
508524
fn visit_item(&mut self, item: &ast::Item) {
509-
let ctor_id = get_struct_ctor_id(item);
510-
if !self.symbol_is_live(item.id, ctor_id) && should_warn(item) {
525+
if self.should_warn_about_item(item) {
511526
self.warn_dead_code(item.id, item.span, item.ident);
527+
} else {
528+
match item.node {
529+
ast::ItemEnum(ref enum_def, _) => {
530+
for variant in enum_def.variants.iter() {
531+
if self.should_warn_about_variant(&variant.node) {
532+
self.warn_dead_code(variant.node.id, variant.span, variant.node.name);
533+
}
534+
}
535+
},
536+
_ => ()
537+
}
512538
}
513539
visit::walk_item(self, item);
514540
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,12 @@ pub struct PubStruct2 {
6363
pub enum pub_enum { foo1, bar1 }
6464
pub enum pub_enum2 { a(*const StructUsedInEnum) }
6565
pub enum pub_enum3 { Foo = STATIC_USED_IN_ENUM_DISCRIMINANT }
66+
6667
enum priv_enum { foo2, bar2 } //~ ERROR: code is never used
67-
enum used_enum { foo3, bar3 }
68+
enum used_enum {
69+
foo3,
70+
bar3 //~ ERROR code is never used
71+
}
6872

6973
fn f<T>() {}
7074

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ fn field_read(f: Foo) -> uint {
2828
}
2929

3030
enum XYZ {
31-
X,
32-
Y {
31+
X, //~ ERROR variant is never used
32+
Y { //~ ERROR variant is never used
3333
a: String,
3434
b: int //~ ERROR: code is never used
3535
},
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(struct_variant)]
12+
#![allow(unused_variable)]
13+
#![deny(dead_code)]
14+
15+
enum Enum1 {
16+
Variant1(int),
17+
Variant2 //~ ERROR: code is never used
18+
}
19+
20+
enum Enum2 {
21+
Variant3(bool),
22+
#[allow(dead_code)]
23+
Variant4(int),
24+
Variant5 { _x: int }, //~ ERROR: variant is never used: `Variant5`
25+
Variant6(int), //~ ERROR: variant is never used: `Variant6`
26+
_Variant7,
27+
}
28+
29+
enum Enum3 { //~ ERROR: enum is never used
30+
Variant8,
31+
Variant9
32+
}
33+
34+
fn main() {
35+
let v = Variant1(1);
36+
match v {
37+
Variant1(_) => (),
38+
Variant2 => ()
39+
}
40+
let x = Variant3(true);
41+
}

0 commit comments

Comments
 (0)