about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2015-03-13 18:12:05 +0530
committerManish Goregaokar <manishsmail@gmail.com>2015-03-13 18:12:05 +0530
commit0d37323fd3052ca893caa7ec7b1a7263a1cd0656 (patch)
treec9bf947cd7b86cd3a9d86984d7413a391c54bf3f
parent0e4b8d6117d767d78c61ff5416572ae5eaaa7440 (diff)
parentb042ffc4a768c2bd6d7588b1b2f47af22669c2cb (diff)
downloadrust-0d37323fd3052ca893caa7ec7b1a7263a1cd0656.tar.gz
rust-0d37323fd3052ca893caa7ec7b1a7263a1cd0656.zip
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-
-rw-r--r--src/librustc/middle/dead.rs28
-rw-r--r--src/librustc/middle/pat_util.rs24
-rw-r--r--src/libtest/lib.rs1
-rw-r--r--src/test/compile-fail/lint-dead-code-variant.rs42
4 files changed, 94 insertions, 1 deletions
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 5efea66ab0c..09378f2c973 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -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 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
             struct_has_extern_repr: false,
             ignore_non_const_paths: false,
             inherited_pub_visibility: false,
+            ignore_variant_stack: vec![],
         }
     }
 
@@ -79,7 +81,9 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
                 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 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
         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);
     }
 
diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs
index c5abff3b963..7f3b00eb3e6 100644
--- a/src/librustc/middle/pat_util.rs
+++ b/src/librustc/middle/pat_util.rs
@@ -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
+}
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index 19da658ed4f..80d5ab5baf3 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -121,6 +121,7 @@ impl fmt::Display for TestName {
 #[derive(Clone, Copy)]
 enum NamePadding {
     PadNone,
+    #[allow(dead_code)]
     PadOnLeft,
     PadOnRight,
 }
diff --git a/src/test/compile-fail/lint-dead-code-variant.rs b/src/test/compile-fail/lint-dead-code-variant.rs
new file mode 100644
index 00000000000..6146be65e38
--- /dev/null
+++ b/src/test/compile-fail/lint-dead-code-variant.rs
@@ -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);
+}