Skip to content

Commit

Permalink
Add detection of dead struct fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakub Wieczorek committed Jun 8, 2014
1 parent 7580ef9 commit 0271224
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
81 changes: 79 additions & 2 deletions src/librustc/middle/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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, ());
}
Expand Down Expand Up @@ -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.
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
67 changes: 67 additions & 0 deletions src/test/compile-fail/lint-dead-code-4.rs
Original file line number Diff line number Diff line change
@@ -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 };
}

0 comments on commit 0271224

Please sign in to comment.