]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // This implements the dead-code warning pass. It follows middle::reachable |
2 | // closely. The idea is that all reachable symbols are live, codes called | |
3 | // from live codes are live, and everything else is dead. | |
4 | ||
923072b8 | 5 | use itertools::Itertools; |
dfeec247 | 6 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
487cf647 | 7 | use rustc_errors::MultiSpan; |
dfeec247 XL |
8 | use rustc_hir as hir; |
9 | use rustc_hir::def::{CtorOf, DefKind, Res}; | |
94222f64 | 10 | use rustc_hir::def_id::{DefId, LocalDefId}; |
5099ac24 | 11 | use rustc_hir::intravisit::{self, Visitor}; |
dfeec247 | 12 | use rustc_hir::{Node, PatKind, TyKind}; |
ba9703b0 | 13 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; |
2b03887a | 14 | use rustc_middle::middle::privacy::Level; |
5099ac24 | 15 | use rustc_middle::ty::query::Providers; |
ba9703b0 | 16 | use rustc_middle::ty::{self, DefIdTree, TyCtxt}; |
dfeec247 | 17 | use rustc_session::lint; |
f9f354fc | 18 | use rustc_span::symbol::{sym, Symbol}; |
94222f64 | 19 | use std::mem; |
1a4d82fc | 20 | |
487cf647 FG |
21 | use crate::errors::{ |
22 | ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, | |
23 | UselessAssignment, | |
24 | }; | |
2b03887a | 25 | |
1a4d82fc | 26 | // Any local node that may call something in its body block should be |
b7449926 | 27 | // explored. For example, if it's a live Node::Item that is a |
1a4d82fc JJ |
28 | // function, then we should explore its block to check for codes that |
29 | // may need to be marked as live. | |
94222f64 | 30 | fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { |
5869c6ff | 31 | matches!( |
5099ac24 | 32 | tcx.hir().find_by_def_id(def_id), |
ba9703b0 XL |
33 | Some( |
34 | Node::Item(..) | |
5869c6ff XL |
35 | | Node::ImplItem(..) |
36 | | Node::ForeignItem(..) | |
37 | | Node::TraitItem(..) | |
38 | | Node::Variant(..) | |
39 | | Node::AnonConst(..) | |
5869c6ff XL |
40 | ) |
41 | ) | |
1a4d82fc JJ |
42 | } |
43 | ||
f035d41b | 44 | struct MarkSymbolVisitor<'tcx> { |
94222f64 | 45 | worklist: Vec<LocalDefId>, |
dc9dc135 | 46 | tcx: TyCtxt<'tcx>, |
3dfed10e | 47 | maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, |
94222f64 | 48 | live_symbols: FxHashSet<LocalDefId>, |
2c00a5a8 | 49 | repr_has_repr_c: bool, |
064997fb | 50 | repr_has_repr_simd: bool, |
abe05a73 | 51 | in_pat: bool, |
b039eaaf | 52 | ignore_variant_stack: Vec<DefId>, |
0731742a | 53 | // maps from tuple struct constructors to tuple struct items |
94222f64 | 54 | struct_constructors: FxHashMap<LocalDefId, LocalDefId>, |
5099ac24 FG |
55 | // maps from ADTs to ignored derived traits (e.g. Debug and Clone) |
56 | // and the span of their respective impl (i.e., part of the derive | |
57 | // macro) | |
58 | ignored_derived_traits: FxHashMap<LocalDefId, Vec<(DefId, DefId)>>, | |
1a4d82fc JJ |
59 | } |
60 | ||
f035d41b | 61 | impl<'tcx> MarkSymbolVisitor<'tcx> { |
3dfed10e | 62 | /// Gets the type-checking results for the current body. |
f035d41b XL |
63 | /// As this will ICE if called outside bodies, only call when working with |
64 | /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). | |
65 | #[track_caller] | |
3dfed10e XL |
66 | fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { |
67 | self.maybe_typeck_results | |
68 | .expect("`MarkSymbolVisitor::typeck_results` called outside of body") | |
f035d41b XL |
69 | } |
70 | ||
e9174d1e | 71 | fn check_def_id(&mut self, def_id: DefId) { |
f9f354fc | 72 | if let Some(def_id) = def_id.as_local() { |
94222f64 XL |
73 | if should_explore(self.tcx, def_id) || self.struct_constructors.contains_key(&def_id) { |
74 | self.worklist.push(def_id); | |
b039eaaf | 75 | } |
94222f64 | 76 | self.live_symbols.insert(def_id); |
b039eaaf SL |
77 | } |
78 | } | |
79 | ||
80 | fn insert_def_id(&mut self, def_id: DefId) { | |
f9f354fc | 81 | if let Some(def_id) = def_id.as_local() { |
94222f64 XL |
82 | debug_assert!(!should_explore(self.tcx, def_id)); |
83 | self.live_symbols.insert(def_id); | |
1a4d82fc | 84 | } |
1a4d82fc JJ |
85 | } |
86 | ||
48663c56 XL |
87 | fn handle_res(&mut self, res: Res) { |
88 | match res { | |
064997fb FG |
89 | Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { |
90 | self.check_def_id(def_id); | |
3157f602 | 91 | } |
dfeec247 XL |
92 | _ if self.in_pat => {} |
93 | Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {} | |
48663c56 | 94 | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => { |
04454e1e FG |
95 | let variant_id = self.tcx.parent(ctor_def_id); |
96 | let enum_id = self.tcx.parent(variant_id); | |
532ac7d7 XL |
97 | self.check_def_id(enum_id); |
98 | if !self.ignore_variant_stack.contains(&ctor_def_id) { | |
99 | self.check_def_id(variant_id); | |
9e0c209e | 100 | } |
532ac7d7 | 101 | } |
48663c56 | 102 | Res::Def(DefKind::Variant, variant_id) => { |
04454e1e | 103 | let enum_id = self.tcx.parent(variant_id); |
532ac7d7 | 104 | self.check_def_id(enum_id); |
3157f602 XL |
105 | if !self.ignore_variant_stack.contains(&variant_id) { |
106 | self.check_def_id(variant_id); | |
1a4d82fc JJ |
107 | } |
108 | } | |
064997fb | 109 | Res::Def(_, def_id) => self.check_def_id(def_id), |
2b03887a FG |
110 | Res::SelfTyParam { trait_: t } => self.check_def_id(t), |
111 | Res::SelfTyAlias { alias_to: i, .. } => self.check_def_id(i), | |
48663c56 | 112 | Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {} |
3157f602 | 113 | } |
1a4d82fc JJ |
114 | } |
115 | ||
3b2f2976 | 116 | fn lookup_and_handle_method(&mut self, id: hir::HirId) { |
3dfed10e | 117 | if let Some(def_id) = self.typeck_results().type_dependent_def_id(id) { |
532ac7d7 | 118 | self.check_def_id(def_id); |
8faf50e0 XL |
119 | } else { |
120 | bug!("no type-dependent def for method"); | |
121 | } | |
1a4d82fc JJ |
122 | } |
123 | ||
dfeec247 | 124 | fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) { |
1b1a35ee | 125 | match self.typeck_results().expr_ty_adjusted(lhs).kind() { |
b7449926 | 126 | ty::Adt(def, _) => { |
487cf647 | 127 | let index = self.typeck_results().field_index(hir_id); |
83c7162d | 128 | self.insert_def_id(def.non_enum_variant().fields[index].did); |
9e0c209e | 129 | } |
b7449926 | 130 | ty::Tuple(..) => {} |
83c7162d | 131 | _ => span_bug!(lhs.span, "named field access on non-ADT"), |
1a4d82fc JJ |
132 | } |
133 | } | |
134 | ||
17df50a5 XL |
135 | #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands. |
136 | fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) { | |
137 | if self | |
138 | .typeck_results() | |
139 | .expr_adjustments(expr) | |
140 | .iter() | |
141 | .any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) | |
142 | { | |
143 | self.visit_expr(expr); | |
144 | } else if let hir::ExprKind::Field(base, ..) = expr.kind { | |
145 | // Ignore write to field | |
146 | self.handle_assign(base); | |
147 | } else { | |
148 | self.visit_expr(expr); | |
149 | } | |
150 | } | |
151 | ||
136023e0 XL |
152 | #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands. |
153 | fn check_for_self_assign(&mut self, assign: &'tcx hir::Expr<'tcx>) { | |
a2a8927a | 154 | fn check_for_self_assign_helper<'tcx>( |
136023e0 XL |
155 | typeck_results: &'tcx ty::TypeckResults<'tcx>, |
156 | lhs: &'tcx hir::Expr<'tcx>, | |
157 | rhs: &'tcx hir::Expr<'tcx>, | |
158 | ) -> bool { | |
159 | match (&lhs.kind, &rhs.kind) { | |
160 | (hir::ExprKind::Path(ref qpath_l), hir::ExprKind::Path(ref qpath_r)) => { | |
161 | if let (Res::Local(id_l), Res::Local(id_r)) = ( | |
162 | typeck_results.qpath_res(qpath_l, lhs.hir_id), | |
163 | typeck_results.qpath_res(qpath_r, rhs.hir_id), | |
164 | ) { | |
165 | if id_l == id_r { | |
166 | return true; | |
167 | } | |
168 | } | |
169 | return false; | |
170 | } | |
171 | (hir::ExprKind::Field(lhs_l, ident_l), hir::ExprKind::Field(lhs_r, ident_r)) => { | |
172 | if ident_l == ident_r { | |
04454e1e | 173 | return check_for_self_assign_helper(typeck_results, lhs_l, lhs_r); |
136023e0 XL |
174 | } |
175 | return false; | |
176 | } | |
177 | _ => { | |
178 | return false; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
923072b8 FG |
183 | if let hir::ExprKind::Assign(lhs, rhs, _) = assign.kind |
184 | && check_for_self_assign_helper(self.typeck_results(), lhs, rhs) | |
136023e0 | 185 | && !assign.span.from_expansion() |
923072b8 | 186 | { |
136023e0 | 187 | let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..)); |
2b03887a | 188 | self.tcx.emit_spanned_lint( |
136023e0 XL |
189 | lint::builtin::DEAD_CODE, |
190 | assign.hir_id, | |
191 | assign.span, | |
2b03887a | 192 | UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) } |
136023e0 | 193 | ) |
136023e0 XL |
194 | } |
195 | } | |
196 | ||
dfeec247 XL |
197 | fn handle_field_pattern_match( |
198 | &mut self, | |
199 | lhs: &hir::Pat<'_>, | |
200 | res: Res, | |
6a06907d | 201 | pats: &[hir::PatField<'_>], |
dfeec247 | 202 | ) { |
1b1a35ee | 203 | let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { |
48663c56 | 204 | ty::Adt(adt, _) => adt.variant_of_res(res), |
dfeec247 | 205 | _ => span_bug!(lhs.span, "non-ADT in struct pattern"), |
1a4d82fc | 206 | }; |
85aaf69f | 207 | for pat in pats { |
e74abb32 | 208 | if let PatKind::Wild = pat.pat.kind { |
62682a34 SL |
209 | continue; |
210 | } | |
487cf647 | 211 | let index = self.typeck_results().field_index(pat.hir_id); |
83c7162d | 212 | self.insert_def_id(variant.fields[index].did); |
1a4d82fc JJ |
213 | } |
214 | } | |
215 | ||
064997fb FG |
216 | fn handle_tuple_field_pattern_match( |
217 | &mut self, | |
218 | lhs: &hir::Pat<'_>, | |
219 | res: Res, | |
220 | pats: &[hir::Pat<'_>], | |
f2b60f7d | 221 | dotdot: hir::DotDotPos, |
064997fb FG |
222 | ) { |
223 | let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { | |
224 | ty::Adt(adt, _) => adt.variant_of_res(res), | |
225 | _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), | |
226 | }; | |
f2b60f7d FG |
227 | let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); |
228 | let first_n = pats.iter().enumerate().take(dotdot); | |
064997fb | 229 | let missing = variant.fields.len() - pats.len(); |
f2b60f7d | 230 | let last_n = pats.iter().enumerate().skip(dotdot).map(|(idx, pat)| (idx + missing, pat)); |
064997fb FG |
231 | for (idx, pat) in first_n.chain(last_n) { |
232 | if let PatKind::Wild = pat.kind { | |
233 | continue; | |
234 | } | |
235 | self.insert_def_id(variant.fields[idx].did); | |
236 | } | |
237 | } | |
238 | ||
1a4d82fc | 239 | fn mark_live_symbols(&mut self) { |
0bf4aa26 XL |
240 | let mut scanned = FxHashSet::default(); |
241 | while let Some(id) = self.worklist.pop() { | |
242 | if !scanned.insert(id) { | |
dfeec247 | 243 | continue; |
1a4d82fc | 244 | } |
1a4d82fc | 245 | |
0731742a XL |
246 | // in the case of tuple struct constructors we want to check the item, not the generated |
247 | // tuple struct constructor function | |
94222f64 | 248 | let id = self.struct_constructors.get(&id).copied().unwrap_or(id); |
0731742a | 249 | |
5099ac24 | 250 | if let Some(node) = self.tcx.hir().find_by_def_id(id) { |
3157f602 XL |
251 | self.live_symbols.insert(id); |
252 | self.visit_node(node); | |
1a4d82fc JJ |
253 | } |
254 | } | |
255 | } | |
256 | ||
c295e0f8 XL |
257 | /// Automatically generated items marked with `rustc_trivial_field_reads` |
258 | /// will be ignored for the purposes of dead code analysis (see PR #85200 | |
259 | /// for discussion). | |
5099ac24 | 260 | fn should_ignore_item(&mut self, def_id: DefId) -> bool { |
c295e0f8 XL |
261 | if let Some(impl_of) = self.tcx.impl_of_method(def_id) { |
262 | if !self.tcx.has_attr(impl_of, sym::automatically_derived) { | |
263 | return false; | |
264 | } | |
265 | ||
923072b8 FG |
266 | if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) |
267 | && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) | |
268 | { | |
9c376795 | 269 | let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().subst_identity(); |
923072b8 FG |
270 | if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() |
271 | && let Some(adt_def_id) = adt_def.did().as_local() | |
272 | { | |
273 | self.ignored_derived_traits | |
274 | .entry(adt_def_id) | |
275 | .or_default() | |
276 | .push((trait_of, impl_of)); | |
c295e0f8 | 277 | } |
923072b8 | 278 | return true; |
c295e0f8 XL |
279 | } |
280 | } | |
281 | ||
282 | return false; | |
283 | } | |
284 | ||
0731742a | 285 | fn visit_node(&mut self, node: Node<'tcx>) { |
2b03887a FG |
286 | if let Node::ImplItem(hir::ImplItem { owner_id, .. }) = node |
287 | && self.should_ignore_item(owner_id.to_def_id()) | |
923072b8 FG |
288 | { |
289 | return; | |
c295e0f8 XL |
290 | } |
291 | ||
2c00a5a8 | 292 | let had_repr_c = self.repr_has_repr_c; |
064997fb | 293 | let had_repr_simd = self.repr_has_repr_simd; |
17df50a5 | 294 | self.repr_has_repr_c = false; |
064997fb | 295 | self.repr_has_repr_simd = false; |
0731742a | 296 | match node { |
04454e1e FG |
297 | Node::Item(item) => match item.kind { |
298 | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => { | |
2b03887a | 299 | let def = self.tcx.adt_def(item.owner_id); |
04454e1e | 300 | self.repr_has_repr_c = def.repr().c(); |
064997fb | 301 | self.repr_has_repr_simd = def.repr().simd(); |
532ac7d7 | 302 | |
064997fb | 303 | intravisit::walk_item(self, &item) |
04454e1e FG |
304 | } |
305 | hir::ItemKind::ForeignMod { .. } => {} | |
064997fb | 306 | _ => intravisit::walk_item(self, &item), |
04454e1e | 307 | }, |
b7449926 | 308 | Node::TraitItem(trait_item) => { |
92a42be0 | 309 | intravisit::walk_trait_item(self, trait_item); |
1a4d82fc | 310 | } |
b7449926 | 311 | Node::ImplItem(impl_item) => { |
2b03887a | 312 | let item = self.tcx.local_parent(impl_item.owner_id.def_id); |
064997fb FG |
313 | if self.tcx.impl_trait_ref(item).is_none() { |
314 | //// If it's a type whose items are live, then it's live, too. | |
315 | //// This is done to handle the case where, for example, the static | |
316 | //// method of a private type is used, but the type itself is never | |
317 | //// called directly. | |
318 | let self_ty = self.tcx.type_of(item); | |
319 | match *self_ty.kind() { | |
320 | ty::Adt(def, _) => self.check_def_id(def.did()), | |
321 | ty::Foreign(did) => self.check_def_id(did), | |
322 | ty::Dynamic(data, ..) => { | |
323 | if let Some(def_id) = data.principal_def_id() { | |
324 | self.check_def_id(def_id) | |
325 | } | |
326 | } | |
327 | _ => {} | |
328 | } | |
329 | } | |
92a42be0 | 330 | intravisit::walk_impl_item(self, impl_item); |
1a4d82fc | 331 | } |
b7449926 | 332 | Node::ForeignItem(foreign_item) => { |
7453a54e | 333 | intravisit::walk_foreign_item(self, &foreign_item); |
1a4d82fc | 334 | } |
532ac7d7 | 335 | _ => {} |
1a4d82fc | 336 | } |
064997fb | 337 | self.repr_has_repr_simd = had_repr_simd; |
17df50a5 | 338 | self.repr_has_repr_c = had_repr_c; |
1a4d82fc | 339 | } |
3b2f2976 | 340 | |
5e7ed085 FG |
341 | fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) { |
342 | if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did().is_local() { | |
83c7162d | 343 | for field in fields { |
487cf647 | 344 | let index = self.typeck_results().field_index(field.hir_id); |
83c7162d | 345 | self.insert_def_id(adt.non_enum_variant().fields[index].did); |
3b2f2976 XL |
346 | } |
347 | } | |
348 | } | |
1a4d82fc JJ |
349 | } |
350 | ||
f035d41b | 351 | impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { |
32a655c1 | 352 | fn visit_nested_body(&mut self, body: hir::BodyId) { |
3dfed10e XL |
353 | let old_maybe_typeck_results = |
354 | self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); | |
0731742a | 355 | let body = self.tcx.hir().body(body); |
32a655c1 | 356 | self.visit_body(body); |
3dfed10e | 357 | self.maybe_typeck_results = old_maybe_typeck_results; |
476ff2be | 358 | } |
1a4d82fc | 359 | |
f2b60f7d | 360 | fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) { |
04454e1e | 361 | let tcx = self.tcx; |
2c00a5a8 | 362 | let has_repr_c = self.repr_has_repr_c; |
064997fb | 363 | let has_repr_simd = self.repr_has_repr_simd; |
04454e1e | 364 | let live_fields = def.fields().iter().filter_map(|f| { |
487cf647 | 365 | let def_id = f.def_id; |
064997fb | 366 | if has_repr_c || (f.is_positional() && has_repr_simd) { |
04454e1e FG |
367 | return Some(def_id); |
368 | } | |
2b03887a | 369 | if !tcx.visibility(f.hir_id.owner.def_id).is_public() { |
04454e1e FG |
370 | return None; |
371 | } | |
372 | if tcx.visibility(def_id).is_public() { Some(def_id) } else { None } | |
17df50a5 | 373 | }); |
04454e1e | 374 | self.live_symbols.extend(live_fields); |
1a4d82fc | 375 | |
92a42be0 | 376 | intravisit::walk_struct_def(self, def); |
1a4d82fc JJ |
377 | } |
378 | ||
dfeec247 | 379 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
e74abb32 | 380 | match expr.kind { |
8faf50e0 | 381 | hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { |
3dfed10e | 382 | let res = self.typeck_results().qpath_res(qpath, expr.hir_id); |
48663c56 | 383 | self.handle_res(res); |
476ff2be | 384 | } |
8faf50e0 | 385 | hir::ExprKind::MethodCall(..) => { |
3b2f2976 | 386 | self.lookup_and_handle_method(expr.hir_id); |
1a4d82fc | 387 | } |
8faf50e0 | 388 | hir::ExprKind::Field(ref lhs, ..) => { |
532ac7d7 | 389 | self.handle_field_access(&lhs, expr.hir_id); |
1a4d82fc | 390 | } |
ba9703b0 | 391 | hir::ExprKind::Struct(ref qpath, ref fields, _) => { |
3dfed10e | 392 | let res = self.typeck_results().qpath_res(qpath, expr.hir_id); |
ba9703b0 | 393 | self.handle_res(res); |
5e7ed085 FG |
394 | if let ty::Adt(adt, _) = self.typeck_results().expr_ty(expr).kind() { |
395 | self.mark_as_used_if_union(*adt, fields); | |
3b2f2976 XL |
396 | } |
397 | } | |
dfeec247 | 398 | _ => (), |
1a4d82fc JJ |
399 | } |
400 | ||
92a42be0 | 401 | intravisit::walk_expr(self, expr); |
1a4d82fc JJ |
402 | } |
403 | ||
dfeec247 | 404 | fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { |
e74abb32 XL |
405 | // Inside the body, ignore constructions of variants |
406 | // necessary for the pattern to match. Those construction sites | |
407 | // can't be reached unless the variant is constructed elsewhere. | |
408 | let len = self.ignore_variant_stack.len(); | |
409 | self.ignore_variant_stack.extend(arm.pat.necessary_variants()); | |
410 | intravisit::walk_arm(self, arm); | |
411 | self.ignore_variant_stack.truncate(len); | |
62682a34 SL |
412 | } |
413 | ||
dfeec247 | 414 | fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { |
5869c6ff | 415 | self.in_pat = true; |
e74abb32 | 416 | match pat.kind { |
416331ca | 417 | PatKind::Struct(ref path, ref fields, _) => { |
3dfed10e | 418 | let res = self.typeck_results().qpath_res(path, pat.hir_id); |
416331ca | 419 | self.handle_field_pattern_match(pat, res, fields); |
1a4d82fc | 420 | } |
e1599b0c | 421 | PatKind::Path(ref qpath) => { |
3dfed10e | 422 | let res = self.typeck_results().qpath_res(qpath, pat.hir_id); |
48663c56 | 423 | self.handle_res(res); |
1a4d82fc | 424 | } |
064997fb FG |
425 | PatKind::TupleStruct(ref qpath, ref fields, dotdot) => { |
426 | let res = self.typeck_results().qpath_res(qpath, pat.hir_id); | |
427 | self.handle_tuple_field_pattern_match(pat, res, fields, dotdot); | |
428 | } | |
dfeec247 | 429 | _ => (), |
1a4d82fc JJ |
430 | } |
431 | ||
92a42be0 | 432 | intravisit::walk_pat(self, pat); |
abe05a73 | 433 | self.in_pat = false; |
1a4d82fc JJ |
434 | } |
435 | ||
487cf647 | 436 | fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { |
48663c56 | 437 | self.handle_res(path.res); |
92a42be0 | 438 | intravisit::walk_path(self, path); |
1a4d82fc | 439 | } |
532ac7d7 | 440 | |
dfeec247 | 441 | fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { |
f2b60f7d | 442 | if let TyKind::OpaqueDef(item_id, _, _) = ty.kind { |
6a06907d | 443 | let item = self.tcx.hir().item(item_id); |
ba9703b0 | 444 | intravisit::walk_item(self, item); |
532ac7d7 XL |
445 | } |
446 | intravisit::walk_ty(self, ty); | |
447 | } | |
e1599b0c XL |
448 | |
449 | fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { | |
94222f64 XL |
450 | // When inline const blocks are used in pattern position, paths |
451 | // referenced by it should be considered as used. | |
452 | let in_pat = mem::replace(&mut self.in_pat, false); | |
453 | ||
454 | self.live_symbols.insert(self.tcx.hir().local_def_id(c.hir_id)); | |
e1599b0c | 455 | intravisit::walk_anon_const(self, c); |
94222f64 XL |
456 | |
457 | self.in_pat = in_pat; | |
e1599b0c | 458 | } |
1a4d82fc JJ |
459 | } |
460 | ||
064997fb FG |
461 | fn has_allow_dead_code_or_lang_attr_helper( |
462 | tcx: TyCtxt<'_>, | |
463 | id: hir::HirId, | |
464 | lint: &'static lint::Lint, | |
465 | ) -> bool { | |
6a06907d | 466 | let attrs = tcx.hir().attrs(id); |
3dfed10e | 467 | if tcx.sess.contains_name(attrs, sym::lang) { |
8faf50e0 XL |
468 | return true; |
469 | } | |
470 | ||
a1dfa0c6 | 471 | // Stable attribute for #[lang = "panic_impl"] |
3dfed10e | 472 | if tcx.sess.contains_name(attrs, sym::panic_handler) { |
8faf50e0 XL |
473 | return true; |
474 | } | |
475 | ||
416331ca | 476 | let def_id = tcx.hir().local_def_id(id); |
04454e1e FG |
477 | if tcx.def_kind(def_id).has_codegen_attrs() { |
478 | let cg_attrs = tcx.codegen_fn_attrs(def_id); | |
479 | ||
480 | // #[used], #[no_mangle], #[export_name], etc also keeps the item alive | |
481 | // forcefully, e.g., for placing it in a specific section. | |
482 | if cg_attrs.contains_extern_indicator() | |
483 | || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) | |
484 | || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) | |
485 | { | |
486 | return true; | |
487 | } | |
041b39d2 XL |
488 | } |
489 | ||
064997fb FG |
490 | tcx.lint_level_at_node(lint, id).0 == lint::Allow |
491 | } | |
492 | ||
493 | fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { | |
494 | has_allow_dead_code_or_lang_attr_helper(tcx, id, lint::builtin::DEAD_CODE) | |
1a4d82fc JJ |
495 | } |
496 | ||
923072b8 | 497 | // These check_* functions seeds items that |
1a4d82fc JJ |
498 | // 1) We want to explicitly consider as live: |
499 | // * Item annotated with #[allow(dead_code)] | |
500 | // - This is done so that if we want to suppress warnings for a | |
501 | // group of dead functions, we only have to annotate the "root". | |
502 | // For example, if both `f` and `g` are dead and `f` calls `g`, | |
503 | // then annotating `f` with `#[allow(dead_code)]` will suppress | |
504 | // warning for both `f` and `g`. | |
505 | // * Item annotated with #[lang=".."] | |
506 | // - This is because lang items are always callable from elsewhere. | |
507 | // or | |
508 | // 2) We are not sure to be live or not | |
1b1a35ee | 509 | // * Implementations of traits and trait methods |
923072b8 | 510 | fn check_item<'tcx>( |
dc9dc135 | 511 | tcx: TyCtxt<'tcx>, |
923072b8 FG |
512 | worklist: &mut Vec<LocalDefId>, |
513 | struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>, | |
514 | id: hir::ItemId, | |
515 | ) { | |
516 | let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id()); | |
517 | if allow_dead_code { | |
2b03887a | 518 | worklist.push(id.owner_id.def_id); |
923072b8 FG |
519 | } |
520 | ||
2b03887a | 521 | match tcx.def_kind(id.owner_id) { |
923072b8 FG |
522 | DefKind::Enum => { |
523 | let item = tcx.hir().item(id); | |
524 | if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { | |
532ac7d7 | 525 | if allow_dead_code { |
487cf647 | 526 | worklist.extend(enum_def.variants.iter().map(|variant| variant.def_id)); |
532ac7d7 XL |
527 | } |
528 | ||
dfeec247 | 529 | for variant in enum_def.variants { |
487cf647 FG |
530 | if let Some(ctor_def_id) = variant.data.ctor_def_id() { |
531 | struct_constructors.insert(ctor_def_id, variant.def_id); | |
532ac7d7 XL |
532 | } |
533 | } | |
1a4d82fc | 534 | } |
923072b8 FG |
535 | } |
536 | DefKind::Impl => { | |
2b03887a | 537 | let of_trait = tcx.impl_trait_ref(id.owner_id); |
923072b8 FG |
538 | |
539 | if of_trait.is_some() { | |
2b03887a | 540 | worklist.push(id.owner_id.def_id); |
1a4d82fc | 541 | } |
923072b8 FG |
542 | |
543 | // get DefIds from another query | |
544 | let local_def_ids = tcx | |
2b03887a | 545 | .associated_item_def_ids(id.owner_id) |
923072b8 FG |
546 | .iter() |
547 | .filter_map(|def_id| def_id.as_local()); | |
548 | ||
549 | // And we access the Map here to get HirId from LocalDefId | |
550 | for id in local_def_ids { | |
551 | if of_trait.is_some() | |
552 | || has_allow_dead_code_or_lang_attr(tcx, tcx.hir().local_def_id_to_hir_id(id)) | |
553 | { | |
554 | worklist.push(id); | |
532ac7d7 | 555 | } |
0731742a | 556 | } |
923072b8 FG |
557 | } |
558 | DefKind::Struct => { | |
559 | let item = tcx.hir().item(id); | |
560 | if let hir::ItemKind::Struct(ref variant_data, _) = item.kind | |
561 | && let Some(ctor_hir_id) = variant_data.ctor_hir_id() | |
562 | { | |
2b03887a | 563 | struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.owner_id.def_id); |
04454e1e | 564 | } |
1a4d82fc | 565 | } |
923072b8 FG |
566 | DefKind::GlobalAsm => { |
567 | // global_asm! is always live. | |
2b03887a | 568 | worklist.push(id.owner_id.def_id); |
923072b8 FG |
569 | } |
570 | _ => {} | |
1a4d82fc | 571 | } |
923072b8 | 572 | } |
476ff2be | 573 | |
9c376795 | 574 | fn check_trait_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) { |
923072b8 | 575 | use hir::TraitItemKind::{Const, Fn}; |
2b03887a | 576 | if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { |
923072b8 | 577 | let trait_item = tcx.hir().trait_item(id); |
fc512014 | 578 | if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_))) |
923072b8 | 579 | && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id()) |
fc512014 | 580 | { |
2b03887a | 581 | worklist.push(trait_item.owner_id.def_id); |
fc512014 | 582 | } |
32a655c1 | 583 | } |
923072b8 | 584 | } |
32a655c1 | 585 | |
9c376795 | 586 | fn check_foreign_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::ForeignItemId) { |
2b03887a | 587 | if matches!(tcx.def_kind(id.owner_id), DefKind::Static(_) | DefKind::Fn) |
923072b8 FG |
588 | && has_allow_dead_code_or_lang_attr(tcx, id.hir_id()) |
589 | { | |
2b03887a | 590 | worklist.push(id.owner_id.def_id); |
fc512014 | 591 | } |
1a4d82fc JJ |
592 | } |
593 | ||
9c376795 FG |
594 | fn create_and_seed_worklist( |
595 | tcx: TyCtxt<'_>, | |
94222f64 | 596 | ) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) { |
2b03887a | 597 | let effective_visibilities = &tcx.effective_visibilities(()); |
923072b8 FG |
598 | // see `MarkSymbolVisitor::struct_constructors` |
599 | let mut struct_constructors = Default::default(); | |
2b03887a | 600 | let mut worklist = effective_visibilities |
dfeec247 | 601 | .iter() |
2b03887a FG |
602 | .filter_map(|(&id, effective_vis)| { |
603 | effective_vis.is_public_at_level(Level::Reachable).then_some(id) | |
604 | }) | |
94222f64 XL |
605 | // Seed entry point |
606 | .chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local())) | |
dfeec247 | 607 | .collect::<Vec<_>>(); |
1a4d82fc | 608 | |
923072b8 FG |
609 | let crate_items = tcx.hir_crate_items(()); |
610 | for id in crate_items.items() { | |
611 | check_item(tcx, &mut worklist, &mut struct_constructors, id); | |
612 | } | |
613 | ||
614 | for id in crate_items.trait_items() { | |
615 | check_trait_item(tcx, &mut worklist, id); | |
616 | } | |
1a4d82fc | 617 | |
923072b8 FG |
618 | for id in crate_items.foreign_items() { |
619 | check_foreign_item(tcx, &mut worklist, id); | |
620 | } | |
621 | ||
622 | (worklist, struct_constructors) | |
1a4d82fc JJ |
623 | } |
624 | ||
9c376795 FG |
625 | fn live_symbols_and_ignored_derived_traits( |
626 | tcx: TyCtxt<'_>, | |
5099ac24 FG |
627 | (): (), |
628 | ) -> (FxHashSet<LocalDefId>, FxHashMap<LocalDefId, Vec<(DefId, DefId)>>) { | |
629 | let (worklist, struct_constructors) = create_and_seed_worklist(tcx); | |
32a655c1 | 630 | let mut symbol_visitor = MarkSymbolVisitor { |
041b39d2 XL |
631 | worklist, |
632 | tcx, | |
3dfed10e | 633 | maybe_typeck_results: None, |
0bf4aa26 | 634 | live_symbols: Default::default(), |
2c00a5a8 | 635 | repr_has_repr_c: false, |
064997fb | 636 | repr_has_repr_simd: false, |
abe05a73 | 637 | in_pat: false, |
32a655c1 | 638 | ignore_variant_stack: vec![], |
0731742a | 639 | struct_constructors, |
5099ac24 | 640 | ignored_derived_traits: FxHashMap::default(), |
32a655c1 | 641 | }; |
1a4d82fc | 642 | symbol_visitor.mark_live_symbols(); |
5099ac24 | 643 | (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) |
1a4d82fc JJ |
644 | } |
645 | ||
923072b8 | 646 | struct DeadVariant { |
064997fb | 647 | def_id: LocalDefId, |
923072b8 FG |
648 | name: Symbol, |
649 | level: lint::Level, | |
650 | } | |
651 | ||
dc9dc135 XL |
652 | struct DeadVisitor<'tcx> { |
653 | tcx: TyCtxt<'tcx>, | |
5099ac24 FG |
654 | live_symbols: &'tcx FxHashSet<LocalDefId>, |
655 | ignored_derived_traits: &'tcx FxHashMap<LocalDefId, Vec<(DefId, DefId)>>, | |
1a4d82fc JJ |
656 | } |
657 | ||
064997fb FG |
658 | enum ShouldWarnAboutField { |
659 | Yes(bool), // positional? | |
660 | No, | |
661 | } | |
32a655c1 | 662 | |
064997fb FG |
663 | impl<'tcx> DeadVisitor<'tcx> { |
664 | fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> ShouldWarnAboutField { | |
665 | if self.live_symbols.contains(&field.did.expect_local()) { | |
666 | return ShouldWarnAboutField::No; | |
667 | } | |
668 | let field_type = self.tcx.type_of(field.did); | |
669 | if field_type.is_phantom_data() { | |
670 | return ShouldWarnAboutField::No; | |
671 | } | |
672 | let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); | |
673 | if is_positional | |
674 | && self | |
675 | .tcx | |
676 | .layout_of(self.tcx.param_env(field.did).and(field_type)) | |
677 | .map_or(true, |layout| layout.is_zst()) | |
678 | { | |
679 | return ShouldWarnAboutField::No; | |
1a4d82fc | 680 | } |
064997fb | 681 | ShouldWarnAboutField::Yes(is_positional) |
1a4d82fc JJ |
682 | } |
683 | ||
923072b8 FG |
684 | fn warn_multiple_dead_codes( |
685 | &self, | |
064997fb | 686 | dead_codes: &[LocalDefId], |
923072b8 | 687 | participle: &str, |
064997fb FG |
688 | parent_item: Option<LocalDefId>, |
689 | is_positional: bool, | |
923072b8 | 690 | ) { |
487cf647 FG |
691 | let Some(&first_id) = dead_codes.first() else { |
692 | return; | |
693 | }; | |
694 | let tcx = self.tcx; | |
695 | let names: Vec<_> = | |
696 | dead_codes.iter().map(|&def_id| tcx.item_name(def_id.to_def_id())).collect(); | |
697 | let spans: Vec<_> = dead_codes | |
698 | .iter() | |
699 | .map(|&def_id| match tcx.def_ident_span(def_id) { | |
700 | Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()), | |
701 | None => tcx.def_span(def_id), | |
702 | }) | |
703 | .collect(); | |
704 | ||
705 | let descr = tcx.def_kind(first_id).descr(first_id.to_def_id()); | |
706 | let num = dead_codes.len(); | |
707 | let multiple = num > 6; | |
708 | let name_list = names.into(); | |
709 | ||
710 | let lint = if is_positional { | |
711 | lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS | |
712 | } else { | |
713 | lint::builtin::DEAD_CODE | |
714 | }; | |
715 | ||
716 | let parent_info = if let Some(parent_item) = parent_item { | |
717 | let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id()); | |
718 | Some(ParentInfo { | |
719 | num, | |
720 | descr, | |
721 | parent_descr, | |
722 | span: tcx.def_ident_span(parent_item).unwrap(), | |
723 | }) | |
724 | } else { | |
725 | None | |
726 | }; | |
727 | ||
728 | let encl_def_id = parent_item.unwrap_or(first_id); | |
729 | let ignored_derived_impls = | |
730 | if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) { | |
731 | let trait_list = ign_traits | |
732 | .iter() | |
733 | .map(|(trait_id, _)| self.tcx.item_name(*trait_id)) | |
734 | .collect::<Vec<_>>(); | |
735 | let trait_list_len = trait_list.len(); | |
736 | Some(IgnoredDerivedImpls { | |
737 | name: self.tcx.item_name(encl_def_id.to_def_id()), | |
738 | trait_list: trait_list.into(), | |
739 | trait_list_len, | |
064997fb | 740 | }) |
487cf647 FG |
741 | } else { |
742 | None | |
2b03887a | 743 | }; |
064997fb | 744 | |
487cf647 FG |
745 | let diag = if is_positional { |
746 | MultipleDeadCodes::UnusedTupleStructFields { | |
747 | multiple, | |
748 | num, | |
749 | descr, | |
750 | participle, | |
751 | name_list, | |
752 | change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() }, | |
753 | parent_info, | |
754 | ignored_derived_impls, | |
755 | } | |
756 | } else { | |
757 | MultipleDeadCodes::DeadCodes { | |
758 | multiple, | |
759 | num, | |
760 | descr, | |
761 | participle, | |
762 | name_list, | |
763 | parent_info, | |
764 | ignored_derived_impls, | |
765 | } | |
766 | }; | |
064997fb | 767 | |
487cf647 FG |
768 | self.tcx.emit_spanned_lint( |
769 | lint, | |
770 | tcx.hir().local_def_id_to_hir_id(first_id), | |
771 | MultiSpan::from_spans(spans), | |
772 | diag, | |
773 | ); | |
923072b8 FG |
774 | } |
775 | ||
776 | fn warn_dead_fields_and_variants( | |
777 | &self, | |
064997fb | 778 | def_id: LocalDefId, |
923072b8 FG |
779 | participle: &str, |
780 | dead_codes: Vec<DeadVariant>, | |
064997fb | 781 | is_positional: bool, |
923072b8 FG |
782 | ) { |
783 | let mut dead_codes = dead_codes | |
784 | .iter() | |
785 | .filter(|v| !v.name.as_str().starts_with('_')) | |
923072b8 FG |
786 | .collect::<Vec<&DeadVariant>>(); |
787 | if dead_codes.is_empty() { | |
788 | return; | |
789 | } | |
790 | dead_codes.sort_by_key(|v| v.level); | |
791 | for (_, group) in &dead_codes.into_iter().group_by(|v| v.level) { | |
792 | self.warn_multiple_dead_codes( | |
064997fb | 793 | &group.map(|v| v.def_id).collect::<Vec<_>>(), |
923072b8 | 794 | participle, |
064997fb FG |
795 | Some(def_id), |
796 | is_positional, | |
923072b8 FG |
797 | ); |
798 | } | |
799 | } | |
800 | ||
064997fb FG |
801 | fn warn_dead_code(&mut self, id: LocalDefId, participle: &str) { |
802 | self.warn_multiple_dead_codes(&[id], participle, None, false); | |
1a4d82fc | 803 | } |
1a4d82fc | 804 | |
064997fb FG |
805 | fn check_definition(&mut self, def_id: LocalDefId) { |
806 | if self.live_symbols.contains(&def_id) { | |
807 | return; | |
808 | } | |
809 | let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); | |
810 | if has_allow_dead_code_or_lang_attr(self.tcx, hir_id) { | |
811 | return; | |
812 | } | |
813 | let Some(name) = self.tcx.opt_item_name(def_id.to_def_id()) else { | |
814 | return | |
815 | }; | |
816 | if name.as_str().starts_with('_') { | |
817 | return; | |
818 | } | |
819 | match self.tcx.def_kind(def_id) { | |
820 | DefKind::AssocConst | |
821 | | DefKind::AssocFn | |
822 | | DefKind::Fn | |
823 | | DefKind::Static(_) | |
824 | | DefKind::Const | |
825 | | DefKind::TyAlias | |
826 | | DefKind::Enum | |
827 | | DefKind::Union | |
828 | | DefKind::ForeignTy => self.warn_dead_code(def_id, "used"), | |
829 | DefKind::Struct => self.warn_dead_code(def_id, "constructed"), | |
830 | DefKind::Variant | DefKind::Field => bug!("should be handled specially"), | |
831 | _ => {} | |
92a42be0 SL |
832 | } |
833 | } | |
064997fb | 834 | } |
92a42be0 | 835 | |
064997fb FG |
836 | fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) { |
837 | let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(()); | |
838 | let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; | |
5099ac24 | 839 | |
064997fb | 840 | let module_items = tcx.hir_module_items(module); |
923072b8 | 841 | |
064997fb | 842 | for item in module_items.items() { |
2b03887a FG |
843 | if !live_symbols.contains(&item.owner_id.def_id) { |
844 | let parent = tcx.local_parent(item.owner_id.def_id); | |
064997fb FG |
845 | if parent != module && !live_symbols.contains(&parent) { |
846 | // We already have diagnosed something. | |
847 | continue; | |
848 | } | |
2b03887a | 849 | visitor.check_definition(item.owner_id.def_id); |
064997fb | 850 | continue; |
1a4d82fc | 851 | } |
1a4d82fc | 852 | |
2b03887a | 853 | let def_kind = tcx.def_kind(item.owner_id); |
064997fb | 854 | if let DefKind::Struct | DefKind::Union | DefKind::Enum = def_kind { |
2b03887a | 855 | let adt = tcx.adt_def(item.owner_id); |
064997fb | 856 | let mut dead_variants = Vec::new(); |
1a4d82fc | 857 | |
064997fb FG |
858 | for variant in adt.variants() { |
859 | let def_id = variant.def_id.expect_local(); | |
860 | if !live_symbols.contains(&def_id) { | |
861 | // Record to group diagnostics. | |
862 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); | |
863 | let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0; | |
864 | dead_variants.push(DeadVariant { def_id, name: variant.name, level }); | |
865 | continue; | |
d9579d0f | 866 | } |
064997fb FG |
867 | |
868 | let mut is_positional = false; | |
869 | let dead_fields = variant | |
870 | .fields | |
871 | .iter() | |
872 | .filter_map(|field| { | |
873 | let def_id = field.did.expect_local(); | |
874 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); | |
875 | if let ShouldWarnAboutField::Yes(is_pos) = | |
876 | visitor.should_warn_about_field(&field) | |
877 | { | |
878 | let level = tcx | |
879 | .lint_level_at_node( | |
880 | if is_pos { | |
881 | is_positional = true; | |
882 | lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS | |
883 | } else { | |
884 | lint::builtin::DEAD_CODE | |
885 | }, | |
886 | hir_id, | |
887 | ) | |
888 | .0; | |
889 | Some(DeadVariant { def_id, name: field.name, level }) | |
890 | } else { | |
891 | None | |
892 | } | |
893 | }) | |
894 | .collect(); | |
895 | visitor.warn_dead_fields_and_variants(def_id, "read", dead_fields, is_positional) | |
d9579d0f | 896 | } |
064997fb | 897 | |
2b03887a FG |
898 | visitor.warn_dead_fields_and_variants( |
899 | item.owner_id.def_id, | |
900 | "constructed", | |
901 | dead_variants, | |
902 | false, | |
903 | ); | |
d9579d0f AL |
904 | } |
905 | } | |
906 | ||
064997fb | 907 | for impl_item in module_items.impl_items() { |
2b03887a | 908 | visitor.check_definition(impl_item.owner_id.def_id); |
1a4d82fc | 909 | } |
1a4d82fc | 910 | |
064997fb | 911 | for foreign_item in module_items.foreign_items() { |
2b03887a | 912 | visitor.check_definition(foreign_item.owner_id.def_id); |
064997fb FG |
913 | } |
914 | ||
915 | // We do not warn trait items. | |
5099ac24 FG |
916 | } |
917 | ||
918 | pub(crate) fn provide(providers: &mut Providers) { | |
919 | *providers = | |
920 | Providers { live_symbols_and_ignored_derived_traits, check_mod_deathness, ..*providers }; | |
1a4d82fc | 921 | } |