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