diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index fb19fbd70c69..46899ae19ca0 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 2bc24fd1eb36..e18484a68f3a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1046,6 +1046,15 @@ pub struct StructField_ { pub attrs: Vec, } +impl StructField_ { + pub fn ident(&self) -> Option { + match self.kind { + NamedField(ref ident, _) => Some(ident.clone()), + UnnamedField(_) => None + } + } +} + pub type StructField = Spanned; #[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 000000000000..718af1841b62 --- /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 or the MIT license +// , 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 }; +}