Extend dead code lint to detect more unused enum variants
This commit is contained in:
parent
bce7a6f4a9
commit
b042ffc4a7
3 changed files with 93 additions and 1 deletions
|
@ -47,6 +47,7 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> {
|
||||||
struct_has_extern_repr: bool,
|
struct_has_extern_repr: bool,
|
||||||
ignore_non_const_paths: bool,
|
ignore_non_const_paths: bool,
|
||||||
inherited_pub_visibility: bool,
|
inherited_pub_visibility: bool,
|
||||||
|
ignore_variant_stack: Vec<ast::NodeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
||||||
|
@ -59,6 +60,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
||||||
struct_has_extern_repr: false,
|
struct_has_extern_repr: false,
|
||||||
ignore_non_const_paths: false,
|
ignore_non_const_paths: false,
|
||||||
inherited_pub_visibility: false,
|
inherited_pub_visibility: false,
|
||||||
|
ignore_variant_stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +81,10 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
||||||
&def::DefPrimTy(_) => (),
|
&def::DefPrimTy(_) => (),
|
||||||
&def::DefVariant(enum_id, variant_id, _) => {
|
&def::DefVariant(enum_id, variant_id, _) => {
|
||||||
self.check_def_id(enum_id);
|
self.check_def_id(enum_id);
|
||||||
|
if !self.ignore_variant_stack.contains(&variant_id.node) {
|
||||||
self.check_def_id(variant_id);
|
self.check_def_id(variant_id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.check_def_id(def.def_id());
|
self.check_def_id(def.def_id());
|
||||||
}
|
}
|
||||||
|
@ -283,6 +287,23 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_arm(&mut self, arm: &ast::Arm) {
|
||||||
|
if arm.pats.len() == 1 {
|
||||||
|
let pat = &*arm.pats[0];
|
||||||
|
let variants = pat_util::necessary_variants(&self.tcx.def_map, pat);
|
||||||
|
|
||||||
|
// Inside the body, ignore constructions of variants
|
||||||
|
// necessary for the pattern to match. Those construction sites
|
||||||
|
// can't be reached unless the variant is constructed elsewhere.
|
||||||
|
let len = self.ignore_variant_stack.len();
|
||||||
|
self.ignore_variant_stack.push_all(&*variants);
|
||||||
|
visit::walk_arm(self, arm);
|
||||||
|
self.ignore_variant_stack.truncate(len);
|
||||||
|
} else {
|
||||||
|
visit::walk_arm(self, arm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_pat(&mut self, pat: &ast::Pat) {
|
fn visit_pat(&mut self, pat: &ast::Pat) {
|
||||||
let def_map = &self.tcx.def_map;
|
let def_map = &self.tcx.def_map;
|
||||||
match pat.node {
|
match pat.node {
|
||||||
|
@ -401,6 +422,11 @@ fn create_and_seed_worklist(tcx: &ty::ctxt,
|
||||||
worklist.push(*id);
|
worklist.push(*id);
|
||||||
}
|
}
|
||||||
for id in reachable_symbols {
|
for id in reachable_symbols {
|
||||||
|
// Reachable variants can be dead, because we warn about
|
||||||
|
// variants never constructed, not variants never used.
|
||||||
|
if let Some(ast_map::NodeVariant(..)) = tcx.map.find(*id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
worklist.push(*id);
|
worklist.push(*id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,3 +140,27 @@ pub fn def_to_path(tcx: &ty::ctxt, id: ast::DefId) -> ast::Path {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return variants that are necessary to exist for the pattern to match.
|
||||||
|
pub fn necessary_variants(dm: &DefMap, pat: &ast::Pat) -> Vec<ast::NodeId> {
|
||||||
|
let mut variants = vec![];
|
||||||
|
walk_pat(pat, |p| {
|
||||||
|
match p.node {
|
||||||
|
ast::PatEnum(_, _) |
|
||||||
|
ast::PatIdent(_, _, None) |
|
||||||
|
ast::PatStruct(..) => {
|
||||||
|
match dm.borrow().get(&p.id) {
|
||||||
|
Some(&DefVariant(_, id, _)) => {
|
||||||
|
variants.push(id.node);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
variants.sort();
|
||||||
|
variants.dedup();
|
||||||
|
variants
|
||||||
|
}
|
||||||
|
|
42
src/test/compile-fail/lint-dead-code-variant.rs
Normal file
42
src/test/compile-fail/lint-dead-code-variant.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![deny(dead_code)]
|
||||||
|
|
||||||
|
#[derive(Copy)]
|
||||||
|
enum Enum {
|
||||||
|
Variant1, //~ ERROR: variant is never used
|
||||||
|
Variant2,
|
||||||
|
Variant3,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy(e: Enum) -> Enum {
|
||||||
|
use Enum::*;
|
||||||
|
match e {
|
||||||
|
Variant1 => Variant1,
|
||||||
|
Variant2 => Variant2,
|
||||||
|
Variant3 => Variant3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max(e: Enum) -> Enum {
|
||||||
|
use Enum::*;
|
||||||
|
match e {
|
||||||
|
Variant1 => Variant3,
|
||||||
|
Variant2 => Variant3,
|
||||||
|
Variant3 => Variant3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let e = Enum::Variant2;
|
||||||
|
copy(e);
|
||||||
|
max(e);
|
||||||
|
}
|
Loading…
Reference in a new issue