Files
rust/compiler/rustc_passes/src/dead.rs

708 lines
26 KiB
Rust
Raw Normal View History

2013-12-08 02:55:27 -05:00
// 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.
2019-12-24 05:02:53 +01:00
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{Node, PatKind, TyKind};
2020-03-29 17:19:48 +02:00
use rustc_middle::hir::map::Map;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy;
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::lint;
2020-04-27 23:26:11 +05:30
use rustc_ast as ast;
2020-04-19 13:00:18 +02:00
use rustc_span::symbol::{sym, Symbol};
2013-12-08 02:55:27 -05:00
// Any local node that may call something in its body block should be
2018-08-25 15:56:16 +01:00
// explored. For example, if it's a live Node::Item that is a
2013-12-08 02:55:27 -05:00
// function, then we should explore its block to check for codes that
// may need to be marked as live.
2019-06-21 23:49:03 +02:00
fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
2019-06-24 09:58:49 +02:00
match tcx.hir().find(hir_id) {
Some(
Node::Item(..)
| Node::ImplItem(..)
| Node::ForeignItem(..)
| Node::TraitItem(..)
| Node::Variant(..)
| Node::AnonConst(..)
| Node::Pat(..),
) => true,
2019-12-22 17:42:04 -05:00
_ => false,
2013-12-08 02:55:27 -05:00
}
}
struct MarkSymbolVisitor<'tcx> {
2019-02-26 11:01:11 +01:00
worklist: Vec<hir::HirId>,
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
2020-07-17 08:47:04 +00:00
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
2019-02-26 11:01:11 +01:00
live_symbols: FxHashSet<hir::HirId>,
repr_has_repr_c: bool,
2017-10-31 11:57:40 +09:00
in_pat: bool,
inherited_pub_visibility: bool,
ignore_variant_stack: Vec<DefId>,
// maps from tuple struct constructors to tuple struct items
2019-02-26 11:01:11 +01:00
struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
}
impl<'tcx> MarkSymbolVisitor<'tcx> {
2020-07-17 08:47:04 +00:00
/// Gets the type-checking results for the current body.
/// As this will ICE if called outside bodies, only call when working with
/// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
#[track_caller]
2020-07-17 08:47:04 +00:00
fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
self.maybe_typeck_results
.expect("`MarkSymbolVisitor::typeck_results` called outside of body")
}
2015-08-16 06:32:28 -04:00
fn check_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) {
2019-02-26 11:01:11 +01:00
self.worklist.push(hir_id);
}
2019-02-26 11:01:11 +01:00
self.live_symbols.insert(hir_id);
}
}
fn insert_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
2019-02-26 11:01:11 +01:00
debug_assert!(!should_explore(self.tcx, hir_id));
self.live_symbols.insert(hir_id);
}
}
fn handle_res(&mut self, res: Res) {
match res {
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, _) => {
self.check_def_id(res.def_id());
}
2019-12-22 17:42:04 -05:00
_ if self.in_pat => {}
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
let variant_id = self.tcx.parent(ctor_def_id).unwrap();
let enum_id = self.tcx.parent(variant_id).unwrap();
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&ctor_def_id) {
self.check_def_id(variant_id);
}
}
Res::Def(DefKind::Variant, variant_id) => {
let enum_id = self.tcx.parent(variant_id).unwrap();
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&variant_id) {
self.check_def_id(variant_id);
2014-09-20 13:26:10 +02:00
}
}
Res::SelfTy(t, i) => {
if let Some(t) = t {
self.check_def_id(t);
}
2020-09-01 14:30:16 +02:00
if let Some((i, _)) = i {
self.check_def_id(i);
}
}
Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
_ => {
self.check_def_id(res.def_id());
}
}
}
fn lookup_and_handle_method(&mut self, id: hir::HirId) {
2020-07-17 08:47:04 +00:00
if let Some(def_id) = self.typeck_results().type_dependent_def_id(id) {
self.check_def_id(def_id);
2018-06-28 22:49:01 +01:00
} else {
bug!("no type-dependent def for method");
}
2013-12-08 02:55:27 -05:00
}
2019-11-30 15:08:22 +01:00
fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) {
2020-08-03 00:49:11 +02:00
match self.typeck_results().expr_ty_adjusted(lhs).kind() {
ty::Adt(def, _) => {
2020-07-17 08:47:04 +00:00
let index = self.tcx.field_index(hir_id, self.typeck_results());
self.insert_def_id(def.non_enum_variant().fields[index].did);
}
ty::Tuple(..) => {}
_ => span_bug!(lhs.span, "named field access on non-ADT"),
}
}
2019-11-30 15:08:22 +01:00
fn handle_field_pattern_match(
&mut self,
lhs: &hir::Pat<'_>,
res: Res,
pats: &[hir::FieldPat<'_>],
) {
2020-08-03 00:49:11 +02:00
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
ty::Adt(adt, _) => adt.variant_of_res(res),
2019-12-22 17:42:04 -05:00
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
};
2015-01-31 12:20:46 -05:00
for pat in pats {
2019-09-26 16:18:31 +01:00
if let PatKind::Wild = pat.pat.kind {
continue;
}
2020-07-17 08:47:04 +00:00
let index = self.tcx.field_index(pat.hir_id, self.typeck_results());
self.insert_def_id(variant.fields[index].did);
2014-06-06 00:00:29 +02:00
}
}
2013-12-08 02:55:27 -05:00
fn mark_live_symbols(&mut self) {
let mut scanned = FxHashSet::default();
2018-10-02 18:05:06 +02:00
while let Some(id) = self.worklist.pop() {
if !scanned.insert(id) {
2019-12-22 17:42:04 -05:00
continue;
2013-12-08 02:55:27 -05:00
}
2013-12-27 16:09:29 -08:00
// in the case of tuple struct constructors we want to check the item, not the generated
// tuple struct constructor function
let id = self.struct_constructors.get(&id).cloned().unwrap_or(id);
2019-06-24 09:58:49 +02:00
if let Some(node) = self.tcx.hir().find(id) {
self.live_symbols.insert(id);
self.visit_node(node);
2013-12-08 02:55:27 -05:00
}
}
}
fn visit_node(&mut self, node: Node<'tcx>) {
let had_repr_c = self.repr_has_repr_c;
self.repr_has_repr_c = false;
let had_inherited_pub_visibility = self.inherited_pub_visibility;
self.inherited_pub_visibility = false;
match node {
2019-12-22 17:42:04 -05:00
Node::Item(item) => match item.kind {
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
let def_id = self.tcx.hir().local_def_id(item.hir_id);
let def = self.tcx.adt_def(def_id);
self.repr_has_repr_c = def.repr.c();
2019-12-22 17:42:04 -05:00
intravisit::walk_item(self, &item);
2013-12-08 02:55:27 -05:00
}
2019-12-22 17:42:04 -05:00
hir::ItemKind::Enum(..) => {
self.inherited_pub_visibility = item.vis.node.is_pub();
intravisit::walk_item(self, &item);
}
hir::ItemKind::ForeignMod(..) => {}
_ => {
intravisit::walk_item(self, &item);
}
},
2018-08-25 15:56:16 +01:00
Node::TraitItem(trait_item) => {
intravisit::walk_trait_item(self, trait_item);
2013-12-08 02:55:27 -05:00
}
2018-08-25 15:56:16 +01:00
Node::ImplItem(impl_item) => {
intravisit::walk_impl_item(self, impl_item);
2013-12-08 02:55:27 -05:00
}
2018-08-25 15:56:16 +01:00
Node::ForeignItem(foreign_item) => {
2016-02-09 22:00:20 +01:00
intravisit::walk_foreign_item(self, &foreign_item);
}
_ => {}
2013-12-08 02:55:27 -05:00
}
self.repr_has_repr_c = had_repr_c;
self.inherited_pub_visibility = had_inherited_pub_visibility;
2013-12-08 02:55:27 -05:00
}
2017-08-06 17:19:15 +02:00
2019-11-30 15:08:22 +01:00
fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &[hir::Field<'_>]) {
if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
for field in fields {
2020-07-17 08:47:04 +00:00
let index = self.tcx.field_index(field.hir_id, self.typeck_results());
self.insert_def_id(adt.non_enum_variant().fields[index].did);
2017-08-06 17:19:15 +02:00
}
}
}
2013-12-08 02:55:27 -05:00
}
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
2020-03-11 12:05:32 +01:00
type Map = intravisit::ErasedMap<'tcx>;
2020-01-07 17:25:33 +01:00
2020-02-09 15:32:00 +01:00
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body: hir::BodyId) {
2020-07-17 08:47:04 +00:00
let old_maybe_typeck_results =
self.maybe_typeck_results.replace(self.tcx.typeck_body(body));
let body = self.tcx.hir().body(body);
self.visit_body(body);
2020-07-17 08:47:04 +00:00
self.maybe_typeck_results = old_maybe_typeck_results;
2016-10-28 22:58:32 +02:00
}
2019-12-22 17:42:04 -05:00
fn visit_variant_data(
&mut self,
def: &'tcx hir::VariantData<'tcx>,
2020-04-19 13:00:18 +02:00
_: Symbol,
2019-12-01 16:08:58 +01:00
_: &hir::Generics<'_>,
2019-12-22 17:42:04 -05:00
_: hir::HirId,
_: rustc_span::Span,
2019-12-22 17:42:04 -05:00
) {
let has_repr_c = self.repr_has_repr_c;
let inherited_pub_visibility = self.inherited_pub_visibility;
2019-12-22 17:42:04 -05:00
let live_fields = def
.fields()
.iter()
.filter(|f| has_repr_c || inherited_pub_visibility || f.vis.node.is_pub());
2019-02-26 11:01:11 +01:00
self.live_symbols.extend(live_fields.map(|f| f.hir_id));
intravisit::walk_struct_def(self, def);
}
2013-12-08 02:55:27 -05:00
2019-11-30 15:08:22 +01:00
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
match expr.kind {
2018-07-11 20:05:29 +08:00
hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
2020-07-17 08:47:04 +00:00
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
self.handle_res(res);
}
2018-07-11 20:05:29 +08:00
hir::ExprKind::MethodCall(..) => {
self.lookup_and_handle_method(expr.hir_id);
2013-12-08 02:55:27 -05:00
}
2018-07-11 20:05:29 +08:00
hir::ExprKind::Field(ref lhs, ..) => {
2019-02-24 09:33:17 +01:00
self.handle_field_access(&lhs, expr.hir_id);
2014-06-06 00:00:29 +02:00
}
hir::ExprKind::Struct(ref qpath, ref fields, _) => {
2020-07-17 08:47:04 +00:00
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
self.handle_res(res);
2020-08-03 00:49:11 +02:00
if let ty::Adt(ref adt, _) = self.typeck_results().expr_ty(expr).kind() {
self.mark_as_used_if_union(adt, fields);
2017-08-06 20:46:32 +02:00
}
2017-08-06 18:49:33 +02:00
}
2019-12-22 17:42:04 -05:00
_ => (),
2013-12-08 02:55:27 -05:00
}
intravisit::walk_expr(self, expr);
2013-12-08 02:55:27 -05:00
}
2019-11-30 15:08:22 +01:00
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
// Inside the body, ignore constructions of variants
// necessary for the pattern to match. Those construction sites
// can't be reached unless the variant is constructed elsewhere.
let len = self.ignore_variant_stack.len();
self.ignore_variant_stack.extend(arm.pat.necessary_variants());
intravisit::walk_arm(self, arm);
self.ignore_variant_stack.truncate(len);
}
2019-11-30 15:08:22 +01:00
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
2019-09-26 16:18:31 +01:00
match pat.kind {
PatKind::Struct(ref path, ref fields, _) => {
2020-07-17 08:47:04 +00:00
let res = self.typeck_results().qpath_res(path, pat.hir_id);
self.handle_field_pattern_match(pat, res, fields);
2014-06-06 00:00:29 +02:00
}
PatKind::Path(ref qpath) => {
2020-07-17 08:47:04 +00:00
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
self.handle_res(res);
}
2019-12-22 17:42:04 -05:00
_ => (),
2014-06-06 00:00:29 +02:00
}
2017-10-31 11:57:40 +09:00
self.in_pat = true;
intravisit::walk_pat(self, pat);
2017-10-31 11:57:40 +09:00
self.in_pat = false;
2014-06-06 00:00:29 +02:00
}
2019-12-01 16:08:58 +01:00
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
self.handle_res(path.res);
intravisit::walk_path(self, path);
}
2019-03-12 21:35:20 +09:00
2019-12-01 16:08:58 +01:00
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
2020-06-07 18:56:17 +01:00
if let TyKind::OpaqueDef(item_id, _) = ty.kind {
let item = self.tcx.hir().expect_item(item_id.id);
intravisit::walk_item(self, item);
2019-03-12 21:35:20 +09:00
}
intravisit::walk_ty(self, ty);
}
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
self.live_symbols.insert(c.hir_id);
intravisit::walk_anon_const(self, c);
}
2013-12-08 02:55:27 -05:00
}
fn has_allow_dead_code_or_lang_attr(
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'_>,
id: hir::HirId,
attrs: &[ast::Attribute],
) -> bool {
if tcx.sess.contains_name(attrs, sym::lang) {
return true;
}
// Stable attribute for #[lang = "panic_impl"]
if tcx.sess.contains_name(attrs, sym::panic_handler) {
return true;
}
// (To be) stable attribute for #[lang = "oom"]
if tcx.sess.contains_name(attrs, sym::alloc_error_handler) {
return true;
}
let def_id = tcx.hir().local_def_id(id);
let cg_attrs = tcx.codegen_fn_attrs(def_id);
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
2019-12-22 17:42:04 -05:00
if cg_attrs.contains_extern_indicator() || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
return true;
}
rustc: Rearchitect lints to be emitted more eagerly In preparation for incremental compilation this commit refactors the lint handling infrastructure in the compiler to be more "eager" and overall more incremental-friendly. Many passes of the compiler can emit lints at various points but before this commit all lints were buffered in a table to be emitted at the very end of compilation. This commit changes these lints to be emitted immediately during compilation using pre-calculated lint level-related data structures. Linting today is split into two phases, one set of "early" lints run on the `syntax::ast` and a "late" set of lints run on the HIR. This commit moves the "early" lints to running as late as possible in compilation, just before HIR lowering. This notably means that we're catching resolve-related lints just before HIR lowering. The early linting remains a pass very similar to how it was before, maintaining context of the current lint level as it walks the tree. Post-HIR, however, linting is structured as a method on the `TyCtxt` which transitively executes a query to calculate lint levels. Each request to lint on a `TyCtxt` will query the entire crate's 'lint level data structure' and then go from there about whether the lint should be emitted or not. The query depends on the entire HIR crate but should be very quick to calculate (just a quick walk of the HIR) and the red-green system should notice that the lint level data structure rarely changes, and should hopefully preserve incrementality. Overall this resulted in a pretty big change to the test suite now that lints are emitted much earlier in compilation (on-demand vs only at the end). This in turn necessitated the addition of many `#![allow(warnings)]` directives throughout the compile-fail test suite and a number of updates to the UI test suite.
2017-07-26 21:51:09 -07:00
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
}
// 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
// * Implementations of traits and trait methods
struct LifeSeeder<'k, 'tcx> {
2019-02-26 11:01:11 +01:00
worklist: Vec<hir::HirId>,
2019-11-28 11:49:29 +01:00
krate: &'k hir::Crate<'k>,
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
// see `MarkSymbolVisitor::struct_constructors`
2019-02-26 11:01:11 +01:00
struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
2013-12-08 02:55:27 -05:00
}
rustc: Rearchitect lints to be emitted more eagerly In preparation for incremental compilation this commit refactors the lint handling infrastructure in the compiler to be more "eager" and overall more incremental-friendly. Many passes of the compiler can emit lints at various points but before this commit all lints were buffered in a table to be emitted at the very end of compilation. This commit changes these lints to be emitted immediately during compilation using pre-calculated lint level-related data structures. Linting today is split into two phases, one set of "early" lints run on the `syntax::ast` and a "late" set of lints run on the HIR. This commit moves the "early" lints to running as late as possible in compilation, just before HIR lowering. This notably means that we're catching resolve-related lints just before HIR lowering. The early linting remains a pass very similar to how it was before, maintaining context of the current lint level as it walks the tree. Post-HIR, however, linting is structured as a method on the `TyCtxt` which transitively executes a query to calculate lint levels. Each request to lint on a `TyCtxt` will query the entire crate's 'lint level data structure' and then go from there about whether the lint should be emitted or not. The query depends on the entire HIR crate but should be very quick to calculate (just a quick walk of the HIR) and the red-green system should notice that the lint level data structure rarely changes, and should hopefully preserve incrementality. Overall this resulted in a pretty big change to the test suite now that lints are emitted much earlier in compilation (on-demand vs only at the end). This in turn necessitated the addition of many `#![allow(warnings)]` directives throughout the compile-fail test suite and a number of updates to the UI test suite.
2017-07-26 21:51:09 -07:00
impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
2019-11-28 19:28:50 +01:00
fn visit_item(&mut self, item: &hir::Item<'_>) {
2019-12-22 17:42:04 -05:00
let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id, &item.attrs);
2014-09-20 13:26:10 +02:00
if allow_dead_code {
2019-02-26 11:01:11 +01:00
self.worklist.push(item.hir_id);
}
2019-09-26 17:51:36 +01:00
match item.kind {
hir::ItemKind::Enum(ref enum_def, _) => {
if allow_dead_code {
2019-08-13 21:40:21 -03:00
self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id));
}
2019-11-29 09:26:18 +01:00
for variant in enum_def.variants {
2019-08-13 21:40:21 -03:00
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
self.struct_constructors.insert(ctor_hir_id, variant.id);
}
}
2014-09-20 13:26:10 +02:00
}
2019-11-28 19:28:50 +01:00
hir::ItemKind::Trait(.., trait_item_refs) => {
for trait_item_ref in trait_item_refs {
let trait_item = self.krate.trait_item(trait_item_ref.id);
match trait_item.kind {
2019-12-22 17:42:04 -05:00
hir::TraitItemKind::Const(_, Some(_))
2020-03-05 09:57:34 -06:00
| hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => {
2019-12-22 17:42:04 -05:00
if has_allow_dead_code_or_lang_attr(
self.tcx,
trait_item.hir_id,
&trait_item.attrs,
) {
2019-02-26 11:01:11 +01:00
self.worklist.push(trait_item.hir_id);
}
}
_ => {}
}
}
}
hir::ItemKind::Impl { ref of_trait, items, .. } => {
if of_trait.is_some() {
self.worklist.push(item.hir_id);
}
for impl_item_ref in items {
let impl_item = self.krate.impl_item(impl_item_ref.id);
if of_trait.is_some()
2019-12-22 17:42:04 -05:00
|| has_allow_dead_code_or_lang_attr(
self.tcx,
impl_item.hir_id,
&impl_item.attrs,
)
{
2019-03-01 10:28:13 +01:00
self.worklist.push(impl_item_ref.id.hir_id);
}
2013-12-08 02:55:27 -05:00
}
}
hir::ItemKind::Struct(ref variant_data, _) => {
if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
self.struct_constructors.insert(ctor_hir_id, item.hir_id);
}
}
2019-12-22 17:42:04 -05:00
_ => (),
}
}
2019-11-28 21:47:10 +01:00
fn visit_trait_item(&mut self, _item: &hir::TraitItem<'_>) {
// ignore: we are handling this in `visit_item` above
}
2019-11-28 22:16:44 +01:00
fn visit_impl_item(&mut self, _item: &hir::ImplItem<'_>) {
// ignore: we are handling this in `visit_item` above
}
2013-12-08 02:55:27 -05:00
}
fn create_and_seed_worklist<'tcx>(
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
2019-11-28 11:49:29 +01:00
krate: &hir::Crate<'_>,
2019-02-26 11:01:11 +01:00
) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) {
2019-12-22 17:42:04 -05:00
let worklist = access_levels
.map
.iter()
.filter_map(
2020-10-14 10:43:51 +02:00
|(&id, &level)| {
if level >= privacy::AccessLevel::Reachable { Some(id) } else { None }
2019-12-22 17:42:04 -05:00
},
)
.chain(
// Seed entry point
tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().local_def_id_to_hir_id(def_id)),
2019-12-22 17:42:04 -05:00
)
.collect::<Vec<_>>();
2013-12-08 02:55:27 -05:00
// Seed implemented trait items
2019-12-22 17:42:04 -05:00
let mut life_seeder =
LifeSeeder { worklist, krate, tcx, struct_constructors: Default::default() };
krate.visit_all_item_likes(&mut life_seeder);
2013-12-08 02:55:27 -05:00
(life_seeder.worklist, life_seeder.struct_constructors)
2013-12-08 02:55:27 -05:00
}
fn find_live<'tcx>(
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
access_levels: &privacy::AccessLevels,
2019-11-28 11:49:29 +01:00
krate: &hir::Crate<'_>,
) -> FxHashSet<hir::HirId> {
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate);
let mut symbol_visitor = MarkSymbolVisitor {
worklist,
tcx,
2020-07-17 08:47:04 +00:00
maybe_typeck_results: None,
2018-10-16 17:21:55 +02:00
live_symbols: Default::default(),
repr_has_repr_c: false,
2017-10-31 11:57:40 +09:00
in_pat: false,
inherited_pub_visibility: false,
ignore_variant_stack: vec![],
struct_constructors,
};
2013-12-08 02:55:27 -05:00
symbol_visitor.mark_live_symbols();
symbol_visitor.live_symbols
}
struct DeadVisitor<'tcx> {
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
2019-02-26 11:01:11 +01:00
live_symbols: FxHashSet<hir::HirId>,
2013-12-08 02:55:27 -05:00
}
impl DeadVisitor<'tcx> {
2019-11-28 19:28:50 +01:00
fn should_warn_about_item(&mut self, item: &hir::Item<'_>) -> bool {
2019-09-26 17:51:36 +01:00
let should_warn = match item.kind {
2018-07-11 23:36:06 +08:00
hir::ItemKind::Static(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::TyAlias(..)
2018-07-11 23:36:06 +08:00
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..) => true,
2019-12-22 17:42:04 -05:00
_ => false,
2014-09-20 13:26:10 +02:00
};
2019-02-26 11:01:11 +01:00
should_warn && !self.symbol_is_live(item.hir_id)
2014-09-20 13:26:10 +02:00
}
2019-11-29 09:40:33 +01:00
fn should_warn_about_field(&mut self, field: &hir::StructField<'_>) -> bool {
let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id));
!field.is_positional()
2019-02-26 11:01:11 +01:00
&& !self.symbol_is_live(field.hir_id)
2018-07-14 17:22:53 +02:00
&& !field_type.is_phantom_data()
2019-02-26 11:01:11 +01:00
&& !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs)
2014-06-06 00:00:29 +02:00
}
2019-11-29 09:26:18 +01:00
fn should_warn_about_variant(&mut self, variant: &hir::Variant<'_>) -> bool {
!self.symbol_is_live(variant.id)
2019-12-22 17:42:04 -05:00
&& !has_allow_dead_code_or_lang_attr(self.tcx, variant.id, &variant.attrs)
2014-09-20 13:26:10 +02:00
}
2019-11-28 20:18:29 +01:00
fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem<'_>) -> bool {
2019-02-26 11:01:11 +01:00
!self.symbol_is_live(fi.hir_id)
&& !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id, &fi.attrs)
}
2019-02-26 11:01:11 +01:00
// id := HIR id of an item's definition.
2019-12-22 17:42:04 -05:00
fn symbol_is_live(&mut self, id: hir::HirId) -> bool {
if self.live_symbols.contains(&id) {
2013-12-08 02:55:27 -05:00
return true;
}
// If it's a type whose items are live, then it's live, too.
2013-12-08 02:55:27 -05:00
// 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.
let def_id = self.tcx.hir().local_def_id(id);
let inherent_impls = self.tcx.inherent_impls(def_id);
for &impl_did in inherent_impls.iter() {
2020-10-14 10:48:03 +02:00
for item_did in self.tcx.associated_item_def_ids(impl_did) {
2020-04-23 20:48:40 +01:00
if let Some(did) = item_did.as_local() {
let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(did);
2019-02-26 11:01:11 +01:00
if self.live_symbols.contains(&item_hir_id) {
return true;
2013-12-08 02:55:27 -05:00
}
}
}
}
false
}
2019-12-22 17:42:04 -05:00
fn warn_dead_code(
&mut self,
id: hir::HirId,
span: rustc_span::Span,
2020-04-19 13:00:18 +02:00
name: Symbol,
2019-12-22 17:42:04 -05:00
participle: &str,
) {
if !name.as_str().starts_with('_') {
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
let def_id = self.tcx.hir().local_def_id(id);
2020-04-24 14:26:11 -05:00
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit()
});
}
}
2013-12-08 02:55:27 -05:00
}
impl Visitor<'tcx> for DeadVisitor<'tcx> {
2020-01-07 17:25:33 +01:00
type Map = Map<'tcx>;
/// Walk nested items in place so that we don't report dead-code
/// on inner functions when the outer function is already getting
/// an error. We could do this also by checking the parents, but
/// this is how the code is setup and it seems harmless enough.
2020-02-09 15:32:00 +01:00
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::All(self.tcx.hir())
}
2019-11-28 19:28:50 +01:00
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
2014-09-20 13:26:10 +02:00
if self.should_warn_about_item(item) {
// For most items, we want to highlight its identifier
2019-09-26 17:51:36 +01:00
let span = match item.kind {
2019-12-22 17:42:04 -05:00
hir::ItemKind::Fn(..)
| hir::ItemKind::Mod(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..)
| hir::ItemKind::Trait(..)
| hir::ItemKind::Impl { .. } => {
// FIXME(66095): Because item.span is annotated with things
// like expansion data, and ident.span isn't, we use the
// def_span method if it's part of a macro invocation
// (and thus has a source_callee set).
// We should probably annotate ident.span with the macro
// context, but that's a larger change.
if item.span.source_callee().is_some() {
2020-03-09 11:42:37 -07:00
self.tcx.sess.source_map().guess_head_span(item.span)
} else {
item.ident.span
}
2019-12-22 17:42:04 -05:00
}
2017-09-25 12:01:55 -07:00
_ => item.span,
};
2019-09-26 17:51:36 +01:00
let participle = match item.kind {
hir::ItemKind::Struct(..) => "constructed", // Issue #52325
2019-12-22 17:42:04 -05:00
_ => "used",
};
self.warn_dead_code(item.hir_id, span, item.ident.name, participle);
2014-09-20 13:26:10 +02:00
} else {
// Only continue if we didn't warn
intravisit::walk_item(self, item);
}
}
2019-12-22 17:42:04 -05:00
fn visit_variant(
&mut self,
variant: &'tcx hir::Variant<'tcx>,
2019-12-01 16:08:58 +01:00
g: &'tcx hir::Generics<'tcx>,
2019-12-22 17:42:04 -05:00
id: hir::HirId,
) {
2019-08-13 21:40:21 -03:00
if self.should_warn_about_variant(&variant) {
self.warn_dead_code(variant.id, variant.span, variant.ident.name, "constructed");
} else {
intravisit::walk_variant(self, variant, g, id);
2013-12-08 02:55:27 -05:00
}
}
2019-11-28 20:18:29 +01:00
fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) {
if self.should_warn_about_foreign_item(fi) {
self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name, "used");
}
intravisit::walk_foreign_item(self, fi);
}
2019-11-29 09:40:33 +01:00
fn visit_struct_field(&mut self, field: &'tcx hir::StructField<'tcx>) {
2017-08-06 17:19:15 +02:00
if self.should_warn_about_field(&field) {
self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read");
2014-06-06 00:00:29 +02:00
}
intravisit::walk_struct_field(self, field);
2014-06-06 00:00:29 +02:00
}
2019-11-28 22:16:44 +01:00
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
match impl_item.kind {
hir::ImplItemKind::Const(_, body_id) => {
2019-02-26 11:01:11 +01:00
if !self.symbol_is_live(impl_item.hir_id) {
2019-12-22 17:42:04 -05:00
self.warn_dead_code(
impl_item.hir_id,
impl_item.span,
impl_item.ident.name,
"used",
);
}
self.visit_nested_body(body_id)
}
2020-03-05 09:57:34 -06:00
hir::ImplItemKind::Fn(_, body_id) => {
2019-02-26 11:01:11 +01:00
if !self.symbol_is_live(impl_item.hir_id) {
// FIXME(66095): Because impl_item.span is annotated with things
// like expansion data, and ident.span isn't, we use the
// def_span method if it's part of a macro invocation
// (and thus has a source_callee set).
// We should probably annotate ident.span with the macro
// context, but that's a larger change.
let span = if impl_item.span.source_callee().is_some() {
self.tcx.sess.source_map().guess_head_span(impl_item.span)
} else {
impl_item.ident.span
};
self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "used");
}
self.visit_nested_body(body_id)
}
hir::ImplItemKind::TyAlias(..) => {}
}
}
// Overwrite so that we don't warn the trait item itself.
2019-11-28 21:47:10 +01:00
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
match trait_item.kind {
2019-12-22 17:42:04 -05:00
hir::TraitItemKind::Const(_, Some(body_id))
2020-03-05 09:57:34 -06:00
| hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => {
self.visit_nested_body(body_id)
2016-10-28 22:58:32 +02:00
}
2019-12-22 17:42:04 -05:00
hir::TraitItemKind::Const(_, None)
2020-03-05 09:57:34 -06:00
| hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_))
2019-12-22 17:42:04 -05:00
| hir::TraitItemKind::Type(..) => {}
2013-12-08 02:55:27 -05:00
}
}
}
2019-06-21 23:49:03 +02:00
pub fn check_crate(tcx: TyCtxt<'_>) {
let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
let krate = tcx.hir().krate();
let live_symbols = find_live(tcx, access_levels, krate);
2019-12-22 17:42:04 -05:00
let mut visitor = DeadVisitor { tcx, live_symbols };
intravisit::walk_crate(&mut visitor, krate);
2013-12-08 02:55:27 -05:00
}