about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJakub Wieczorek <jakub@jakub.cc>2014-06-06 00:00:29 +0200
committerJakub Wieczorek <jakub@jakub.cc>2014-06-08 13:30:04 +0200
commit0271224bdae26260ab498f47323997f9edb5879e (patch)
tree4b2b91ede68b072dcafa37b0fb2a338b1e6b0483 /src
parent7580ef902eaaf402219b47c3f92019c0644086db (diff)
Add detection of dead struct fields
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/dead.rs81
-rw-r--r--src/libsyntax/ast.rs9
-rw-r--r--src/test/compile-fail/lint-dead-code-4.rs67
3 files changed, 155 insertions, 2 deletions
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index fb19fbd70c6..46899ae19ca 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -124,6 +124,32 @@ impl<'a> MarkSymbolVisitor<'a> {
         }
     }
 
+    fn handle_field_access(&mut self, lhs: &ast::Expr, name: &ast::Ident) {
+        match ty::get(ty::expr_ty_adjusted(self.tcx, lhs)).sty {
+            ty::ty_struct(id, _) => {
+                let fields = ty::lookup_struct_fields(self.tcx, id);
+                let field_id = fields.iter()
+                    .find(|field| field.name == name.name).unwrap().id;
+                self.live_symbols.insert(field_id.node);
+            },
+            _ => ()
+        }
+    }
+
+    fn handle_field_pattern_match(&mut self, lhs: &ast::Pat, pats: &[ast::FieldPat]) {
+        match self.tcx.def_map.borrow().get(&lhs.id) {
+            &def::DefStruct(id) | &def::DefVariant(_, id, _) => {
+                let fields = ty::lookup_struct_fields(self.tcx, id);
+                for pat in pats.iter() {
+                    let field_id = fields.iter()
+                        .find(|field| field.name == pat.ident.name).unwrap().id;
+                    self.live_symbols.insert(field_id.node);
+                }
+            }
+            _ => ()
+        }
+    }
+
     fn mark_live_symbols(&mut self) {
         let mut scanned = HashSet::new();
         while self.worklist.len() > 0 {
@@ -147,10 +173,22 @@ impl<'a> MarkSymbolVisitor<'a> {
         match *node {
             ast_map::NodeItem(item) => {
                 match item.node {
+                    ast::ItemStruct(struct_def, _) => {
+                        let has_extern_repr = item.attrs.iter().fold(attr::ReprAny, |acc, attr| {
+                            attr::find_repr_attr(self.tcx.sess.diagnostic(), attr, acc)
+                        }) == attr::ReprExtern;
+                        let live_fields = struct_def.fields.iter().filter(|f| {
+                            has_extern_repr || match f.node.kind {
+                                ast::NamedField(_, ast::Public) => true,
+                                _ => false
+                            }
+                        });
+                        self.live_symbols.extend(live_fields.map(|f| f.node.id));
+                        visit::walk_item(self, item, ());
+                    }
                     ast::ItemFn(..)
                     | ast::ItemTy(..)
                     | ast::ItemEnum(..)
-                    | ast::ItemStruct(..)
                     | ast::ItemStatic(..) => {
                         visit::walk_item(self, item, ());
                     }
@@ -178,18 +216,32 @@ impl<'a> Visitor<()> for MarkSymbolVisitor<'a> {
             ast::ExprMethodCall(..) => {
                 self.lookup_and_handle_method(expr.id, expr.span);
             }
+            ast::ExprField(ref lhs, ref ident, _) => {
+                self.handle_field_access(*lhs, ident);
+            }
             _ => ()
         }
 
         visit::walk_expr(self, expr, ())
     }
 
+    fn visit_pat(&mut self, pat: &ast::Pat, _: ()) {
+        match pat.node {
+            ast::PatStruct(_, ref fields, _) => {
+                self.handle_field_pattern_match(pat, fields.as_slice());
+            }
+            _ => ()
+        }
+
+        visit::walk_pat(self, pat, ())
+    }
+
     fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId, _: ()) {
         self.lookup_and_handle_definition(&id);
         visit::walk_path(self, path, ());
     }
 
-    fn visit_item(&mut self, _item: &ast::Item, _: ()) {
+    fn visit_item(&mut self, _: &ast::Item, _: ()) {
         // Do not recurse into items. These items will be added to the
         // worklist and recursed into manually if necessary.
     }
@@ -317,6 +369,23 @@ struct DeadVisitor<'a> {
 }
 
 impl<'a> DeadVisitor<'a> {
+    fn should_warn_about_field(&mut self, node: &ast::StructField_) -> bool {
+        let (is_named, has_leading_underscore) = match node.ident() {
+            Some(ref ident) => (true, token::get_ident(*ident).get()[0] == ('_' as u8)),
+            _ => (false, false)
+        };
+        let field_type = ty::node_id_to_type(self.tcx, node.id);
+        let is_marker_field = match ty::ty_to_def_id(field_type) {
+            Some(def_id) => self.tcx.lang_items.items().any(|(_, item)| *item == Some(def_id)),
+            _ => false
+        };
+        is_named
+            && !self.symbol_is_live(node.id, None)
+            && !has_leading_underscore
+            && !is_marker_field
+            && !has_allow_dead_code_or_lang_attr(node.attrs.as_slice())
+    }
+
     // id := node id of an item's definition.
     // ctor_id := `Some` if the item is a struct_ctor (tuple struct),
     //            `None` otherwise.
@@ -399,6 +468,14 @@ impl<'a> Visitor<()> for DeadVisitor<'a> {
         visit::walk_block(self, block, ());
     }
 
+    fn visit_struct_field(&mut self, field: &ast::StructField, _: ()) {
+        if self.should_warn_about_field(&field.node) {
+            self.warn_dead_code(field.node.id, field.span, field.node.ident().unwrap());
+        }
+
+        visit::walk_struct_field(self, field, ());
+    }
+
     // Overwrite so that we don't warn the trait method itself.
     fn visit_trait_method(&mut self, trait_method: &ast::TraitMethod, _: ()) {
         match *trait_method {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 2bc24fd1eb3..e18484a68f3 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1046,6 +1046,15 @@ pub struct StructField_ {
     pub attrs: Vec<Attribute>,
 }
 
+impl StructField_ {
+    pub fn ident(&self) -> Option<Ident> {
+        match self.kind {
+            NamedField(ref ident, _) => Some(ident.clone()),
+            UnnamedField(_) => None
+        }
+    }
+}
+
 pub type StructField = Spanned<StructField_>;
 
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
diff --git a/src/test/compile-fail/lint-dead-code-4.rs b/src/test/compile-fail/lint-dead-code-4.rs
new file mode 100644
index 00000000000..718af1841b6
--- /dev/null
+++ b/src/test/compile-fail/lint-dead-code-4.rs
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+#![feature(struct_variant)]
+#![allow(unused_variable)]
+#![allow(non_camel_case_types)]
+#![deny(dead_code)]
+
+extern crate libc;
+
+use std::num;
+
+struct Foo {
+    x: uint,
+    b: bool, //~ ERROR: code is never used
+    marker: std::kinds::marker::NoCopy
+}
+
+fn field_read(f: Foo) -> uint {
+    num::pow(f.x, 2)
+}
+
+enum XYZ {
+    X,
+    Y {
+        a: String,
+        b: int //~ ERROR: code is never used
+    },
+    Z
+}
+
+fn field_match_in_patterns(b: XYZ) -> String {
+    match b {
+        Y { a: a, .. } => a,
+        _ => "".to_string()
+    }
+}
+
+struct Bar {
+    x: uint, //~ ERROR: code is never used
+    b: bool,
+    _guard: ()
+}
+
+#[repr(C)]
+struct Baz {
+    x: libc::c_uint
+}
+
+fn field_match_in_let(f: Bar) -> bool {
+    let Bar { b, .. } = f;
+    b
+}
+
+fn main() {
+    field_read(Foo { x: 1, b: false, marker: std::kinds::marker::NoCopy });
+    field_match_in_patterns(Z);
+    field_match_in_let(Bar { x: 42u, b: true, _guard: () });
+    let _ = Baz { x: 0 };
+}