2013-12-08 01:55:27 -06:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// This implements the dead-code warning pass. It follows middle::reachable
|
|
|
|
// closely. The idea is that all reachable symbols are live, codes called
|
|
|
|
// from live codes are live, and everything else is dead.
|
|
|
|
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def;
|
2014-09-20 06:26:10 -05:00
|
|
|
use middle::pat_util;
|
2014-01-11 02:21:53 -06:00
|
|
|
use middle::privacy;
|
2013-12-08 01:55:27 -06:00
|
|
|
use middle::ty;
|
|
|
|
use middle::typeck;
|
2014-09-20 06:26:10 -05:00
|
|
|
use lint;
|
2014-02-28 16:34:26 -06:00
|
|
|
use util::nodemap::NodeSet;
|
2013-12-08 01:55:27 -06:00
|
|
|
|
2014-05-29 21:03:06 -05:00
|
|
|
use std::collections::HashSet;
|
2013-12-08 01:55:27 -06:00
|
|
|
use syntax::ast;
|
|
|
|
use syntax::ast_map;
|
2014-07-14 18:31:52 -05:00
|
|
|
use syntax::ast_util::{local_def, is_local, PostExpansionMethod};
|
2014-09-20 06:26:10 -05:00
|
|
|
use syntax::attr::{mod, AttrMetaMethods};
|
2013-12-08 01:55:27 -06:00
|
|
|
use syntax::codemap;
|
2014-09-20 06:26:10 -05:00
|
|
|
use syntax::visit::{mod, Visitor};
|
2013-12-08 01:55:27 -06:00
|
|
|
|
|
|
|
// Any local node that may call something in its body block should be
|
2014-01-09 07:05:33 -06:00
|
|
|
// explored. For example, if it's a live NodeItem that is a
|
2013-12-08 01:55:27 -06:00
|
|
|
// function, then we should explore its block to check for codes that
|
|
|
|
// may need to be marked as live.
|
2014-03-05 21:07:47 -06:00
|
|
|
fn should_explore(tcx: &ty::ctxt, def_id: ast::DefId) -> bool {
|
2013-12-08 01:55:27 -06:00
|
|
|
if !is_local(def_id) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-12-27 18:09:29 -06:00
|
|
|
|
2014-02-13 23:07:09 -06:00
|
|
|
match tcx.map.find(def_id.node) {
|
2014-01-17 06:23:09 -06:00
|
|
|
Some(ast_map::NodeItem(..))
|
2014-08-04 15:56:56 -05:00
|
|
|
| Some(ast_map::NodeImplItem(..))
|
2014-01-17 06:23:09 -06:00
|
|
|
| Some(ast_map::NodeForeignItem(..))
|
2014-08-04 15:56:56 -05:00
|
|
|
| Some(ast_map::NodeTraitItem(..)) => true,
|
2013-12-08 01:55:27 -06:00
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct MarkSymbolVisitor<'a, 'tcx: 'a> {
|
2014-03-05 21:07:47 -06:00
|
|
|
worklist: Vec<ast::NodeId>,
|
2014-04-22 07:56:37 -05:00
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2014-05-05 20:56:44 -05:00
|
|
|
live_symbols: Box<HashSet<ast::NodeId>>,
|
2014-09-20 06:26:10 -05:00
|
|
|
struct_has_extern_repr: bool,
|
2014-11-08 18:54:33 -06:00
|
|
|
ignore_non_const_paths: bool,
|
|
|
|
inherited_pub_visibility: bool,
|
2014-06-12 01:38:30 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
|
|
|
|
fn new(tcx: &'a ty::ctxt<'tcx>,
|
|
|
|
worklist: Vec<ast::NodeId>) -> MarkSymbolVisitor<'a, 'tcx> {
|
2013-12-08 01:55:27 -06:00
|
|
|
MarkSymbolVisitor {
|
|
|
|
worklist: worklist,
|
|
|
|
tcx: tcx,
|
2014-04-25 03:08:02 -05:00
|
|
|
live_symbols: box HashSet::new(),
|
2014-09-20 06:26:10 -05:00
|
|
|
struct_has_extern_repr: false,
|
2014-11-08 18:54:33 -06:00
|
|
|
ignore_non_const_paths: false,
|
|
|
|
inherited_pub_visibility: false,
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-31 15:19:57 -06:00
|
|
|
fn check_def_id(&mut self, def_id: ast::DefId) {
|
|
|
|
if should_explore(self.tcx, def_id) {
|
|
|
|
self.worklist.push(def_id.node);
|
|
|
|
}
|
|
|
|
self.live_symbols.insert(def_id.node);
|
|
|
|
}
|
|
|
|
|
2013-12-11 19:49:46 -06:00
|
|
|
fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) {
|
2014-11-06 11:25:16 -06:00
|
|
|
self.tcx.def_map.borrow().get(id).map(|def| {
|
2014-09-20 06:26:10 -05:00
|
|
|
match def {
|
2014-10-30 18:19:01 -05:00
|
|
|
&def::DefConst(_) => {
|
|
|
|
self.check_def_id(def.def_id())
|
|
|
|
}
|
|
|
|
_ if self.ignore_non_const_paths => (),
|
2014-09-20 06:26:10 -05:00
|
|
|
&def::DefPrimTy(_) => (),
|
|
|
|
&def::DefVariant(enum_id, variant_id, _) => {
|
|
|
|
self.check_def_id(enum_id);
|
|
|
|
self.check_def_id(variant_id);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.check_def_id(def.def_id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-12-31 15:19:57 -06:00
|
|
|
}
|
|
|
|
|
2014-03-06 11:24:11 -06:00
|
|
|
fn lookup_and_handle_method(&mut self, id: ast::NodeId,
|
2013-12-31 15:19:57 -06:00
|
|
|
span: codemap::Span) {
|
2014-03-06 11:24:11 -06:00
|
|
|
let method_call = typeck::MethodCall::expr(id);
|
2014-11-06 11:25:16 -06:00
|
|
|
match self.tcx.method_map.borrow().get(&method_call) {
|
2014-02-26 08:06:45 -06:00
|
|
|
Some(method) => {
|
|
|
|
match method.origin {
|
2014-02-26 08:01:36 -06:00
|
|
|
typeck::MethodStatic(def_id) => {
|
2013-12-31 15:19:57 -06:00
|
|
|
match ty::provided_source(self.tcx, def_id) {
|
|
|
|
Some(p_did) => self.check_def_id(p_did),
|
|
|
|
None => self.check_def_id(def_id)
|
|
|
|
}
|
|
|
|
}
|
2014-05-29 00:26:56 -05:00
|
|
|
typeck::MethodStaticUnboxedClosure(_) => {}
|
2014-09-11 00:07:49 -05:00
|
|
|
typeck::MethodTypeParam(typeck::MethodParam {
|
2014-10-05 19:36:53 -05:00
|
|
|
ref trait_ref,
|
2013-12-31 15:19:57 -06:00
|
|
|
method_num: index,
|
|
|
|
..
|
2014-09-12 10:42:58 -05:00
|
|
|
}) |
|
2014-09-11 00:07:49 -05:00
|
|
|
typeck::MethodTraitObject(typeck::MethodObject {
|
2014-10-05 19:36:53 -05:00
|
|
|
ref trait_ref,
|
2013-12-31 15:19:57 -06:00
|
|
|
method_num: index,
|
|
|
|
..
|
|
|
|
}) => {
|
2014-08-04 15:56:56 -05:00
|
|
|
let trait_item = ty::trait_item(self.tcx,
|
2014-09-12 10:42:58 -05:00
|
|
|
trait_ref.def_id,
|
2014-08-04 15:56:56 -05:00
|
|
|
index);
|
|
|
|
match trait_item {
|
|
|
|
ty::MethodTraitItem(method) => {
|
|
|
|
self.check_def_id(method.def_id);
|
|
|
|
}
|
2014-08-05 21:44:21 -05:00
|
|
|
ty::TypeTraitItem(typedef) => {
|
|
|
|
self.check_def_id(typedef.def_id);
|
|
|
|
}
|
2014-08-04 15:56:56 -05:00
|
|
|
}
|
2013-12-31 15:19:57 -06:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
2013-12-31 15:19:57 -06:00
|
|
|
None => {
|
|
|
|
self.tcx.sess.span_bug(span,
|
|
|
|
"method call expression not \
|
|
|
|
in method map?!")
|
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-05 17:00:29 -05:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-09 22:54:33 -05:00
|
|
|
fn handle_tup_field_access(&mut self, lhs: &ast::Expr, idx: uint) {
|
|
|
|
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[idx].id;
|
|
|
|
self.live_symbols.insert(field_id.node);
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-05 19:36:53 -05:00
|
|
|
fn handle_field_pattern_match(&mut self, lhs: &ast::Pat,
|
|
|
|
pats: &[codemap::Spanned<ast::FieldPat>]) {
|
2014-10-15 01:05:01 -05:00
|
|
|
let id = match (*self.tcx.def_map.borrow())[lhs.id] {
|
|
|
|
def::DefVariant(_, id, _) => id,
|
2014-07-04 18:45:47 -05:00
|
|
|
_ => {
|
|
|
|
match ty::ty_to_def_id(ty::node_id_to_type(self.tcx,
|
|
|
|
lhs.id)) {
|
|
|
|
None => {
|
|
|
|
self.tcx.sess.span_bug(lhs.span,
|
|
|
|
"struct pattern wasn't of a \
|
|
|
|
type with a def ID?!")
|
|
|
|
}
|
|
|
|
Some(def_id) => def_id,
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
|
|
|
}
|
2014-07-04 18:45:47 -05:00
|
|
|
};
|
|
|
|
let fields = ty::lookup_struct_fields(self.tcx, id);
|
|
|
|
for pat in pats.iter() {
|
|
|
|
let field_id = fields.iter()
|
2014-10-05 19:36:53 -05:00
|
|
|
.find(|field| field.name == pat.node.ident.name).unwrap().id;
|
2014-07-04 18:45:47 -05:00
|
|
|
self.live_symbols.insert(field_id.node);
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-08 01:55:27 -06:00
|
|
|
fn mark_live_symbols(&mut self) {
|
|
|
|
let mut scanned = HashSet::new();
|
|
|
|
while self.worklist.len() > 0 {
|
2013-12-23 09:20:52 -06:00
|
|
|
let id = self.worklist.pop().unwrap();
|
2013-12-08 01:55:27 -06:00
|
|
|
if scanned.contains(&id) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
scanned.insert(id);
|
2013-12-27 18:09:29 -06:00
|
|
|
|
2014-02-13 23:07:09 -06:00
|
|
|
match self.tcx.map.find(id) {
|
2014-01-17 06:23:09 -06:00
|
|
|
Some(ref node) => {
|
2013-12-08 01:55:27 -06:00
|
|
|
self.live_symbols.insert(id);
|
|
|
|
self.visit_node(node);
|
|
|
|
}
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 07:05:33 -06:00
|
|
|
fn visit_node(&mut self, node: &ast_map::Node) {
|
2014-09-12 05:10:30 -05:00
|
|
|
let had_extern_repr = self.struct_has_extern_repr;
|
|
|
|
self.struct_has_extern_repr = false;
|
2014-11-08 18:54:33 -06:00
|
|
|
let had_inherited_pub_visibility = self.inherited_pub_visibility;
|
|
|
|
self.inherited_pub_visibility = false;
|
2013-12-08 01:55:27 -06:00
|
|
|
match *node {
|
2014-02-13 23:07:09 -06:00
|
|
|
ast_map::NodeItem(item) => {
|
2013-12-08 01:55:27 -06:00
|
|
|
match item.node {
|
2014-06-12 01:38:30 -05:00
|
|
|
ast::ItemStruct(..) => {
|
2014-09-12 05:10:30 -05:00
|
|
|
self.struct_has_extern_repr = item.attrs.iter().any(|attr| {
|
|
|
|
attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr)
|
|
|
|
.contains(&attr::ReprExtern)
|
2014-05-27 01:56:52 -05:00
|
|
|
});
|
2014-06-12 01:38:30 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_item(self, &*item);
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
2014-11-08 18:54:33 -06:00
|
|
|
ast::ItemEnum(..) => {
|
|
|
|
self.inherited_pub_visibility = item.vis == ast::Public;
|
|
|
|
visit::walk_item(self, &*item);
|
|
|
|
}
|
2014-01-09 07:05:33 -06:00
|
|
|
ast::ItemFn(..)
|
2014-06-12 01:38:30 -05:00
|
|
|
| ast::ItemTy(..)
|
rustc: Add `const` globals to the language
This change is an implementation of [RFC 69][rfc] which adds a third kind of
global to the language, `const`. This global is most similar to what the old
`static` was, and if you're unsure about what to use then you should use a
`const`.
The semantics of these three kinds of globals are:
* A `const` does not represent a memory location, but only a value. Constants
are translated as rvalues, which means that their values are directly inlined
at usage location (similar to a #define in C/C++). Constant values are, well,
constant, and can not be modified. Any "modification" is actually a
modification to a local value on the stack rather than the actual constant
itself.
Almost all values are allowed inside constants, whether they have interior
mutability or not. There are a few minor restrictions listed in the RFC, but
they should in general not come up too often.
* A `static` now always represents a memory location (unconditionally). Any
references to the same `static` are actually a reference to the same memory
location. Only values whose types ascribe to `Sync` are allowed in a `static`.
This restriction is in place because many threads may access a `static`
concurrently. Lifting this restriction (and allowing unsafe access) is a
future extension not implemented at this time.
* A `static mut` continues to always represent a memory location. All references
to a `static mut` continue to be `unsafe`.
This is a large breaking change, and many programs will need to be updated
accordingly. A summary of the breaking changes is:
* Statics may no longer be used in patterns. Statics now always represent a
memory location, which can sometimes be modified. To fix code, repurpose the
matched-on-`static` to a `const`.
static FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
change this code to:
const FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
* Statics may no longer refer to other statics by value. Due to statics being
able to change at runtime, allowing them to reference one another could
possibly lead to confusing semantics. If you are in this situation, use a
constant initializer instead. Note, however, that statics may reference other
statics by address, however.
* Statics may no longer be used in constant expressions, such as array lengths.
This is due to the same restrictions as listed above. Use a `const` instead.
[breaking-change]
[rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
|
|
|
| ast::ItemStatic(..)
|
|
|
|
| ast::ItemConst(..) => {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_item(self, &*item);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
2014-08-04 15:56:56 -05:00
|
|
|
ast_map::NodeTraitItem(trait_method) => {
|
2014-09-07 12:09:06 -05:00
|
|
|
visit::walk_trait_item(self, trait_method);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
2014-08-04 15:56:56 -05:00
|
|
|
ast_map::NodeImplItem(impl_item) => {
|
|
|
|
match *impl_item {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::MethodImplItem(ref method) => {
|
|
|
|
visit::walk_block(self, method.pe_body());
|
2014-08-04 15:56:56 -05:00
|
|
|
}
|
2014-08-05 21:44:21 -05:00
|
|
|
ast::TypeImplItem(_) => {}
|
2014-08-04 15:56:56 -05:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
2014-02-13 23:07:09 -06:00
|
|
|
ast_map::NodeForeignItem(foreign_item) => {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_foreign_item(self, &*foreign_item);
|
2013-12-13 23:08:26 -06:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
_ => ()
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
self.struct_has_extern_repr = had_extern_repr;
|
2014-11-08 18:54:33 -06:00
|
|
|
self.inherited_pub_visibility = had_inherited_pub_visibility;
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
|
2014-06-12 01:38:30 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_struct_def(&mut self, def: &ast::StructDef, _: ast::Ident,
|
|
|
|
_: &ast::Generics, _: ast::NodeId) {
|
|
|
|
let has_extern_repr = self.struct_has_extern_repr;
|
2014-11-08 18:54:33 -06:00
|
|
|
let inherited_pub_visibility = self.inherited_pub_visibility;
|
2014-06-12 01:38:30 -05:00
|
|
|
let live_fields = def.fields.iter().filter(|f| {
|
2014-11-08 18:54:33 -06:00
|
|
|
has_extern_repr || inherited_pub_visibility || match f.node.kind {
|
2014-06-12 01:38:30 -05:00
|
|
|
ast::NamedField(_, ast::Public) => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
self.live_symbols.extend(live_fields.map(|f| f.node.id));
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_struct_def(self, def);
|
2014-06-12 01:38:30 -05:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2013-12-08 01:55:27 -06:00
|
|
|
match expr.node {
|
|
|
|
ast::ExprMethodCall(..) => {
|
2014-03-06 11:24:11 -06:00
|
|
|
self.lookup_and_handle_method(expr.id, expr.span);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
2014-06-05 17:00:29 -05:00
|
|
|
ast::ExprField(ref lhs, ref ident, _) => {
|
2014-06-13 16:56:42 -05:00
|
|
|
self.handle_field_access(&**lhs, &ident.node);
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
2014-08-09 22:54:33 -05:00
|
|
|
ast::ExprTupField(ref lhs, idx, _) => {
|
|
|
|
self.handle_tup_field_access(&**lhs, idx.node);
|
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_expr(self, expr);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_pat(&mut self, pat: &ast::Pat) {
|
2014-09-20 06:26:10 -05:00
|
|
|
let def_map = &self.tcx.def_map;
|
2014-06-05 17:00:29 -05:00
|
|
|
match pat.node {
|
|
|
|
ast::PatStruct(_, ref fields, _) => {
|
|
|
|
self.handle_field_pattern_match(pat, fields.as_slice());
|
|
|
|
}
|
2014-09-20 06:26:10 -05:00
|
|
|
_ if pat_util::pat_is_const(def_map, pat) => {
|
2014-10-30 18:19:01 -05:00
|
|
|
// it might be the only use of a const
|
2014-06-30 20:02:14 -05:00
|
|
|
self.lookup_and_handle_definition(&pat.id)
|
|
|
|
}
|
2014-06-05 17:00:29 -05:00
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
|
2014-10-30 18:19:01 -05:00
|
|
|
self.ignore_non_const_paths = true;
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_pat(self, pat);
|
2014-10-30 18:19:01 -05:00
|
|
|
self.ignore_non_const_paths = false;
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId) {
|
2014-10-30 18:19:01 -05:00
|
|
|
self.lookup_and_handle_definition(&id);
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_path(self, path);
|
2013-12-11 19:49:46 -06:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_item(&mut self, _: &ast::Item) {
|
2013-12-08 01:55:27 -06:00
|
|
|
// Do not recurse into items. These items will be added to the
|
|
|
|
// worklist and recursed into manually if necessary.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-11 02:21:53 -06:00
|
|
|
fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
|
2014-06-04 16:35:58 -05:00
|
|
|
if attr::contains_name(attrs.as_slice(), "lang") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-13 17:03:26 -05:00
|
|
|
let dead_code = lint::builtin::DEAD_CODE.name_lower();
|
2014-09-14 22:27:36 -05:00
|
|
|
for attr in lint::gather_attrs(attrs).into_iter() {
|
2014-06-13 17:03:26 -05:00
|
|
|
match attr {
|
|
|
|
Ok((ref name, lint::Allow, _))
|
|
|
|
if name.get() == dead_code.as_slice() => return true,
|
2014-06-04 16:35:58 -05:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
2014-01-11 02:21:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// This visitor seeds items that
|
|
|
|
// 1) We want to explicitly consider as live:
|
|
|
|
// * Item annotated with #[allow(dead_code)]
|
|
|
|
// - This is done so that if we want to suppress warnings for a
|
|
|
|
// group of dead functions, we only have to annotate the "root".
|
|
|
|
// For example, if both `f` and `g` are dead and `f` calls `g`,
|
|
|
|
// then annotating `f` with `#[allow(dead_code)]` will suppress
|
|
|
|
// warning for both `f` and `g`.
|
|
|
|
// * Item annotated with #[lang=".."]
|
|
|
|
// - This is because lang items are always callable from elsewhere.
|
|
|
|
// or
|
|
|
|
// 2) We are not sure to be live or not
|
|
|
|
// * Implementation of a trait method
|
|
|
|
struct LifeSeeder {
|
2014-09-20 06:26:10 -05:00
|
|
|
worklist: Vec<ast::NodeId>
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'v> Visitor<'v> for LifeSeeder {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_item(&mut self, item: &ast::Item) {
|
2014-09-20 06:26:10 -05:00
|
|
|
let allow_dead_code = has_allow_dead_code_or_lang_attr(item.attrs.as_slice());
|
|
|
|
if allow_dead_code {
|
2014-01-11 02:21:53 -06:00
|
|
|
self.worklist.push(item.id);
|
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
match item.node {
|
2014-09-20 06:26:10 -05:00
|
|
|
ast::ItemEnum(ref enum_def, _) if allow_dead_code => {
|
|
|
|
self.worklist.extend(enum_def.variants.iter().map(|variant| variant.node.id));
|
|
|
|
}
|
2014-08-04 15:56:56 -05:00
|
|
|
ast::ItemImpl(_, Some(ref _trait_ref), _, ref impl_items) => {
|
|
|
|
for impl_item in impl_items.iter() {
|
|
|
|
match *impl_item {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::MethodImplItem(ref method) => {
|
2014-08-04 15:56:56 -05:00
|
|
|
self.worklist.push(method.id);
|
|
|
|
}
|
2014-08-05 21:44:21 -05:00
|
|
|
ast::TypeImplItem(_) => {}
|
2014-08-04 15:56:56 -05:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
2014-01-11 02:21:53 -06:00
|
|
|
_ => ()
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_item(self, item);
|
2014-01-11 02:21:53 -06:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
fn visit_fn(&mut self, fk: visit::FnKind<'v>,
|
|
|
|
_: &'v ast::FnDecl, block: &'v ast::Block,
|
2014-09-12 05:10:30 -05:00
|
|
|
_: codemap::Span, id: ast::NodeId) {
|
2014-01-11 02:21:53 -06:00
|
|
|
// Check for method here because methods are not ast::Item
|
2014-09-09 17:54:36 -05:00
|
|
|
match fk {
|
2014-01-11 02:21:53 -06:00
|
|
|
visit::FkMethod(_, _, method) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
if has_allow_dead_code_or_lang_attr(method.attrs.as_slice()) {
|
2014-01-11 02:21:53 -06:00
|
|
|
self.worklist.push(id);
|
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_block(self, block);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
fn create_and_seed_worklist(tcx: &ty::ctxt,
|
2013-12-08 01:55:27 -06:00
|
|
|
exported_items: &privacy::ExportedItems,
|
2014-02-28 16:34:26 -06:00
|
|
|
reachable_symbols: &NodeSet,
|
2014-03-04 12:02:49 -06:00
|
|
|
krate: &ast::Crate) -> Vec<ast::NodeId> {
|
|
|
|
let mut worklist = Vec::new();
|
2013-12-08 01:55:27 -06:00
|
|
|
|
|
|
|
// Preferably, we would only need to seed the worklist with reachable
|
|
|
|
// symbols. However, since the set of reachable symbols differs
|
|
|
|
// depending on whether a crate is built as bin or lib, and we want
|
|
|
|
// the warning to be consistent, we also seed the worklist with
|
|
|
|
// exported symbols.
|
2014-07-21 22:54:28 -05:00
|
|
|
for id in exported_items.iter() {
|
|
|
|
worklist.push(*id);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
2014-07-21 22:54:28 -05:00
|
|
|
for id in reachable_symbols.iter() {
|
|
|
|
worklist.push(*id);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Seed entry point
|
2014-03-28 12:29:55 -05:00
|
|
|
match *tcx.sess.entry_fn.borrow() {
|
2013-12-08 01:55:27 -06:00
|
|
|
Some((id, _)) => worklist.push(id),
|
|
|
|
None => ()
|
|
|
|
}
|
|
|
|
|
2014-04-20 23:49:39 -05:00
|
|
|
// Seed implemented trait methods
|
2014-01-11 02:21:53 -06:00
|
|
|
let mut life_seeder = LifeSeeder {
|
2013-12-08 01:55:27 -06:00
|
|
|
worklist: worklist
|
|
|
|
};
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_crate(&mut life_seeder, krate);
|
2013-12-08 01:55:27 -06:00
|
|
|
|
2014-01-11 02:21:53 -06:00
|
|
|
return life_seeder.worklist;
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
fn find_live(tcx: &ty::ctxt,
|
2013-12-08 01:55:27 -06:00
|
|
|
exported_items: &privacy::ExportedItems,
|
2014-02-28 16:34:26 -06:00
|
|
|
reachable_symbols: &NodeSet,
|
2014-02-05 15:15:24 -06:00
|
|
|
krate: &ast::Crate)
|
2014-05-05 20:56:44 -05:00
|
|
|
-> Box<HashSet<ast::NodeId>> {
|
2013-12-08 01:55:27 -06:00
|
|
|
let worklist = create_and_seed_worklist(tcx, exported_items,
|
2014-02-05 15:15:24 -06:00
|
|
|
reachable_symbols, krate);
|
2014-04-09 10:18:40 -05:00
|
|
|
let mut symbol_visitor = MarkSymbolVisitor::new(tcx, worklist);
|
2013-12-08 01:55:27 -06:00
|
|
|
symbol_visitor.mark_live_symbols();
|
|
|
|
symbol_visitor.live_symbols
|
|
|
|
}
|
|
|
|
|
2014-01-09 07:05:33 -06:00
|
|
|
fn get_struct_ctor_id(item: &ast::Item) -> Option<ast::NodeId> {
|
2013-12-08 01:55:27 -06:00
|
|
|
match item.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ItemStruct(ref struct_def, _) => struct_def.ctor_id,
|
2013-12-08 01:55:27 -06:00
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct DeadVisitor<'a, 'tcx: 'a> {
|
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2014-05-05 20:56:44 -05:00
|
|
|
live_symbols: Box<HashSet<ast::NodeId>>,
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
|
2014-09-20 06:26:10 -05:00
|
|
|
fn should_warn_about_item(&mut self, item: &ast::Item) -> bool {
|
|
|
|
let should_warn = match item.node {
|
|
|
|
ast::ItemStatic(..)
|
2014-10-10 10:31:13 -05:00
|
|
|
| ast::ItemConst(..)
|
2014-09-20 06:26:10 -05:00
|
|
|
| ast::ItemFn(..)
|
|
|
|
| ast::ItemEnum(..)
|
|
|
|
| ast::ItemStruct(..) => true,
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
let ctor_id = get_struct_ctor_id(item);
|
|
|
|
should_warn && !self.symbol_is_live(item.id, ctor_id)
|
|
|
|
}
|
|
|
|
|
2014-06-05 17:00:29 -05:00
|
|
|
fn should_warn_about_field(&mut self, node: &ast::StructField_) -> bool {
|
2014-08-03 01:40:06 -05:00
|
|
|
let is_named = node.ident().is_some();
|
2014-06-05 17:00:29 -05:00
|
|
|
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)
|
|
|
|
&& !is_marker_field
|
|
|
|
&& !has_allow_dead_code_or_lang_attr(node.attrs.as_slice())
|
|
|
|
}
|
|
|
|
|
2014-09-20 06:26:10 -05:00
|
|
|
fn should_warn_about_variant(&mut self, variant: &ast::Variant_) -> bool {
|
|
|
|
!self.symbol_is_live(variant.id, None)
|
|
|
|
&& !has_allow_dead_code_or_lang_attr(variant.attrs.as_slice())
|
|
|
|
}
|
|
|
|
|
2013-12-08 01:55:27 -06:00
|
|
|
// id := node id of an item's definition.
|
|
|
|
// ctor_id := `Some` if the item is a struct_ctor (tuple struct),
|
|
|
|
// `None` otherwise.
|
|
|
|
// If the item is a struct_ctor, then either its `id` or
|
|
|
|
// `ctor_id` (unwrapped) is in the live_symbols set. More specifically,
|
|
|
|
// DefMap maps the ExprPath of a struct_ctor to the node referred by
|
|
|
|
// `ctor_id`. On the other hand, in a statement like
|
|
|
|
// `type <ident> <generics> = <ty>;` where <ty> refers to a struct_ctor,
|
|
|
|
// DefMap maps <ty> to `id` instead.
|
|
|
|
fn symbol_is_live(&mut self, id: ast::NodeId,
|
|
|
|
ctor_id: Option<ast::NodeId>) -> bool {
|
|
|
|
if self.live_symbols.contains(&id)
|
2013-12-06 12:51:10 -06:00
|
|
|
|| ctor_id.map_or(false,
|
|
|
|
|ctor| self.live_symbols.contains(&ctor)) {
|
2013-12-08 01:55:27 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// If it's a type whose methods are live, then it's live, too.
|
|
|
|
// This is done to handle the case where, for example, the static
|
|
|
|
// method of a private type is used, but the type itself is never
|
|
|
|
// called directly.
|
2014-08-04 15:56:56 -05:00
|
|
|
let impl_items = self.tcx.impl_items.borrow();
|
2014-11-06 11:25:16 -06:00
|
|
|
match self.tcx.inherent_impls.borrow().get(&local_def(id)) {
|
2013-12-08 01:55:27 -06:00
|
|
|
None => (),
|
2014-04-22 11:06:43 -05:00
|
|
|
Some(impl_list) => {
|
2014-09-12 09:54:08 -05:00
|
|
|
for impl_did in impl_list.iter() {
|
2014-10-15 01:05:01 -05:00
|
|
|
for item_did in (*impl_items)[*impl_did].iter() {
|
2014-08-04 15:56:56 -05:00
|
|
|
if self.live_symbols.contains(&item_did.def_id()
|
|
|
|
.node) {
|
2013-12-08 01:55:27 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2013-12-13 23:08:26 -06:00
|
|
|
|
2014-05-09 20:45:36 -05:00
|
|
|
fn warn_dead_code(&mut self,
|
|
|
|
id: ast::NodeId,
|
|
|
|
span: codemap::Span,
|
2014-09-20 07:08:10 -05:00
|
|
|
ident: ast::Ident,
|
|
|
|
node_type: &str) {
|
2014-08-03 01:40:06 -05:00
|
|
|
let name = ident.as_str();
|
|
|
|
if !name.starts_with("_") {
|
|
|
|
self.tcx
|
|
|
|
.sess
|
|
|
|
.add_lint(lint::builtin::DEAD_CODE,
|
|
|
|
id,
|
|
|
|
span,
|
2014-09-20 07:08:10 -05:00
|
|
|
format!("{} is never used: `{}`", node_type, name));
|
2014-08-03 01:40:06 -05:00
|
|
|
}
|
2013-12-13 23:08:26 -06:00
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_item(&mut self, item: &ast::Item) {
|
2014-09-20 06:26:10 -05:00
|
|
|
if self.should_warn_about_item(item) {
|
2014-09-20 07:08:10 -05:00
|
|
|
self.warn_dead_code(item.id, item.span, item.ident, item.node.descriptive_variant());
|
2014-09-20 06:26:10 -05:00
|
|
|
} else {
|
|
|
|
match item.node {
|
|
|
|
ast::ItemEnum(ref enum_def, _) => {
|
|
|
|
for variant in enum_def.variants.iter() {
|
|
|
|
if self.should_warn_about_variant(&variant.node) {
|
2014-09-20 07:08:10 -05:00
|
|
|
self.warn_dead_code(variant.node.id, variant.span,
|
|
|
|
variant.node.name, "variant");
|
2014-09-20 06:26:10 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_item(self, item);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_foreign_item(&mut self, fi: &ast::ForeignItem) {
|
2013-12-13 23:08:26 -06:00
|
|
|
if !self.symbol_is_live(fi.id, None) {
|
2014-09-20 07:08:10 -05:00
|
|
|
self.warn_dead_code(fi.id, fi.span, fi.ident, fi.node.descriptive_variant());
|
2013-12-13 23:08:26 -06:00
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_foreign_item(self, fi);
|
2013-12-13 23:08:26 -06:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
fn visit_fn(&mut self, fk: visit::FnKind<'v>,
|
|
|
|
_: &'v ast::FnDecl, block: &'v ast::Block,
|
2014-09-12 05:10:30 -05:00
|
|
|
span: codemap::Span, id: ast::NodeId) {
|
2014-01-09 07:05:33 -06:00
|
|
|
// Have to warn method here because methods are not ast::Item
|
2014-09-09 17:54:36 -05:00
|
|
|
match fk {
|
|
|
|
visit::FkMethod(name, _, _) => {
|
2013-12-08 01:55:27 -06:00
|
|
|
if !self.symbol_is_live(id, None) {
|
2014-09-20 07:08:10 -05:00
|
|
|
self.warn_dead_code(id, span, name, "method");
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_block(self, block);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_struct_field(&mut self, field: &ast::StructField) {
|
2014-06-05 17:00:29 -05:00
|
|
|
if self.should_warn_about_field(&field.node) {
|
2014-09-20 07:08:10 -05:00
|
|
|
self.warn_dead_code(field.node.id, field.span,
|
|
|
|
field.node.ident().unwrap(), "struct field");
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_struct_field(self, field);
|
2014-06-05 17:00:29 -05:00
|
|
|
}
|
|
|
|
|
2013-12-08 01:55:27 -06:00
|
|
|
// Overwrite so that we don't warn the trait method itself.
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_trait_item(&mut self, trait_method: &ast::TraitItem) {
|
2013-12-08 01:55:27 -06:00
|
|
|
match *trait_method {
|
2014-08-04 15:56:56 -05:00
|
|
|
ast::ProvidedMethod(ref method) => {
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_block(self, &*method.pe_body())
|
2014-07-07 18:35:15 -05:00
|
|
|
}
|
2014-08-05 21:44:21 -05:00
|
|
|
ast::RequiredMethod(_) => {}
|
|
|
|
ast::TypeTraitItem(_) => {}
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
pub fn check_crate(tcx: &ty::ctxt,
|
2013-12-08 01:55:27 -06:00
|
|
|
exported_items: &privacy::ExportedItems,
|
2014-09-07 12:09:06 -05:00
|
|
|
reachable_symbols: &NodeSet) {
|
|
|
|
let krate = tcx.map.krate();
|
2014-04-09 10:18:40 -05:00
|
|
|
let live_symbols = find_live(tcx, exported_items,
|
2014-02-05 15:15:24 -06:00
|
|
|
reachable_symbols, krate);
|
2013-12-08 01:55:27 -06:00
|
|
|
let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols };
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_crate(&mut visitor, krate);
|
2013-12-08 01:55:27 -06:00
|
|
|
}
|