rustc_mir_transform/
lint_and_remove_uninhabited.rs1use rustc_hir::def::DefKind;
2use rustc_middle::mir::*;
3use rustc_middle::ty::TyCtxt;
4use rustc_session::lint::builtin::UNREACHABLE_CODE;
5
6use crate::errors::UnreachableDueToUninhabited;
7
8pub(super) struct LintAndRemoveUninhabited;
11
12impl<'tcx> crate::MirPass<'tcx> for LintAndRemoveUninhabited {
13 #[tracing::instrument(level = "debug", skip_all)]
14 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
15 let def_id = body.source.def_id().expect_local();
16 tracing::debug!(?def_id);
17 let parent_module = tcx.parent_module_from_def_id(def_id).to_def_id();
18 let typing_env = body.typing_env(tcx);
19
20 let return_ty_is_inhabited = matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)
24 && body.local_decls[RETURN_PLACE].ty.is_inhabited_from(tcx, parent_module, typing_env);
25
26 let mut lints = vec![];
27 for bbdata in body.basic_blocks.as_mut() {
28 let term = bbdata.terminator_mut();
29 let TerminatorKind::Call { ref mut target, destination, .. } = term.kind else {
30 continue;
31 };
32 let Some(target_bb) = *target else { continue };
33
34 let ty = destination.ty(&body.local_decls, tcx).ty;
35 let ty_is_inhabited = ty.is_inhabited_from(tcx, parent_module, typing_env);
36 if !ty_is_inhabited {
37 if !ty.is_never() && return_ty_is_inhabited {
47 lints.push((target_bb, ty, term.source_info.span));
48 }
49
50 *target = None;
55 }
56 }
57
58 for (target_bb, orig_ty, orig_span) in lints {
59 if orig_span.in_external_macro(tcx.sess.source_map()) {
60 continue;
61 }
62
63 let Some((target_loc, descr)) = find_unreachable_code_from(target_bb, body) else {
64 continue;
65 };
66 let lint_root = body.source_scopes[target_loc.scope]
67 .local_data
68 .as_ref()
69 .unwrap_crate_local()
70 .lint_root;
71 tcx.emit_node_span_lint(
72 UNREACHABLE_CODE,
73 lint_root,
74 target_loc.span,
75 UnreachableDueToUninhabited {
76 expr: target_loc.span,
77 orig: orig_span,
78 descr,
79 ty: orig_ty,
80 },
81 );
82 }
83 }
84
85 fn is_required(&self) -> bool {
86 true
87 }
88}
89
90#[tracing::instrument(level = "debug", skip(body), ret)]
92fn find_unreachable_code_from<'tcx>(
93 bb: BasicBlock,
94 body: &Body<'tcx>,
95) -> Option<(SourceInfo, &'static str)> {
96 let bb = &body.basic_blocks[bb];
97 for stmt in &bb.statements {
98 match &stmt.kind {
99 StatementKind::Assign((_, Rvalue::Use(Operand::Constant(const_), _)))
101 if const_.ty().is_unit() =>
102 {
103 continue;
104 }
105 StatementKind::Assign((place, _)) if place.as_local() == Some(RETURN_PLACE) => {
109 continue;
110 }
111 StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => {
112 continue;
113 }
114 StatementKind::FakeRead(..) => return Some((stmt.source_info, "definition")),
115 _ => return Some((stmt.source_info, "expression")),
116 }
117 }
118
119 let term = bb.terminator();
120 match term.kind {
121 TerminatorKind::Goto { .. } | TerminatorKind::Return => None,
123 _ => Some((term.source_info, "expression")),
124 }
125}