2023-10-02 08:59:31 -04:00
|
|
|
// This implements the dead-code warning pass.
|
|
|
|
|
// All reachable symbols are live, code called from live code is live, code with certain lint
|
|
|
|
|
// expectations such as `#[expect(unused)]` and `#[expect(dead_code)]` is live, and everything else
|
|
|
|
|
// is dead.
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2023-02-21 12:19:54 +01:00
|
|
|
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
|
2024-01-02 00:14:21 +08:00
|
|
|
use hir::ItemKind;
|
2023-08-10 19:23:17 +02:00
|
|
|
use rustc_data_structures::unord::UnordSet;
|
2022-10-22 18:48:20 +08:00
|
|
|
use rustc_errors::MultiSpan;
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
|
|
|
|
use rustc_hir::def::{CtorOf, DefKind, Res};
|
2023-04-26 20:53:51 +02:00
|
|
|
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
|
2021-11-03 18:03:12 -05:00
|
|
|
use rustc_hir::intravisit::{self, Visitor};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::{Node, PatKind, TyKind};
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
2022-09-22 16:19:53 +03:00
|
|
|
use rustc_middle::middle::privacy::Level;
|
2023-05-15 06:24:45 +02:00
|
|
|
use rustc_middle::query::Providers;
|
2024-02-29 14:08:01 +08:00
|
|
|
use rustc_middle::ty::{self, TyCtxt};
|
2023-11-26 14:48:34 -05:00
|
|
|
use rustc_session::lint;
|
|
|
|
|
use rustc_session::lint::builtin::DEAD_CODE;
|
2020-04-19 13:00:18 +02:00
|
|
|
use rustc_span::symbol::{sym, Symbol};
|
2023-03-28 23:32:25 -07:00
|
|
|
use rustc_target::abi::FieldIdx;
|
2021-08-14 21:54:17 +01:00
|
|
|
use std::mem;
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2022-10-22 18:48:20 +08:00
|
|
|
use crate::errors::{
|
|
|
|
|
ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo,
|
|
|
|
|
UselessAssignment,
|
|
|
|
|
};
|
2022-10-01 18:39:02 -06:00
|
|
|
|
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.
|
2021-07-28 18:23:40 +03:00
|
|
|
fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
2020-12-24 02:55:21 +01:00
|
|
|
matches!(
|
2024-03-14 21:05:06 +03:00
|
|
|
tcx.hir_node_by_def_id(def_id),
|
|
|
|
|
Node::Item(..)
|
|
|
|
|
| Node::ImplItem(..)
|
|
|
|
|
| Node::ForeignItem(..)
|
|
|
|
|
| Node::TraitItem(..)
|
|
|
|
|
| Node::Variant(..)
|
|
|
|
|
| Node::AnonConst(..)
|
2020-12-24 02:55:21 +01:00
|
|
|
)
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-29 14:08:01 +08:00
|
|
|
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
|
|
|
|
|
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
|
|
|
|
&& let Res::Def(def_kind, def_id) = path.res
|
|
|
|
|
&& def_id.is_local()
|
|
|
|
|
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
|
|
|
|
|
{
|
|
|
|
|
tcx.visibility(def_id).is_public()
|
|
|
|
|
} else {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
/// Determine if a work from the worklist is coming from the a `#[allow]`
|
|
|
|
|
/// or a `#[expect]` of `dead_code`
|
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
|
|
|
enum ComesFromAllowExpect {
|
|
|
|
|
Yes,
|
|
|
|
|
No,
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-25 20:58:33 +03:00
|
|
|
struct MarkSymbolVisitor<'tcx> {
|
2023-08-10 19:23:17 +02:00
|
|
|
worklist: Vec<(LocalDefId, ComesFromAllowExpect)>,
|
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>>,
|
2023-02-21 12:19:54 +01:00
|
|
|
live_symbols: LocalDefIdSet,
|
2024-01-18 12:54:01 -05:00
|
|
|
repr_unconditionally_treats_fields_as_live: bool,
|
2022-07-25 22:36:03 +02:00
|
|
|
repr_has_repr_simd: bool,
|
2017-10-31 11:57:40 +09:00
|
|
|
in_pat: bool,
|
2015-09-04 13:52:28 -04:00
|
|
|
ignore_variant_stack: Vec<DefId>,
|
2018-12-18 15:52:32 +01:00
|
|
|
// maps from tuple struct constructors to tuple struct items
|
2023-02-21 12:19:54 +01:00
|
|
|
struct_constructors: LocalDefIdMap<LocalDefId>,
|
2022-01-11 21:06:18 +01:00
|
|
|
// maps from ADTs to ignored derived traits (e.g. Debug and Clone)
|
2022-01-15 21:01:44 +01:00
|
|
|
// and the span of their respective impl (i.e., part of the derive
|
|
|
|
|
// macro)
|
2023-02-21 12:19:54 +01:00
|
|
|
ignored_derived_traits: LocalDefIdMap<Vec<(DefId, DefId)>>,
|
2014-06-12 08:38:30 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-25 20:58:33 +03:00
|
|
|
impl<'tcx> MarkSymbolVisitor<'tcx> {
|
2020-07-17 08:47:04 +00:00
|
|
|
/// Gets the type-checking results for the current body.
|
2020-06-25 20:58:33 +03:00
|
|
|
/// 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")
|
2020-06-25 20:58:33 +03:00
|
|
|
}
|
|
|
|
|
|
2015-08-16 06:32:28 -04:00
|
|
|
fn check_def_id(&mut self, def_id: DefId) {
|
2020-04-12 13:45:41 +01:00
|
|
|
if let Some(def_id) = def_id.as_local() {
|
2021-07-28 18:23:40 +03:00
|
|
|
if should_explore(self.tcx, def_id) || self.struct_constructors.contains_key(&def_id) {
|
2023-08-10 19:23:17 +02:00
|
|
|
self.worklist.push((def_id, ComesFromAllowExpect::No));
|
2015-09-04 13:52:28 -04:00
|
|
|
}
|
2021-07-28 18:23:40 +03:00
|
|
|
self.live_symbols.insert(def_id);
|
2015-09-04 13:52:28 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn insert_def_id(&mut self, def_id: DefId) {
|
2020-04-12 13:45:41 +01:00
|
|
|
if let Some(def_id) = def_id.as_local() {
|
2021-07-28 18:23:40 +03:00
|
|
|
debug_assert!(!should_explore(self.tcx, def_id));
|
|
|
|
|
self.live_symbols.insert(def_id);
|
2013-12-31 16:19:57 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-20 19:36:05 +03:00
|
|
|
fn handle_res(&mut self, res: Res) {
|
|
|
|
|
match res {
|
2023-09-26 02:15:32 +00:00
|
|
|
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
|
2022-06-19 16:34:37 +02:00
|
|
|
self.check_def_id(def_id);
|
2016-06-03 23:15:00 +03:00
|
|
|
}
|
2019-03-28 23:33:17 +00:00
|
|
|
_ if self.in_pat => {}
|
2019-08-05 19:23:46 -07:00
|
|
|
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
|
2019-04-20 19:36:05 +03:00
|
|
|
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
|
2022-04-25 22:08:45 +03:00
|
|
|
let variant_id = self.tcx.parent(ctor_def_id);
|
|
|
|
|
let enum_id = self.tcx.parent(variant_id);
|
2019-03-21 23:38:50 +01:00
|
|
|
self.check_def_id(enum_id);
|
|
|
|
|
if !self.ignore_variant_stack.contains(&ctor_def_id) {
|
|
|
|
|
self.check_def_id(variant_id);
|
2016-09-08 19:05:50 +03:00
|
|
|
}
|
2019-03-21 23:38:50 +01:00
|
|
|
}
|
2019-04-20 19:36:05 +03:00
|
|
|
Res::Def(DefKind::Variant, variant_id) => {
|
2022-04-25 22:08:45 +03:00
|
|
|
let enum_id = self.tcx.parent(variant_id);
|
2019-03-21 23:38:50 +01:00
|
|
|
self.check_def_id(enum_id);
|
2016-06-03 23:15:00 +03:00
|
|
|
if !self.ignore_variant_stack.contains(&variant_id) {
|
|
|
|
|
self.check_def_id(variant_id);
|
2014-09-20 13:26:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-06-19 16:34:37 +02:00
|
|
|
Res::Def(_, def_id) => self.check_def_id(def_id),
|
2022-09-16 11:45:33 +10:00
|
|
|
Res::SelfTyParam { trait_: t } => self.check_def_id(t),
|
|
|
|
|
Res::SelfTyAlias { alias_to: i, .. } => self.check_def_id(i),
|
2019-04-20 19:36:05 +03:00
|
|
|
Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
|
2016-06-03 23:15:00 +03:00
|
|
|
}
|
2013-12-31 16:19:57 -05:00
|
|
|
}
|
|
|
|
|
|
2017-08-10 16:10:04 +02:00
|
|
|
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) {
|
2019-03-15 17:42:34 +01:00
|
|
|
self.check_def_id(def_id);
|
2018-06-28 22:49:01 +01:00
|
|
|
} else {
|
2024-01-16 10:03:29 +00:00
|
|
|
assert!(
|
|
|
|
|
self.typeck_results().tainted_by_errors.is_some(),
|
|
|
|
|
"no type-dependent def for method"
|
|
|
|
|
);
|
2018-06-28 22:49:01 +01:00
|
|
|
}
|
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() {
|
2018-08-22 01:35:02 +01:00
|
|
|
ty::Adt(def, _) => {
|
2022-12-04 17:59:21 +00:00
|
|
|
let index = self.typeck_results().field_index(hir_id);
|
2018-04-05 03:20:21 +03:00
|
|
|
self.insert_def_id(def.non_enum_variant().fields[index].did);
|
2016-08-10 21:00:17 +03:00
|
|
|
}
|
2018-08-22 01:35:02 +01:00
|
|
|
ty::Tuple(..) => {}
|
2018-04-01 21:48:39 +03:00
|
|
|
_ => span_bug!(lhs.span, "named field access on non-ADT"),
|
2014-08-10 15:54:33 +12:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-21 22:57:10 -04:00
|
|
|
#[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands.
|
2021-02-19 00:00:00 +00:00
|
|
|
fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
|
|
|
|
if self
|
|
|
|
|
.typeck_results()
|
|
|
|
|
.expr_adjustments(expr)
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
|
|
|
|
|
{
|
|
|
|
|
self.visit_expr(expr);
|
|
|
|
|
} else if let hir::ExprKind::Field(base, ..) = expr.kind {
|
|
|
|
|
// Ignore write to field
|
|
|
|
|
self.handle_assign(base);
|
|
|
|
|
} else {
|
|
|
|
|
self.visit_expr(expr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-21 22:57:10 -04:00
|
|
|
#[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands.
|
2021-07-14 15:34:28 +02:00
|
|
|
fn check_for_self_assign(&mut self, assign: &'tcx hir::Expr<'tcx>) {
|
2021-12-13 22:16:36 -07:00
|
|
|
fn check_for_self_assign_helper<'tcx>(
|
2021-07-14 15:34:28 +02:00
|
|
|
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
|
|
|
|
lhs: &'tcx hir::Expr<'tcx>,
|
|
|
|
|
rhs: &'tcx hir::Expr<'tcx>,
|
|
|
|
|
) -> bool {
|
|
|
|
|
match (&lhs.kind, &rhs.kind) {
|
|
|
|
|
(hir::ExprKind::Path(ref qpath_l), hir::ExprKind::Path(ref qpath_r)) => {
|
|
|
|
|
if let (Res::Local(id_l), Res::Local(id_r)) = (
|
|
|
|
|
typeck_results.qpath_res(qpath_l, lhs.hir_id),
|
|
|
|
|
typeck_results.qpath_res(qpath_r, rhs.hir_id),
|
2022-06-10 12:14:24 +09:00
|
|
|
) {
|
|
|
|
|
if id_l == id_r {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-07-14 15:34:28 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
(hir::ExprKind::Field(lhs_l, ident_l), hir::ExprKind::Field(lhs_r, ident_r)) => {
|
|
|
|
|
if ident_l == ident_r {
|
2022-04-13 23:07:20 +02:00
|
|
|
return check_for_self_assign_helper(typeck_results, lhs_l, lhs_r);
|
2021-07-14 15:34:28 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:34:58 +09:00
|
|
|
if let hir::ExprKind::Assign(lhs, rhs, _) = assign.kind
|
|
|
|
|
&& check_for_self_assign_helper(self.typeck_results(), lhs, rhs)
|
2021-07-14 15:34:28 +02:00
|
|
|
&& !assign.span.from_expansion()
|
2022-06-08 16:34:58 +09:00
|
|
|
{
|
2021-07-14 15:34:28 +02:00
|
|
|
let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..));
|
2024-01-16 16:27:02 +11:00
|
|
|
self.tcx.emit_node_span_lint(
|
2021-07-14 15:34:28 +02:00
|
|
|
lint::builtin::DEAD_CODE,
|
|
|
|
|
assign.hir_id,
|
|
|
|
|
assign.span,
|
2022-10-01 18:39:02 -06:00
|
|
|
UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) },
|
2021-07-14 15:34:28 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn handle_field_pattern_match(
|
|
|
|
|
&mut self,
|
|
|
|
|
lhs: &hir::Pat<'_>,
|
|
|
|
|
res: Res,
|
2021-03-16 00:36:07 +03:00
|
|
|
pats: &[hir::PatField<'_>],
|
2019-11-30 15:08:22 +01:00
|
|
|
) {
|
2020-08-03 00:49:11 +02:00
|
|
|
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
2019-04-20 19:36:05 +03:00
|
|
|
ty::Adt(adt, _) => adt.variant_of_res(res),
|
2016-03-25 18:31:27 +01:00
|
|
|
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
|
2014-07-04 16:45:47 -07:00
|
|
|
};
|
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 {
|
2015-06-22 21:36:14 +09:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-12-04 17:59:21 +00:00
|
|
|
let index = self.typeck_results().field_index(pat.hir_id);
|
2018-04-05 03:20:21 +03:00
|
|
|
self.insert_def_id(variant.fields[index].did);
|
2014-06-06 00:00:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 22:36:03 +02:00
|
|
|
fn handle_tuple_field_pattern_match(
|
|
|
|
|
&mut self,
|
|
|
|
|
lhs: &hir::Pat<'_>,
|
|
|
|
|
res: Res,
|
|
|
|
|
pats: &[hir::Pat<'_>],
|
2022-09-01 13:29:57 +10:00
|
|
|
dotdot: hir::DotDotPos,
|
2022-07-25 22:36:03 +02:00
|
|
|
) {
|
|
|
|
|
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
|
|
|
|
ty::Adt(adt, _) => adt.variant_of_res(res),
|
Overhaul the handling of errors at the top-level.
Currently `emit_stashed_diagnostic` is called from four(!) different
places: `print_error_count`, `DiagCtxtInner::drop`, `abort_if_errors`,
and `compile_status`.
And `flush_delayed` is called from two different places:
`DiagCtxtInner::drop` and `Queries`.
This is pretty gross! Each one should really be called from a single
place, but there's a bunch of entanglements. This commit cleans up this
mess.
Specifically, it:
- Removes all the existing calls to `emit_stashed_diagnostic`, and adds
a single new call in `finish_diagnostics`.
- Removes the early `flush_delayed` call in `codegen_and_build_linker`,
replacing it with a simple early return if delayed bugs are present.
- Changes `DiagCtxtInner::drop` and `DiagCtxtInner::flush_delayed` so
they both assert that the stashed diagnostics are empty (i.e.
processed beforehand).
- Changes `interface::run_compiler` so that any errors emitted during
`finish_diagnostics` (i.e. late-emitted stashed diagnostics) are
counted and cannot be overlooked. This requires adding
`ErrorGuaranteed` return values to several functions.
- Removes the `stashed_err_count` call in `analysis`. This is possible
now that we don't have to worry about calling `flush_delayed` early
from `codegen_and_build_linker` when stashed diagnostics are pending.
- Changes the `span_bug` case in `handle_tuple_field_pattern_match` to a
`delayed_span_bug`, because it now can be reached due to the removal
of the `stashed_err_count` call in `analysis`.
- Slightly changes the expected output of three tests. If no errors are
emitted but there are delayed bugs, the error count is no longer
printed. This is because delayed bugs are now always printed after the
error count is printed (or not printed, if the error count is zero).
There is a lot going on in this commit. It's hard to break into smaller
pieces because the existing code is very tangled. It took me a long time
and a lot of effort to understand how the different pieces interact, and
I think the new code is a lot simpler and easier to understand.
2024-02-19 10:00:19 +11:00
|
|
|
_ => {
|
|
|
|
|
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-25 22:36:03 +02:00
|
|
|
};
|
2022-09-01 13:29:57 +10:00
|
|
|
let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
|
|
|
|
|
let first_n = pats.iter().enumerate().take(dotdot);
|
2022-07-25 22:36:03 +02:00
|
|
|
let missing = variant.fields.len() - pats.len();
|
2022-09-01 13:29:57 +10:00
|
|
|
let last_n = pats.iter().enumerate().skip(dotdot).map(|(idx, pat)| (idx + missing, pat));
|
2022-07-25 22:36:03 +02:00
|
|
|
for (idx, pat) in first_n.chain(last_n) {
|
|
|
|
|
if let PatKind::Wild = pat.kind {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-03-28 23:32:25 -07:00
|
|
|
self.insert_def_id(variant.fields[FieldIdx::from_usize(idx)].did);
|
2022-07-25 22:36:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-11 00:37:49 -07:00
|
|
|
fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
|
|
|
|
let data = self.typeck_results().offset_of_data();
|
|
|
|
|
let &(container, ref indices) =
|
|
|
|
|
data.get(expr.hir_id).expect("no offset_of_data for offset_of");
|
|
|
|
|
|
2023-04-16 14:13:29 -07:00
|
|
|
let body_did = self.typeck_results().hir_owner.to_def_id();
|
|
|
|
|
let param_env = self.tcx.param_env(body_did);
|
|
|
|
|
|
2022-09-11 00:37:49 -07:00
|
|
|
let mut current_ty = container;
|
|
|
|
|
|
2023-08-15 22:32:55 +01:00
|
|
|
for &(variant, field) in indices {
|
|
|
|
|
match current_ty.kind() {
|
2024-02-12 15:39:32 +09:00
|
|
|
ty::Adt(def, args) => {
|
2023-08-15 20:10:45 +01:00
|
|
|
let field = &def.variant(variant).fields[field];
|
2022-09-11 00:37:49 -07:00
|
|
|
|
|
|
|
|
self.insert_def_id(field.did);
|
2024-02-12 15:39:32 +09:00
|
|
|
let field_ty = field.ty(self.tcx, args);
|
2022-09-11 00:37:49 -07:00
|
|
|
|
2023-04-16 14:44:09 -07:00
|
|
|
current_ty = self.tcx.normalize_erasing_regions(param_env, field_ty);
|
2022-09-11 00:37:49 -07:00
|
|
|
}
|
|
|
|
|
// we don't need to mark tuple fields as live,
|
|
|
|
|
// but we may need to mark subfields
|
2023-08-15 22:32:55 +01:00
|
|
|
ty::Tuple(tys) => {
|
2023-04-16 14:44:09 -07:00
|
|
|
current_ty =
|
2023-08-15 20:10:45 +01:00
|
|
|
self.tcx.normalize_erasing_regions(param_env, tys[field.as_usize()]);
|
2022-09-11 00:37:49 -07:00
|
|
|
}
|
2023-08-15 22:32:55 +01:00
|
|
|
_ => span_bug!(expr.span, "named field access on non-ADT"),
|
2022-09-11 00:37:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-08 02:55:27 -05:00
|
|
|
fn mark_live_symbols(&mut self) {
|
2023-08-10 19:23:17 +02:00
|
|
|
let mut scanned = UnordSet::default();
|
|
|
|
|
while let Some(work) = self.worklist.pop() {
|
|
|
|
|
if !scanned.insert(work) {
|
2013-12-08 02:55:27 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
2013-12-27 16:09:29 -08:00
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
let (id, comes_from_allow_expect) = work;
|
|
|
|
|
|
2023-03-03 12:41:52 -03:00
|
|
|
// Avoid accessing the HIR for the synthesized associated type generated for RPITITs.
|
2023-07-14 15:18:19 +00:00
|
|
|
if self.tcx.is_impl_trait_in_trait(id.to_def_id()) {
|
2023-03-03 12:41:52 -03:00
|
|
|
self.live_symbols.insert(id);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 15:52:32 +01:00
|
|
|
// in the case of tuple struct constructors we want to check the item, not the generated
|
|
|
|
|
// tuple struct constructor function
|
2021-07-28 18:23:40 +03:00
|
|
|
let id = self.struct_constructors.get(&id).copied().unwrap_or(id);
|
2018-12-18 15:52:32 +01:00
|
|
|
|
2024-03-14 21:05:06 +03:00
|
|
|
// When using `#[allow]` or `#[expect]` of `dead_code`, we do a QOL improvement
|
|
|
|
|
// by declaring fn calls, statics, ... within said items as live, as well as
|
|
|
|
|
// the item itself, although technically this is not the case.
|
|
|
|
|
//
|
|
|
|
|
// This means that the lint for said items will never be fired.
|
|
|
|
|
//
|
|
|
|
|
// This doesn't make any difference for the item declared with `#[allow]`, as
|
|
|
|
|
// the lint firing will be a nop, as it will be silenced by the `#[allow]` of
|
|
|
|
|
// the item.
|
|
|
|
|
//
|
|
|
|
|
// However, for `#[expect]`, the presence or absence of the lint is relevant,
|
|
|
|
|
// so we don't add it to the list of live symbols when it comes from a
|
|
|
|
|
// `#[expect]`. This means that we will correctly report an item as live or not
|
|
|
|
|
// for the `#[expect]` case.
|
|
|
|
|
//
|
|
|
|
|
// Note that an item can and will be duplicated on the worklist with different
|
|
|
|
|
// `ComesFromAllowExpect`, particularly if it was added from the
|
|
|
|
|
// `effective_visibilities` query or from the `#[allow]`/`#[expect]` checks,
|
|
|
|
|
// this "duplication" is essential as otherwise a function with `#[expect]`
|
|
|
|
|
// called from a `pub fn` may be falsely reported as not live, falsely
|
|
|
|
|
// triggering the `unfulfilled_lint_expectations` lint.
|
|
|
|
|
if comes_from_allow_expect != ComesFromAllowExpect::Yes {
|
|
|
|
|
self.live_symbols.insert(id);
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
2024-03-14 21:05:06 +03:00
|
|
|
self.visit_node(self.tcx.hir_node_by_def_id(id));
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 19:35:49 +02:00
|
|
|
/// Automatically generated items marked with `rustc_trivial_field_reads`
|
|
|
|
|
/// will be ignored for the purposes of dead code analysis (see PR #85200
|
|
|
|
|
/// for discussion).
|
2022-01-11 21:06:18 +01:00
|
|
|
fn should_ignore_item(&mut self, def_id: DefId) -> bool {
|
2021-05-21 19:35:49 +02:00
|
|
|
if let Some(impl_of) = self.tcx.impl_of_method(def_id) {
|
2023-02-16 17:17:55 +00:00
|
|
|
if !self.tcx.is_automatically_derived(impl_of) {
|
2021-10-02 09:53:51 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:34:58 +09:00
|
|
|
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
|
|
|
|
|
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
|
|
|
|
|
{
|
2023-07-11 22:35:29 +01:00
|
|
|
let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().instantiate_identity();
|
2022-06-08 16:34:58 +09:00
|
|
|
if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind()
|
|
|
|
|
&& let Some(adt_def_id) = adt_def.did().as_local()
|
|
|
|
|
{
|
|
|
|
|
self.ignored_derived_traits
|
|
|
|
|
.entry(adt_def_id)
|
|
|
|
|
.or_default()
|
|
|
|
|
.push((trait_of, impl_of));
|
2021-05-21 19:35:49 +02:00
|
|
|
}
|
2022-06-08 16:34:58 +09:00
|
|
|
return true;
|
2021-05-21 19:35:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 15:52:32 +01:00
|
|
|
fn visit_node(&mut self, node: Node<'tcx>) {
|
2022-10-27 14:02:18 +11:00
|
|
|
if let Node::ImplItem(hir::ImplItem { owner_id, .. }) = node
|
|
|
|
|
&& self.should_ignore_item(owner_id.to_def_id())
|
2022-06-10 12:14:24 +09:00
|
|
|
{
|
|
|
|
|
return;
|
2021-05-21 19:35:49 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-18 12:54:01 -05:00
|
|
|
let unconditionally_treated_fields_as_live =
|
|
|
|
|
self.repr_unconditionally_treats_fields_as_live;
|
2022-07-25 22:36:03 +02:00
|
|
|
let had_repr_simd = self.repr_has_repr_simd;
|
2024-01-18 12:54:01 -05:00
|
|
|
self.repr_unconditionally_treats_fields_as_live = false;
|
2022-07-25 22:36:03 +02:00
|
|
|
self.repr_has_repr_simd = false;
|
2018-12-18 15:52:32 +01:00
|
|
|
match node {
|
2022-02-13 19:38:36 +01:00
|
|
|
Node::Item(item) => match item.kind {
|
|
|
|
|
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
|
2022-10-27 14:02:18 +11:00
|
|
|
let def = self.tcx.adt_def(item.owner_id);
|
2024-01-18 12:54:01 -05:00
|
|
|
self.repr_unconditionally_treats_fields_as_live =
|
|
|
|
|
def.repr().c() || def.repr().transparent();
|
2022-07-25 22:36:03 +02:00
|
|
|
self.repr_has_repr_simd = def.repr().simd();
|
2019-03-28 23:33:17 +00:00
|
|
|
|
2023-11-21 20:07:32 +01:00
|
|
|
intravisit::walk_item(self, item)
|
2022-02-13 19:38:36 +01:00
|
|
|
}
|
|
|
|
|
hir::ItemKind::ForeignMod { .. } => {}
|
2024-01-02 00:14:21 +08:00
|
|
|
hir::ItemKind::Trait(..) => {
|
|
|
|
|
for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
|
|
|
|
|
if let Some(local_def_id) = impl_def_id.as_local()
|
|
|
|
|
&& let ItemKind::Impl(impl_ref) =
|
|
|
|
|
self.tcx.hir().expect_item(local_def_id).kind
|
|
|
|
|
{
|
|
|
|
|
// skip items
|
|
|
|
|
// mark dependent traits live
|
|
|
|
|
intravisit::walk_generics(self, impl_ref.generics);
|
|
|
|
|
// mark dependent parameters live
|
|
|
|
|
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
intravisit::walk_item(self, item)
|
|
|
|
|
}
|
2023-11-21 20:07:32 +01:00
|
|
|
_ => intravisit::walk_item(self, item),
|
2022-02-13 19:38:36 +01:00
|
|
|
},
|
2018-08-25 15:56:16 +01:00
|
|
|
Node::TraitItem(trait_item) => {
|
2024-01-02 00:14:21 +08:00
|
|
|
// mark corresponing ImplTerm live
|
|
|
|
|
let trait_item_id = trait_item.owner_id.to_def_id();
|
|
|
|
|
if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) {
|
|
|
|
|
// mark the trait live
|
|
|
|
|
self.check_def_id(trait_id);
|
|
|
|
|
|
|
|
|
|
for impl_id in self.tcx.all_impls(trait_id) {
|
|
|
|
|
if let Some(local_impl_id) = impl_id.as_local()
|
|
|
|
|
&& let ItemKind::Impl(impl_ref) =
|
|
|
|
|
self.tcx.hir().expect_item(local_impl_id).kind
|
|
|
|
|
{
|
2024-02-29 14:08:01 +08:00
|
|
|
if self.tcx.visibility(trait_id).is_public()
|
|
|
|
|
&& matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
|
|
|
|
|
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 00:14:21 +08:00
|
|
|
// mark self_ty live
|
|
|
|
|
intravisit::walk_ty(self, impl_ref.self_ty);
|
|
|
|
|
if let Some(&impl_item_id) =
|
|
|
|
|
self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id)
|
|
|
|
|
{
|
|
|
|
|
self.check_def_id(impl_item_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-17 18:56:13 -05:00
|
|
|
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) => {
|
2022-10-27 14:02:18 +11:00
|
|
|
let item = self.tcx.local_parent(impl_item.owner_id.def_id);
|
2023-01-10 14:57:22 -07:00
|
|
|
if self.tcx.impl_trait_ref(item).is_none() {
|
2022-06-19 16:30:32 +02:00
|
|
|
//// If it's a type whose items 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.
|
2023-07-11 22:35:29 +01:00
|
|
|
let self_ty = self.tcx.type_of(item).instantiate_identity();
|
2022-06-19 16:30:32 +02:00
|
|
|
match *self_ty.kind() {
|
|
|
|
|
ty::Adt(def, _) => self.check_def_id(def.did()),
|
|
|
|
|
ty::Foreign(did) => self.check_def_id(did),
|
|
|
|
|
ty::Dynamic(data, ..) => {
|
|
|
|
|
if let Some(def_id) = data.principal_def_id() {
|
|
|
|
|
self.check_def_id(def_id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-17 18:56:13 -05:00
|
|
|
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) => {
|
2023-11-21 20:07:32 +01:00
|
|
|
intravisit::walk_foreign_item(self, foreign_item);
|
2013-12-14 00:08:26 -05:00
|
|
|
}
|
2019-03-28 23:33:17 +00:00
|
|
|
_ => {}
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
2022-07-25 22:36:03 +02:00
|
|
|
self.repr_has_repr_simd = had_repr_simd;
|
2024-01-18 12:54:01 -05:00
|
|
|
self.repr_unconditionally_treats_fields_as_live = unconditionally_treated_fields_as_live;
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
2017-08-06 17:19:15 +02:00
|
|
|
|
2022-03-05 07:28:41 +11:00
|
|
|
fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) {
|
|
|
|
|
if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did().is_local() {
|
2018-04-05 03:20:21 +03:00
|
|
|
for field in fields {
|
2022-12-04 17:59:21 +00:00
|
|
|
let index = self.typeck_results().field_index(field.hir_id);
|
2018-04-05 03:20:21 +03:00
|
|
|
self.insert_def_id(adt.non_enum_variant().fields[index].did);
|
2017-08-06 17:19:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-29 14:08:01 +08:00
|
|
|
|
|
|
|
|
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
|
|
|
|
|
let mut ready;
|
|
|
|
|
(ready, unsolved_impl_items) = unsolved_impl_items
|
|
|
|
|
.into_iter()
|
|
|
|
|
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
|
|
|
|
|
|
|
|
|
|
while !ready.is_empty() {
|
|
|
|
|
self.worklist =
|
|
|
|
|
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
|
|
|
|
|
self.mark_live_symbols();
|
|
|
|
|
|
|
|
|
|
(ready, unsolved_impl_items) = unsolved_impl_items
|
|
|
|
|
.into_iter()
|
|
|
|
|
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool {
|
|
|
|
|
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
|
|
|
|
|
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
|
|
|
|
|
&& let Res::Def(def_kind, def_id) = path.res
|
|
|
|
|
&& let Some(local_def_id) = def_id.as_local()
|
|
|
|
|
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
|
|
|
|
|
{
|
|
|
|
|
self.live_symbols.contains(&local_def_id)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-25 20:58:33 +03:00
|
|
|
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
|
2017-01-06 21:54:24 +02:00
|
|
|
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));
|
2018-12-04 13:45:36 +01:00
|
|
|
let body = self.tcx.hir().body(body);
|
2017-01-06 21:54:24 +02:00
|
|
|
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
|
|
|
}
|
2014-06-12 08:38:30 +02:00
|
|
|
|
2022-08-10 11:22:01 +10:00
|
|
|
fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) {
|
2022-02-13 01:54:13 +01:00
|
|
|
let tcx = self.tcx;
|
2024-01-18 12:54:01 -05:00
|
|
|
let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live;
|
2022-07-25 22:36:03 +02:00
|
|
|
let has_repr_simd = self.repr_has_repr_simd;
|
2022-02-13 01:54:13 +01:00
|
|
|
let live_fields = def.fields().iter().filter_map(|f| {
|
2022-11-06 19:46:55 +00:00
|
|
|
let def_id = f.def_id;
|
2024-01-18 12:54:01 -05:00
|
|
|
if unconditionally_treat_fields_as_live || (f.is_positional() && has_repr_simd) {
|
2022-02-13 01:54:13 +01:00
|
|
|
return Some(def_id);
|
|
|
|
|
}
|
2022-09-20 14:11:23 +09:00
|
|
|
if !tcx.visibility(f.hir_id.owner.def_id).is_public() {
|
2022-02-13 01:54:13 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
if tcx.visibility(def_id).is_public() { Some(def_id) } else { None }
|
2021-05-15 12:58:23 +02:00
|
|
|
});
|
2022-02-13 01:54:13 +01:00
|
|
|
self.live_symbols.extend(live_fields);
|
2014-06-12 08:38:30 +02:00
|
|
|
|
2015-11-17 18:56:13 -05:00
|
|
|
intravisit::walk_struct_def(self, def);
|
2014-06-12 08:38:30 +02:00
|
|
|
}
|
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>) {
|
2019-09-26 14:39:48 +01:00
|
|
|
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);
|
2019-04-20 19:36:05 +03:00
|
|
|
self.handle_res(res);
|
2016-10-27 05:17:42 +03:00
|
|
|
}
|
2018-07-11 20:05:29 +08:00
|
|
|
hir::ExprKind::MethodCall(..) => {
|
2017-08-10 16:10:04 +02:00
|
|
|
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, ..) => {
|
2024-02-03 17:34:25 +01:00
|
|
|
if self.typeck_results().opt_field_index(expr.hir_id).is_some() {
|
|
|
|
|
self.handle_field_access(lhs, expr.hir_id);
|
|
|
|
|
} else {
|
|
|
|
|
self.tcx.dcx().span_delayed_bug(expr.span, "couldn't resolve index for field");
|
|
|
|
|
}
|
2014-06-06 00:00:29 +02:00
|
|
|
}
|
2023-11-21 20:07:32 +01:00
|
|
|
hir::ExprKind::Struct(qpath, fields, _) => {
|
2020-07-17 08:47:04 +00:00
|
|
|
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
|
2020-04-11 22:34:45 +09:00
|
|
|
self.handle_res(res);
|
2022-03-05 07:28:41 +11:00
|
|
|
if let ty::Adt(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
|
|
|
}
|
2023-02-21 16:11:48 +00:00
|
|
|
hir::ExprKind::Closure(cls) => {
|
|
|
|
|
self.insert_def_id(cls.def_id.to_def_id());
|
|
|
|
|
}
|
2022-09-11 00:37:49 -07:00
|
|
|
hir::ExprKind::OffsetOf(..) => {
|
|
|
|
|
self.handle_offset_of(expr);
|
|
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-17 18:56:13 -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>) {
|
2019-09-21 15:49:15 +02:00
|
|
|
// 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);
|
2015-06-02 20:31:40 +09:00
|
|
|
}
|
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
2021-01-23 00:00:00 +00:00
|
|
|
self.in_pat = true;
|
2019-09-26 16:18:31 +01:00
|
|
|
match pat.kind {
|
2023-11-21 20:07:32 +01:00
|
|
|
PatKind::Struct(ref path, fields, _) => {
|
2020-07-17 08:47:04 +00:00
|
|
|
let res = self.typeck_results().qpath_res(path, pat.hir_id);
|
2019-08-02 20:57:29 +00:00
|
|
|
self.handle_field_pattern_match(pat, res, fields);
|
2014-06-06 00:00:29 +02:00
|
|
|
}
|
2019-08-05 19:23:46 -07:00
|
|
|
PatKind::Path(ref qpath) => {
|
2020-07-17 08:47:04 +00:00
|
|
|
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
|
2019-04-20 19:36:05 +03:00
|
|
|
self.handle_res(res);
|
2014-06-30 18:02:14 -07:00
|
|
|
}
|
2023-11-21 20:07:32 +01:00
|
|
|
PatKind::TupleStruct(ref qpath, fields, dotdot) => {
|
2022-07-25 22:36:03 +02:00
|
|
|
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
|
|
|
|
|
self.handle_tuple_field_pattern_match(pat, res, fields, dotdot);
|
|
|
|
|
}
|
2014-06-06 00:00:29 +02:00
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-17 18:56:13 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2022-11-25 11:26:36 +03:00
|
|
|
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
|
2019-04-20 19:36:05 +03:00
|
|
|
self.handle_res(path.res);
|
2015-11-17 18:56:13 -05:00
|
|
|
intravisit::walk_path(self, path);
|
2013-12-11 20:49:46 -05:00
|
|
|
}
|
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>) {
|
2022-09-06 17:37:00 +02:00
|
|
|
if let TyKind::OpaqueDef(item_id, _, _) = ty.kind {
|
2021-01-30 12:06:04 +01:00
|
|
|
let item = self.tcx.hir().item(item_id);
|
2020-03-22 13:36:56 +01:00
|
|
|
intravisit::walk_item(self, item);
|
2019-03-12 21:35:20 +09:00
|
|
|
}
|
|
|
|
|
intravisit::walk_ty(self, ty);
|
|
|
|
|
}
|
2019-08-05 19:23:46 -07:00
|
|
|
|
|
|
|
|
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
|
2021-08-14 21:54:17 +01:00
|
|
|
// When inline const blocks are used in pattern position, paths
|
|
|
|
|
// referenced by it should be considered as used.
|
|
|
|
|
let in_pat = mem::replace(&mut self.in_pat, false);
|
|
|
|
|
|
2022-11-05 15:33:58 +00:00
|
|
|
self.live_symbols.insert(c.def_id);
|
2019-08-05 19:23:46 -07:00
|
|
|
intravisit::walk_anon_const(self, c);
|
2021-08-14 21:54:17 +01:00
|
|
|
|
|
|
|
|
self.in_pat = in_pat;
|
2019-08-05 19:23:46 -07:00
|
|
|
}
|
2023-02-25 19:53:37 +00:00
|
|
|
|
|
|
|
|
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
|
|
|
|
|
// When inline const blocks are used in pattern position, paths
|
|
|
|
|
// referenced by it should be considered as used.
|
|
|
|
|
let in_pat = mem::replace(&mut self.in_pat, false);
|
|
|
|
|
|
|
|
|
|
self.live_symbols.insert(c.def_id);
|
|
|
|
|
intravisit::walk_inline_const(self, c);
|
|
|
|
|
|
|
|
|
|
self.in_pat = in_pat;
|
|
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
fn has_allow_dead_code_or_lang_attr(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
def_id: LocalDefId,
|
|
|
|
|
) -> Option<ComesFromAllowExpect> {
|
2023-01-30 10:53:11 +00:00
|
|
|
fn has_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
2023-03-13 18:54:05 +00:00
|
|
|
tcx.has_attr(def_id, sym::lang)
|
2023-01-30 10:53:11 +00:00
|
|
|
// Stable attribute for #[lang = "panic_impl"]
|
2023-03-13 18:54:05 +00:00
|
|
|
|| tcx.has_attr(def_id, sym::panic_handler)
|
2018-07-06 15:49:52 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
2023-11-24 19:28:19 +03:00
|
|
|
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
2023-08-10 19:23:17 +02:00
|
|
|
let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
|
|
|
|
|
matches!(lint_level, lint::Allow | lint::Expect(_))
|
2018-07-06 15:49:52 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-30 10:53:11 +00:00
|
|
|
fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|
|
|
|
tcx.def_kind(def_id).has_codegen_attrs() && {
|
|
|
|
|
let cg_attrs = tcx.codegen_fn_attrs(def_id);
|
2022-05-04 11:18:37 +02:00
|
|
|
|
2023-01-30 10:53:11 +00:00
|
|
|
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
|
|
|
|
|
// forcefully, e.g., for placing it in a specific section.
|
|
|
|
|
cg_attrs.contains_extern_indicator()
|
|
|
|
|
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
|
|
|
|
|
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|
2022-05-04 11:18:37 +02:00
|
|
|
}
|
2017-06-03 14:54:08 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
if has_allow_expect_dead_code(tcx, def_id) {
|
|
|
|
|
Some(ComesFromAllowExpect::Yes)
|
|
|
|
|
} else if has_used_like_attr(tcx, def_id) || has_lang_attr(tcx, def_id) {
|
|
|
|
|
Some(ComesFromAllowExpect::No)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2014-01-11 03:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-03 16:19:59 -04:00
|
|
|
// These check_* functions seeds items that
|
2014-01-11 03:21:53 -05:00
|
|
|
// 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
|
2020-09-22 21:25:32 +02:00
|
|
|
// * Implementations of traits and trait methods
|
2022-05-03 16:19:59 -04:00
|
|
|
fn check_item<'tcx>(
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-08-10 19:23:17 +02:00
|
|
|
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
|
2023-02-21 12:19:54 +01:00
|
|
|
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
|
2024-02-29 14:08:01 +08:00
|
|
|
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
|
2022-05-03 16:19:59 -04:00
|
|
|
id: hir::ItemId,
|
|
|
|
|
) {
|
2022-11-05 15:33:58 +00:00
|
|
|
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
|
2023-08-10 19:23:17 +02:00
|
|
|
if let Some(comes_from_allow) = allow_dead_code {
|
|
|
|
|
worklist.push((id.owner_id.def_id, comes_from_allow));
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
match tcx.def_kind(id.owner_id) {
|
2022-05-03 16:19:59 -04:00
|
|
|
DefKind::Enum => {
|
|
|
|
|
let item = tcx.hir().item(id);
|
|
|
|
|
if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
|
2023-08-10 19:23:17 +02:00
|
|
|
if let Some(comes_from_allow) = allow_dead_code {
|
|
|
|
|
worklist.extend(
|
|
|
|
|
enum_def.variants.iter().map(|variant| (variant.def_id, comes_from_allow)),
|
|
|
|
|
);
|
2019-03-21 23:38:50 +01:00
|
|
|
}
|
|
|
|
|
|
2019-11-29 09:26:18 +01:00
|
|
|
for variant in enum_def.variants {
|
2022-11-06 19:46:55 +00:00
|
|
|
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
|
|
|
|
|
struct_constructors.insert(ctor_def_id, variant.def_id);
|
2019-03-21 23:38:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
2014-09-20 13:26:10 +02:00
|
|
|
}
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
2023-02-12 18:26:47 +00:00
|
|
|
DefKind::Impl { of_trait } => {
|
2022-05-12 11:34:37 -04:00
|
|
|
// get DefIds from another query
|
|
|
|
|
let local_def_ids = tcx
|
2022-10-27 14:02:18 +11:00
|
|
|
.associated_item_def_ids(id.owner_id)
|
2022-05-12 11:34:37 -04:00
|
|
|
.iter()
|
|
|
|
|
.filter_map(|def_id| def_id.as_local());
|
|
|
|
|
|
2024-02-29 14:08:01 +08:00
|
|
|
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
|
|
|
|
|
|
2022-05-12 11:34:37 -04:00
|
|
|
// And we access the Map here to get HirId from LocalDefId
|
2024-02-29 14:08:01 +08:00
|
|
|
for local_def_id in local_def_ids {
|
|
|
|
|
// check the function may construct Self
|
|
|
|
|
let mut may_construct_self = true;
|
2024-03-14 21:05:06 +03:00
|
|
|
if let Some(fn_sig) =
|
|
|
|
|
tcx.hir().fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id))
|
2024-02-29 14:08:01 +08:00
|
|
|
{
|
|
|
|
|
may_construct_self =
|
|
|
|
|
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 00:14:21 +08:00
|
|
|
// for impl trait blocks, mark associate functions live if the trait is public
|
|
|
|
|
if of_trait
|
2024-02-29 14:08:01 +08:00
|
|
|
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
|
|
|
|
|
|| tcx.visibility(local_def_id).is_public()
|
|
|
|
|
&& (ty_is_pub || may_construct_self))
|
2024-01-02 00:14:21 +08:00
|
|
|
{
|
2024-02-29 14:08:01 +08:00
|
|
|
worklist.push((local_def_id, ComesFromAllowExpect::No));
|
|
|
|
|
} else if of_trait && tcx.visibility(local_def_id).is_public() {
|
|
|
|
|
// pub method && private ty & methods not construct self
|
|
|
|
|
unsolved_impl_items.push((id, local_def_id));
|
|
|
|
|
} else if let Some(comes_from_allow) =
|
|
|
|
|
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
|
|
|
|
|
{
|
|
|
|
|
worklist.push((local_def_id, comes_from_allow));
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
|
|
|
|
DefKind::Struct => {
|
|
|
|
|
let item = tcx.hir().item(id);
|
2022-06-08 16:34:58 +09:00
|
|
|
if let hir::ItemKind::Struct(ref variant_data, _) = item.kind
|
2022-11-05 15:33:58 +00:00
|
|
|
&& let Some(ctor_def_id) = variant_data.ctor_def_id()
|
2022-06-08 16:34:58 +09:00
|
|
|
{
|
2022-11-05 15:33:58 +00:00
|
|
|
struct_constructors.insert(ctor_def_id, item.owner_id.def_id);
|
2018-12-18 15:52:32 +01:00
|
|
|
}
|
2014-01-11 03:21:53 -05:00
|
|
|
}
|
2022-05-03 16:19:59 -04:00
|
|
|
DefKind::GlobalAsm => {
|
|
|
|
|
// global_asm! is always live.
|
2023-08-10 19:23:17 +02:00
|
|
|
worklist.push((id.owner_id.def_id, ComesFromAllowExpect::No));
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
|
|
|
|
_ => {}
|
2014-01-11 03:21:53 -05:00
|
|
|
}
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
2016-11-04 18:20:15 -04:00
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
fn check_trait_item(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
|
|
|
|
|
id: hir::TraitItemId,
|
|
|
|
|
) {
|
2022-05-03 16:19:59 -04:00
|
|
|
use hir::TraitItemKind::{Const, Fn};
|
2022-10-27 14:02:18 +11:00
|
|
|
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
|
2022-05-03 16:19:59 -04:00
|
|
|
let trait_item = tcx.hir().trait_item(id);
|
2024-01-02 00:14:21 +08:00
|
|
|
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
|
2023-08-10 19:23:17 +02:00
|
|
|
&& let Some(comes_from_allow) =
|
|
|
|
|
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
|
2020-12-08 15:37:39 +01:00
|
|
|
{
|
2023-08-10 19:23:17 +02:00
|
|
|
worklist.push((trait_item.owner_id.def_id, comes_from_allow));
|
2020-11-26 21:00:35 +01:00
|
|
|
}
|
2016-12-04 04:21:06 +02:00
|
|
|
}
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
2016-12-04 04:21:06 +02:00
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
fn check_foreign_item(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
|
|
|
|
|
id: hir::ForeignItemId,
|
|
|
|
|
) {
|
2024-02-23 23:12:20 +00:00
|
|
|
if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. } | DefKind::Fn)
|
2023-08-10 19:23:17 +02:00
|
|
|
&& let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
|
2022-05-03 16:19:59 -04:00
|
|
|
{
|
2023-08-10 19:23:17 +02:00
|
|
|
worklist.push((id.owner_id.def_id, comes_from_allow));
|
2020-11-26 21:00:35 +01:00
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
fn create_and_seed_worklist(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
2024-02-29 14:08:01 +08:00
|
|
|
) -> (
|
|
|
|
|
Vec<(LocalDefId, ComesFromAllowExpect)>,
|
|
|
|
|
LocalDefIdMap<LocalDefId>,
|
|
|
|
|
Vec<(hir::ItemId, LocalDefId)>,
|
|
|
|
|
) {
|
2022-09-22 16:19:53 +03:00
|
|
|
let effective_visibilities = &tcx.effective_visibilities(());
|
2022-05-03 16:19:59 -04:00
|
|
|
// see `MarkSymbolVisitor::struct_constructors`
|
2024-02-29 14:08:01 +08:00
|
|
|
let mut unsolved_impl_item = Vec::new();
|
2022-05-03 16:19:59 -04:00
|
|
|
let mut struct_constructors = Default::default();
|
2022-09-22 16:19:53 +03:00
|
|
|
let mut worklist = effective_visibilities
|
2018-10-04 12:38:06 +02:00
|
|
|
.iter()
|
2022-09-12 10:57:34 +03:00
|
|
|
.filter_map(|(&id, effective_vis)| {
|
2023-08-10 19:23:17 +02:00
|
|
|
effective_vis
|
|
|
|
|
.is_public_at_level(Level::Reachable)
|
|
|
|
|
.then_some(id)
|
|
|
|
|
.map(|id| (id, ComesFromAllowExpect::No))
|
2022-09-12 10:57:34 +03:00
|
|
|
})
|
2021-07-28 18:23:40 +03:00
|
|
|
// Seed entry point
|
2023-08-10 19:23:17 +02:00
|
|
|
.chain(
|
|
|
|
|
tcx.entry_fn(())
|
|
|
|
|
.and_then(|(def_id, _)| def_id.as_local().map(|id| (id, ComesFromAllowExpect::No))),
|
|
|
|
|
)
|
2018-07-16 19:35:45 +02:00
|
|
|
.collect::<Vec<_>>();
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2022-05-03 16:19:59 -04:00
|
|
|
let crate_items = tcx.hir_crate_items(());
|
|
|
|
|
for id in crate_items.items() {
|
2024-02-29 14:08:01 +08:00
|
|
|
check_item(tcx, &mut worklist, &mut struct_constructors, &mut unsolved_impl_item, id);
|
2022-05-03 16:19:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for id in crate_items.trait_items() {
|
|
|
|
|
check_trait_item(tcx, &mut worklist, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for id in crate_items.foreign_items() {
|
|
|
|
|
check_foreign_item(tcx, &mut worklist, id);
|
|
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2024-02-29 14:08:01 +08:00
|
|
|
(worklist, struct_constructors, unsolved_impl_item)
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 22:10:40 +01:00
|
|
|
fn live_symbols_and_ignored_derived_traits(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
2022-01-29 21:10:41 +01:00
|
|
|
(): (),
|
2023-02-21 12:19:54 +01:00
|
|
|
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
|
2024-02-29 14:08:01 +08:00
|
|
|
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
|
2017-01-06 21:54:24 +02:00
|
|
|
let mut symbol_visitor = MarkSymbolVisitor {
|
2017-07-03 11:19:51 -07:00
|
|
|
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(),
|
2024-01-18 12:54:01 -05:00
|
|
|
repr_unconditionally_treats_fields_as_live: false,
|
2022-07-25 22:36:03 +02:00
|
|
|
repr_has_repr_simd: false,
|
2017-10-31 11:57:40 +09:00
|
|
|
in_pat: false,
|
2017-01-06 21:54:24 +02:00
|
|
|
ignore_variant_stack: vec![],
|
2018-12-18 15:52:32 +01:00
|
|
|
struct_constructors,
|
2023-02-21 12:19:54 +01:00
|
|
|
ignored_derived_traits: Default::default(),
|
2017-01-06 21:54:24 +02:00
|
|
|
};
|
2013-12-08 02:55:27 -05:00
|
|
|
symbol_visitor.mark_live_symbols();
|
2024-02-29 14:08:01 +08:00
|
|
|
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
|
|
|
|
|
|
2022-01-11 21:06:18 +01:00
|
|
|
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2023-10-02 08:59:31 -04:00
|
|
|
struct DeadItem {
|
2022-06-22 20:33:59 +02:00
|
|
|
def_id: LocalDefId,
|
2022-06-10 12:14:24 +09:00
|
|
|
name: Symbol,
|
|
|
|
|
level: lint::Level,
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 22:03:44 +03:00
|
|
|
struct DeadVisitor<'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-02-21 12:19:54 +01:00
|
|
|
live_symbols: &'tcx LocalDefIdSet,
|
|
|
|
|
ignored_derived_traits: &'tcx LocalDefIdMap<Vec<(DefId, DefId)>>,
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
|
|
|
|
|
2022-07-25 22:36:03 +02:00
|
|
|
enum ShouldWarnAboutField {
|
2023-12-25 13:14:12 +00:00
|
|
|
Yes,
|
2022-07-25 22:36:03 +02:00
|
|
|
No,
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-26 14:48:34 -05:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
|
enum ReportOn {
|
|
|
|
|
TupleField,
|
|
|
|
|
NamedField,
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-13 22:16:36 -07:00
|
|
|
impl<'tcx> DeadVisitor<'tcx> {
|
2022-07-25 22:36:03 +02:00
|
|
|
fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> ShouldWarnAboutField {
|
2022-06-19 18:52:06 +02:00
|
|
|
if self.live_symbols.contains(&field.did.expect_local()) {
|
2022-07-25 22:36:03 +02:00
|
|
|
return ShouldWarnAboutField::No;
|
|
|
|
|
}
|
2023-07-11 22:35:29 +01:00
|
|
|
let field_type = self.tcx.type_of(field.did).instantiate_identity();
|
2022-07-25 22:36:03 +02:00
|
|
|
if field_type.is_phantom_data() {
|
|
|
|
|
return ShouldWarnAboutField::No;
|
2022-06-19 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
|
2022-07-25 22:36:03 +02:00
|
|
|
if is_positional
|
|
|
|
|
&& self
|
|
|
|
|
.tcx
|
|
|
|
|
.layout_of(self.tcx.param_env(field.did).and(field_type))
|
|
|
|
|
.map_or(true, |layout| layout.is_zst())
|
|
|
|
|
{
|
|
|
|
|
return ShouldWarnAboutField::No;
|
2022-06-19 18:52:06 +02:00
|
|
|
}
|
2023-12-25 13:14:12 +00:00
|
|
|
ShouldWarnAboutField::Yes
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-26 14:48:34 -05:00
|
|
|
fn def_lint_level(&self, id: LocalDefId) -> lint::Level {
|
2023-12-25 13:14:12 +00:00
|
|
|
let hir_id = self.tcx.local_def_id_to_hir_id(id);
|
2023-11-26 14:48:34 -05:00
|
|
|
self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|
2013-12-14 00:08:26 -05:00
|
|
|
|
2023-09-12 03:11:11 -04:00
|
|
|
// # Panics
|
|
|
|
|
// All `dead_codes` must have the same lint level, otherwise we will intentionally ICE.
|
|
|
|
|
// This is because we emit a multi-spanned lint using the lint level of the `dead_codes`'s
|
|
|
|
|
// first local def id.
|
|
|
|
|
// Prefer calling `Self.warn_dead_code` or `Self.warn_dead_code_grouped_by_lint_level`
|
|
|
|
|
// since those methods group by lint level before calling this method.
|
2023-10-02 08:59:31 -04:00
|
|
|
fn lint_at_single_level(
|
2022-06-10 12:14:24 +09:00
|
|
|
&self,
|
2023-12-25 13:14:12 +00:00
|
|
|
dead_codes: &[&DeadItem],
|
2022-06-10 12:14:24 +09:00
|
|
|
participle: &str,
|
2022-06-22 20:33:59 +02:00
|
|
|
parent_item: Option<LocalDefId>,
|
2023-11-26 14:48:34 -05:00
|
|
|
report_on: ReportOn,
|
2022-06-10 12:14:24 +09:00
|
|
|
) {
|
2023-12-25 13:14:12 +00:00
|
|
|
let Some(&first_item) = dead_codes.first() else {
|
2022-10-22 18:48:20 +08:00
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let tcx = self.tcx;
|
2023-09-12 03:11:11 -04:00
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let first_lint_level = first_item.level;
|
|
|
|
|
assert!(dead_codes.iter().skip(1).all(|item| item.level == first_lint_level));
|
2023-09-12 03:11:11 -04:00
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let names: Vec<_> = dead_codes.iter().map(|item| item.name).collect();
|
2022-10-22 18:48:20 +08:00
|
|
|
let spans: Vec<_> = dead_codes
|
|
|
|
|
.iter()
|
2023-12-25 13:14:12 +00:00
|
|
|
.map(|item| match tcx.def_ident_span(item.def_id) {
|
|
|
|
|
Some(s) => s.with_ctxt(tcx.def_span(item.def_id).ctxt()),
|
|
|
|
|
None => tcx.def_span(item.def_id),
|
2022-10-22 18:48:20 +08:00
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let descr = tcx.def_descr(first_item.def_id.to_def_id());
|
2023-04-13 23:38:52 +12:00
|
|
|
// `impl` blocks are "batched" and (unlike other batching) might
|
|
|
|
|
// contain different kinds of associated items.
|
2023-12-25 13:14:12 +00:00
|
|
|
let descr = if dead_codes.iter().any(|item| tcx.def_descr(item.def_id.to_def_id()) != descr)
|
|
|
|
|
{
|
2023-04-13 23:38:52 +12:00
|
|
|
"associated item"
|
|
|
|
|
} else {
|
|
|
|
|
descr
|
|
|
|
|
};
|
2022-10-22 18:48:20 +08:00
|
|
|
let num = dead_codes.len();
|
|
|
|
|
let multiple = num > 6;
|
|
|
|
|
let name_list = names.into();
|
|
|
|
|
|
|
|
|
|
let parent_info = if let Some(parent_item) = parent_item {
|
2023-02-21 14:05:32 -07:00
|
|
|
let parent_descr = tcx.def_descr(parent_item.to_def_id());
|
2023-04-13 22:42:47 +12:00
|
|
|
let span = if let DefKind::Impl { .. } = tcx.def_kind(parent_item) {
|
|
|
|
|
tcx.def_span(parent_item)
|
|
|
|
|
} else {
|
|
|
|
|
tcx.def_ident_span(parent_item).unwrap()
|
|
|
|
|
};
|
|
|
|
|
Some(ParentInfo { num, descr, parent_descr, span })
|
2022-10-22 18:48:20 +08:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let encl_def_id = parent_item.unwrap_or(first_item.def_id);
|
2022-10-22 18:48:20 +08:00
|
|
|
let ignored_derived_impls =
|
|
|
|
|
if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
|
|
|
|
|
let trait_list = ign_traits
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(trait_id, _)| self.tcx.item_name(*trait_id))
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
let trait_list_len = trait_list.len();
|
|
|
|
|
Some(IgnoredDerivedImpls {
|
|
|
|
|
name: self.tcx.item_name(encl_def_id.to_def_id()),
|
|
|
|
|
trait_list: trait_list.into(),
|
|
|
|
|
trait_list_len,
|
2022-06-22 20:33:59 +02:00
|
|
|
})
|
2022-10-22 18:48:20 +08:00
|
|
|
} else {
|
|
|
|
|
None
|
2022-09-16 11:01:02 +04:00
|
|
|
};
|
2022-07-25 22:36:03 +02:00
|
|
|
|
2023-11-26 14:48:34 -05:00
|
|
|
let diag = match report_on {
|
|
|
|
|
ReportOn::TupleField => MultipleDeadCodes::UnusedTupleStructFields {
|
2022-10-22 18:48:20 +08:00
|
|
|
multiple,
|
|
|
|
|
num,
|
|
|
|
|
descr,
|
|
|
|
|
participle,
|
|
|
|
|
name_list,
|
|
|
|
|
change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() },
|
|
|
|
|
parent_info,
|
|
|
|
|
ignored_derived_impls,
|
2023-11-26 14:48:34 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
ReportOn::NamedField => MultipleDeadCodes::DeadCodes {
|
2022-10-22 18:48:20 +08:00
|
|
|
multiple,
|
|
|
|
|
num,
|
|
|
|
|
descr,
|
|
|
|
|
participle,
|
|
|
|
|
name_list,
|
|
|
|
|
parent_info,
|
|
|
|
|
ignored_derived_impls,
|
2023-11-26 14:48:34 -05:00
|
|
|
},
|
2022-10-22 18:48:20 +08:00
|
|
|
};
|
2022-06-22 20:33:59 +02:00
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id);
|
2024-01-16 16:27:02 +11:00
|
|
|
self.tcx.emit_node_span_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag);
|
2022-06-10 12:14:24 +09:00
|
|
|
}
|
|
|
|
|
|
2023-10-02 08:59:31 -04:00
|
|
|
fn warn_multiple(
|
2022-06-10 12:14:24 +09:00
|
|
|
&self,
|
2022-06-22 20:33:59 +02:00
|
|
|
def_id: LocalDefId,
|
2022-06-10 12:14:24 +09:00
|
|
|
participle: &str,
|
2023-10-02 08:59:31 -04:00
|
|
|
dead_codes: Vec<DeadItem>,
|
2023-11-26 14:48:34 -05:00
|
|
|
report_on: ReportOn,
|
2022-06-10 12:14:24 +09:00
|
|
|
) {
|
|
|
|
|
let mut dead_codes = dead_codes
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|v| !v.name.as_str().starts_with('_'))
|
2023-10-02 08:59:31 -04:00
|
|
|
.collect::<Vec<&DeadItem>>();
|
2022-06-10 12:14:24 +09:00
|
|
|
if dead_codes.is_empty() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
dead_codes.sort_by_key(|v| v.level);
|
2024-01-25 22:20:42 -08:00
|
|
|
for group in dead_codes[..].chunk_by(|a, b| a.level == b.level) {
|
2023-11-26 14:48:34 -05:00
|
|
|
self.lint_at_single_level(&group, participle, Some(def_id), report_on);
|
2022-06-10 12:14:24 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 20:33:59 +02:00
|
|
|
fn warn_dead_code(&mut self, id: LocalDefId, participle: &str) {
|
2023-12-25 13:14:12 +00:00
|
|
|
let item = DeadItem {
|
|
|
|
|
def_id: id,
|
|
|
|
|
name: self.tcx.item_name(id.to_def_id()),
|
2023-11-26 14:48:34 -05:00
|
|
|
level: self.def_lint_level(id),
|
2023-12-25 13:14:12 +00:00
|
|
|
};
|
2023-11-26 14:48:34 -05:00
|
|
|
self.lint_at_single_level(&[&item], participle, None, ReportOn::NamedField);
|
2013-12-14 00:08:26 -05:00
|
|
|
}
|
2013-12-08 02:55:27 -05:00
|
|
|
|
2022-06-19 18:52:06 +02:00
|
|
|
fn check_definition(&mut self, def_id: LocalDefId) {
|
2023-04-13 22:42:47 +12:00
|
|
|
if self.is_live_code(def_id) {
|
2022-06-19 18:52:06 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
match self.tcx.def_kind(def_id) {
|
|
|
|
|
DefKind::AssocConst
|
|
|
|
|
| DefKind::AssocFn
|
|
|
|
|
| DefKind::Fn
|
2024-02-23 23:12:20 +00:00
|
|
|
| DefKind::Static { .. }
|
2022-06-19 18:52:06 +02:00
|
|
|
| DefKind::Const
|
2023-09-26 02:15:32 +00:00
|
|
|
| DefKind::TyAlias
|
2022-06-19 18:52:06 +02:00
|
|
|
| DefKind::Enum
|
|
|
|
|
| DefKind::Union
|
2024-01-02 00:14:21 +08:00
|
|
|
| DefKind::ForeignTy
|
|
|
|
|
| DefKind::Trait => self.warn_dead_code(def_id, "used"),
|
2022-06-19 18:52:06 +02:00
|
|
|
DefKind::Struct => self.warn_dead_code(def_id, "constructed"),
|
|
|
|
|
DefKind::Variant | DefKind::Field => bug!("should be handled specially"),
|
|
|
|
|
_ => {}
|
2013-12-14 00:08:26 -05:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-13 22:42:47 +12:00
|
|
|
|
|
|
|
|
fn is_live_code(&self, def_id: LocalDefId) -> bool {
|
|
|
|
|
// if we cannot get a name for the item, then we just assume that it is
|
|
|
|
|
// live. I mean, we can't really emit a lint.
|
|
|
|
|
let Some(name) = self.tcx.opt_item_name(def_id.to_def_id()) else {
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
2023-08-10 19:23:17 +02:00
|
|
|
self.live_symbols.contains(&def_id) || name.as_str().starts_with('_')
|
2023-04-13 22:42:47 +12:00
|
|
|
}
|
2022-06-19 18:52:06 +02:00
|
|
|
}
|
2013-12-14 00:08:26 -05:00
|
|
|
|
2023-04-26 20:53:51 +02:00
|
|
|
fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
|
2022-06-19 18:52:06 +02:00
|
|
|
let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
|
|
|
|
|
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
|
2014-06-06 00:00:29 +02:00
|
|
|
|
2022-06-19 18:52:06 +02:00
|
|
|
let module_items = tcx.hir_module_items(module);
|
|
|
|
|
|
|
|
|
|
for item in module_items.items() {
|
2024-01-02 00:14:21 +08:00
|
|
|
let def_kind = tcx.def_kind(item.owner_id);
|
2023-08-15 09:41:43 -04:00
|
|
|
|
2024-01-02 00:14:21 +08:00
|
|
|
let mut dead_codes = Vec::new();
|
|
|
|
|
// if we have diagnosed the trait, do not diagnose unused methods
|
|
|
|
|
if matches!(def_kind, DefKind::Impl { .. })
|
|
|
|
|
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
|
|
|
|
|
{
|
|
|
|
|
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
|
|
|
|
|
// We have diagnosed unused methods in traits
|
|
|
|
|
if matches!(def_kind, DefKind::Impl { of_trait: true })
|
|
|
|
|
&& tcx.def_kind(def_id) == DefKind::AssocFn
|
|
|
|
|
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(local_def_id) = def_id.as_local()
|
|
|
|
|
&& !visitor.is_live_code(local_def_id)
|
|
|
|
|
{
|
|
|
|
|
let name = tcx.item_name(def_id);
|
|
|
|
|
let level = visitor.def_lint_level(local_def_id);
|
|
|
|
|
dead_codes.push(DeadItem { def_id: local_def_id, name, level });
|
2023-04-13 22:42:47 +12:00
|
|
|
}
|
2023-04-13 08:48:50 +12:00
|
|
|
}
|
2024-01-02 00:14:21 +08:00
|
|
|
}
|
|
|
|
|
if !dead_codes.is_empty() {
|
|
|
|
|
visitor.warn_multiple(item.owner_id.def_id, "used", dead_codes, ReportOn::NamedField);
|
2023-04-13 08:48:50 +12:00
|
|
|
}
|
|
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
if !live_symbols.contains(&item.owner_id.def_id) {
|
|
|
|
|
let parent = tcx.local_parent(item.owner_id.def_id);
|
2023-04-26 20:53:51 +02:00
|
|
|
if parent != module.to_local_def_id() && !live_symbols.contains(&parent) {
|
2022-06-19 18:52:06 +02:00
|
|
|
// We already have diagnosed something.
|
|
|
|
|
continue;
|
2015-03-22 19:24:56 -06:00
|
|
|
}
|
2022-10-27 14:02:18 +11:00
|
|
|
visitor.check_definition(item.owner_id.def_id);
|
2022-06-19 18:52:06 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let DefKind::Struct | DefKind::Union | DefKind::Enum = def_kind {
|
2022-10-27 14:02:18 +11:00
|
|
|
let adt = tcx.adt_def(item.owner_id);
|
2022-06-19 18:52:06 +02:00
|
|
|
let mut dead_variants = Vec::new();
|
|
|
|
|
|
|
|
|
|
for variant in adt.variants() {
|
|
|
|
|
let def_id = variant.def_id.expect_local();
|
|
|
|
|
if !live_symbols.contains(&def_id) {
|
|
|
|
|
// Record to group diagnostics.
|
2023-11-26 14:48:34 -05:00
|
|
|
let level = visitor.def_lint_level(def_id);
|
2023-10-02 08:59:31 -04:00
|
|
|
dead_variants.push(DeadItem { def_id, name: variant.name, level });
|
2022-06-19 18:52:06 +02:00
|
|
|
continue;
|
2015-03-22 19:24:56 -06:00
|
|
|
}
|
2022-06-19 18:52:06 +02:00
|
|
|
|
2023-12-25 13:14:12 +00:00
|
|
|
let is_positional = variant.fields.raw.first().map_or(false, |field| {
|
|
|
|
|
field.name.as_str().starts_with(|c: char| c.is_ascii_digit())
|
|
|
|
|
});
|
2023-11-26 14:48:34 -05:00
|
|
|
let report_on =
|
|
|
|
|
if is_positional { ReportOn::TupleField } else { ReportOn::NamedField };
|
2022-06-19 18:52:06 +02:00
|
|
|
let dead_fields = variant
|
|
|
|
|
.fields
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|field| {
|
|
|
|
|
let def_id = field.did.expect_local();
|
2023-12-25 13:14:12 +00:00
|
|
|
if let ShouldWarnAboutField::Yes = visitor.should_warn_about_field(field) {
|
2023-11-26 14:48:34 -05:00
|
|
|
let level = visitor.def_lint_level(def_id);
|
2023-10-02 08:59:31 -04:00
|
|
|
Some(DeadItem { def_id, name: field.name, level })
|
2022-06-19 18:52:06 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
2023-11-26 14:48:34 -05:00
|
|
|
visitor.warn_multiple(def_id, "read", dead_fields, report_on);
|
2015-03-22 19:24:56 -06:00
|
|
|
}
|
2022-06-19 18:52:06 +02:00
|
|
|
|
2023-11-26 14:48:34 -05:00
|
|
|
visitor.warn_multiple(
|
|
|
|
|
item.owner_id.def_id,
|
|
|
|
|
"constructed",
|
|
|
|
|
dead_variants,
|
|
|
|
|
ReportOn::NamedField,
|
|
|
|
|
);
|
2015-03-22 19:24:56 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-19 18:52:06 +02:00
|
|
|
for foreign_item in module_items.foreign_items() {
|
2022-10-27 14:02:18 +11:00
|
|
|
visitor.check_definition(foreign_item.owner_id.def_id);
|
2022-06-19 18:52:06 +02:00
|
|
|
}
|
2022-01-29 21:10:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn provide(providers: &mut Providers) {
|
|
|
|
|
*providers =
|
|
|
|
|
Providers { live_symbols_and_ignored_derived_traits, check_mod_deathness, ..*providers };
|
2013-12-08 02:55:27 -05:00
|
|
|
}
|