Add detection of dead struct fields

This commit is contained in:
Jakub Wieczorek 2014-06-06 00:00:29 +02:00
parent 7580ef902e
commit 0271224bda
3 changed files with 155 additions and 2 deletions

View file

@ -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 {

View file

@ -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)]

View file

@ -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 };
}