Skip to content

Commit 0271224

Browse files
author
Jakub Wieczorek
committed
Add detection of dead struct fields
1 parent 7580ef9 commit 0271224

File tree

3 files changed

+155
-2
lines changed

3 files changed

+155
-2
lines changed

src/librustc/middle/dead.rs

+79-2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,32 @@ impl<'a> MarkSymbolVisitor<'a> {
124124
}
125125
}
126126

127+
fn handle_field_access(&mut self, lhs: &ast::Expr, name: &ast::Ident) {
128+
match ty::get(ty::expr_ty_adjusted(self.tcx, lhs)).sty {
129+
ty::ty_struct(id, _) => {
130+
let fields = ty::lookup_struct_fields(self.tcx, id);
131+
let field_id = fields.iter()
132+
.find(|field| field.name == name.name).unwrap().id;
133+
self.live_symbols.insert(field_id.node);
134+
},
135+
_ => ()
136+
}
137+
}
138+
139+
fn handle_field_pattern_match(&mut self, lhs: &ast::Pat, pats: &[ast::FieldPat]) {
140+
match self.tcx.def_map.borrow().get(&lhs.id) {
141+
&def::DefStruct(id) | &def::DefVariant(_, id, _) => {
142+
let fields = ty::lookup_struct_fields(self.tcx, id);
143+
for pat in pats.iter() {
144+
let field_id = fields.iter()
145+
.find(|field| field.name == pat.ident.name).unwrap().id;
146+
self.live_symbols.insert(field_id.node);
147+
}
148+
}
149+
_ => ()
150+
}
151+
}
152+
127153
fn mark_live_symbols(&mut self) {
128154
let mut scanned = HashSet::new();
129155
while self.worklist.len() > 0 {
@@ -147,10 +173,22 @@ impl<'a> MarkSymbolVisitor<'a> {
147173
match *node {
148174
ast_map::NodeItem(item) => {
149175
match item.node {
176+
ast::ItemStruct(struct_def, _) => {
177+
let has_extern_repr = item.attrs.iter().fold(attr::ReprAny, |acc, attr| {
178+
attr::find_repr_attr(self.tcx.sess.diagnostic(), attr, acc)
179+
}) == attr::ReprExtern;
180+
let live_fields = struct_def.fields.iter().filter(|f| {
181+
has_extern_repr || match f.node.kind {
182+
ast::NamedField(_, ast::Public) => true,
183+
_ => false
184+
}
185+
});
186+
self.live_symbols.extend(live_fields.map(|f| f.node.id));
187+
visit::walk_item(self, item, ());
188+
}
150189
ast::ItemFn(..)
151190
| ast::ItemTy(..)
152191
| ast::ItemEnum(..)
153-
| ast::ItemStruct(..)
154192
| ast::ItemStatic(..) => {
155193
visit::walk_item(self, item, ());
156194
}
@@ -178,18 +216,32 @@ impl<'a> Visitor<()> for MarkSymbolVisitor<'a> {
178216
ast::ExprMethodCall(..) => {
179217
self.lookup_and_handle_method(expr.id, expr.span);
180218
}
219+
ast::ExprField(ref lhs, ref ident, _) => {
220+
self.handle_field_access(*lhs, ident);
221+
}
181222
_ => ()
182223
}
183224

184225
visit::walk_expr(self, expr, ())
185226
}
186227

228+
fn visit_pat(&mut self, pat: &ast::Pat, _: ()) {
229+
match pat.node {
230+
ast::PatStruct(_, ref fields, _) => {
231+
self.handle_field_pattern_match(pat, fields.as_slice());
232+
}
233+
_ => ()
234+
}
235+
236+
visit::walk_pat(self, pat, ())
237+
}
238+
187239
fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId, _: ()) {
188240
self.lookup_and_handle_definition(&id);
189241
visit::walk_path(self, path, ());
190242
}
191243

192-
fn visit_item(&mut self, _item: &ast::Item, _: ()) {
244+
fn visit_item(&mut self, _: &ast::Item, _: ()) {
193245
// Do not recurse into items. These items will be added to the
194246
// worklist and recursed into manually if necessary.
195247
}
@@ -317,6 +369,23 @@ struct DeadVisitor<'a> {
317369
}
318370

319371
impl<'a> DeadVisitor<'a> {
372+
fn should_warn_about_field(&mut self, node: &ast::StructField_) -> bool {
373+
let (is_named, has_leading_underscore) = match node.ident() {
374+
Some(ref ident) => (true, token::get_ident(*ident).get()[0] == ('_' as u8)),
375+
_ => (false, false)
376+
};
377+
let field_type = ty::node_id_to_type(self.tcx, node.id);
378+
let is_marker_field = match ty::ty_to_def_id(field_type) {
379+
Some(def_id) => self.tcx.lang_items.items().any(|(_, item)| *item == Some(def_id)),
380+
_ => false
381+
};
382+
is_named
383+
&& !self.symbol_is_live(node.id, None)
384+
&& !has_leading_underscore
385+
&& !is_marker_field
386+
&& !has_allow_dead_code_or_lang_attr(node.attrs.as_slice())
387+
}
388+
320389
// id := node id of an item's definition.
321390
// ctor_id := `Some` if the item is a struct_ctor (tuple struct),
322391
// `None` otherwise.
@@ -399,6 +468,14 @@ impl<'a> Visitor<()> for DeadVisitor<'a> {
399468
visit::walk_block(self, block, ());
400469
}
401470

471+
fn visit_struct_field(&mut self, field: &ast::StructField, _: ()) {
472+
if self.should_warn_about_field(&field.node) {
473+
self.warn_dead_code(field.node.id, field.span, field.node.ident().unwrap());
474+
}
475+
476+
visit::walk_struct_field(self, field, ());
477+
}
478+
402479
// Overwrite so that we don't warn the trait method itself.
403480
fn visit_trait_method(&mut self, trait_method: &ast::TraitMethod, _: ()) {
404481
match *trait_method {

src/libsyntax/ast.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,15 @@ pub struct StructField_ {
10461046
pub attrs: Vec<Attribute>,
10471047
}
10481048

1049+
impl StructField_ {
1050+
pub fn ident(&self) -> Option<Ident> {
1051+
match self.kind {
1052+
NamedField(ref ident, _) => Some(ident.clone()),
1053+
UnnamedField(_) => None
1054+
}
1055+
}
1056+
}
1057+
10491058
pub type StructField = Spanned<StructField_>;
10501059

10511060
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(struct_variant)]
12+
#![allow(unused_variable)]
13+
#![allow(non_camel_case_types)]
14+
#![deny(dead_code)]
15+
16+
extern crate libc;
17+
18+
use std::num;
19+
20+
struct Foo {
21+
x: uint,
22+
b: bool, //~ ERROR: code is never used
23+
marker: std::kinds::marker::NoCopy
24+
}
25+
26+
fn field_read(f: Foo) -> uint {
27+
num::pow(f.x, 2)
28+
}
29+
30+
enum XYZ {
31+
X,
32+
Y {
33+
a: String,
34+
b: int //~ ERROR: code is never used
35+
},
36+
Z
37+
}
38+
39+
fn field_match_in_patterns(b: XYZ) -> String {
40+
match b {
41+
Y { a: a, .. } => a,
42+
_ => "".to_string()
43+
}
44+
}
45+
46+
struct Bar {
47+
x: uint, //~ ERROR: code is never used
48+
b: bool,
49+
_guard: ()
50+
}
51+
52+
#[repr(C)]
53+
struct Baz {
54+
x: libc::c_uint
55+
}
56+
57+
fn field_match_in_let(f: Bar) -> bool {
58+
let Bar { b, .. } = f;
59+
b
60+
}
61+
62+
fn main() {
63+
field_read(Foo { x: 1, b: false, marker: std::kinds::marker::NoCopy });
64+
field_match_in_patterns(Z);
65+
field_match_in_let(Bar { x: 42u, b: true, _guard: () });
66+
let _ = Baz { x: 0 };
67+
}

0 commit comments

Comments
 (0)