Rollup merge of #21468 - sanxiyn:dead-variant, r=
This implements a wish suggested in #17410, detecting enum variants that are never constructed, even in the presence of `#[derive(Clone)]`. The implementation is general and not specific to `#[derive(Clone)]`. r? @jakub-
This commit is contained in:
commit
0d37323fd3
@ -47,6 +47,7 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> {
|
||||
struct_has_extern_repr: bool,
|
||||
ignore_non_const_paths: bool,
|
||||
inherited_pub_visibility: bool,
|
||||
ignore_variant_stack: Vec<ast::NodeId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
||||
@ -59,6 +60,7 @@ fn new(tcx: &'a ty::ctxt<'tcx>,
|
||||
struct_has_extern_repr: false,
|
||||
ignore_non_const_paths: false,
|
||||
inherited_pub_visibility: false,
|
||||
ignore_variant_stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +81,9 @@ fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) {
|
||||
def::DefPrimTy(_) => (),
|
||||
def::DefVariant(enum_id, variant_id, _) => {
|
||||
self.check_def_id(enum_id);
|
||||
self.check_def_id(variant_id);
|
||||
if !self.ignore_variant_stack.contains(&variant_id.node) {
|
||||
self.check_def_id(variant_id);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.check_def_id(def.def_id());
|
||||
@ -278,6 +282,23 @@ fn visit_expr(&mut self, expr: &ast::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) {
|
||||
let def_map = &self.tcx.def_map;
|
||||
match pat.node {
|
||||
@ -397,6 +418,11 @@ fn create_and_seed_worklist(tcx: &ty::ctxt,
|
||||
worklist.push(*id);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -155,3 +155,27 @@ pub fn def_to_path(tcx: &ty::ctxt, id: ast::DefId) -> ast::Path {
|
||||
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
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[derive(Clone, Copy)]
|
||||
enum NamePadding {
|
||||
PadNone,
|
||||
#[allow(dead_code)]
|
||||
PadOnLeft,
|
||||
PadOnRight,
|
||||
}
|
||||
|
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 New Issue
Block a user