Skip to content

Commit b042ffc

Browse files
committed
Extend dead code lint to detect more unused enum variants
1 parent bce7a6f commit b042ffc

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

src/librustc/middle/dead.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> {
4747
struct_has_extern_repr: bool,
4848
ignore_non_const_paths: bool,
4949
inherited_pub_visibility: bool,
50+
ignore_variant_stack: Vec<ast::NodeId>,
5051
}
5152

5253
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
@@ -59,6 +60,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
5960
struct_has_extern_repr: false,
6061
ignore_non_const_paths: false,
6162
inherited_pub_visibility: false,
63+
ignore_variant_stack: vec![],
6264
}
6365
}
6466

@@ -79,7 +81,9 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
7981
&def::DefPrimTy(_) => (),
8082
&def::DefVariant(enum_id, variant_id, _) => {
8183
self.check_def_id(enum_id);
82-
self.check_def_id(variant_id);
84+
if !self.ignore_variant_stack.contains(&variant_id.node) {
85+
self.check_def_id(variant_id);
86+
}
8387
}
8488
_ => {
8589
self.check_def_id(def.def_id());
@@ -283,6 +287,23 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
283287
visit::walk_expr(self, expr);
284288
}
285289

290+
fn visit_arm(&mut self, arm: &ast::Arm) {
291+
if arm.pats.len() == 1 {
292+
let pat = &*arm.pats[0];
293+
let variants = pat_util::necessary_variants(&self.tcx.def_map, pat);
294+
295+
// Inside the body, ignore constructions of variants
296+
// necessary for the pattern to match. Those construction sites
297+
// can't be reached unless the variant is constructed elsewhere.
298+
let len = self.ignore_variant_stack.len();
299+
self.ignore_variant_stack.push_all(&*variants);
300+
visit::walk_arm(self, arm);
301+
self.ignore_variant_stack.truncate(len);
302+
} else {
303+
visit::walk_arm(self, arm);
304+
}
305+
}
306+
286307
fn visit_pat(&mut self, pat: &ast::Pat) {
287308
let def_map = &self.tcx.def_map;
288309
match pat.node {
@@ -401,6 +422,11 @@ fn create_and_seed_worklist(tcx: &ty::ctxt,
401422
worklist.push(*id);
402423
}
403424
for id in reachable_symbols {
425+
// Reachable variants can be dead, because we warn about
426+
// variants never constructed, not variants never used.
427+
if let Some(ast_map::NodeVariant(..)) = tcx.map.find(*id) {
428+
continue;
429+
}
404430
worklist.push(*id);
405431
}
406432

src/librustc/middle/pat_util.rs

+24
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,27 @@ pub fn def_to_path(tcx: &ty::ctxt, id: ast::DefId) -> ast::Path {
140140
span: DUMMY_SP,
141141
})
142142
}
143+
144+
/// Return variants that are necessary to exist for the pattern to match.
145+
pub fn necessary_variants(dm: &DefMap, pat: &ast::Pat) -> Vec<ast::NodeId> {
146+
let mut variants = vec![];
147+
walk_pat(pat, |p| {
148+
match p.node {
149+
ast::PatEnum(_, _) |
150+
ast::PatIdent(_, _, None) |
151+
ast::PatStruct(..) => {
152+
match dm.borrow().get(&p.id) {
153+
Some(&DefVariant(_, id, _)) => {
154+
variants.push(id.node);
155+
}
156+
_ => ()
157+
}
158+
}
159+
_ => ()
160+
}
161+
true
162+
});
163+
variants.sort();
164+
variants.dedup();
165+
variants
166+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2015 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+
#![deny(dead_code)]
12+
13+
#[derive(Copy)]
14+
enum Enum {
15+
Variant1, //~ ERROR: variant is never used
16+
Variant2,
17+
Variant3,
18+
}
19+
20+
fn copy(e: Enum) -> Enum {
21+
use Enum::*;
22+
match e {
23+
Variant1 => Variant1,
24+
Variant2 => Variant2,
25+
Variant3 => Variant3,
26+
}
27+
}
28+
29+
fn max(e: Enum) -> Enum {
30+
use Enum::*;
31+
match e {
32+
Variant1 => Variant3,
33+
Variant2 => Variant3,
34+
Variant3 => Variant3,
35+
}
36+
}
37+
38+
fn main() {
39+
let e = Enum::Variant2;
40+
copy(e);
41+
max(e);
42+
}

0 commit comments

Comments
 (0)