Skip to main content

rustc_mir_build/
check_unsafety.rs

1use std::borrow::Cow;
2use std::mem;
3
4use rustc_ast::AsmMacro;
5use rustc_data_structures::stack::ensure_sufficient_stack;
6use rustc_errors::DiagArgValue;
7use rustc_hir::def::DefKind;
8use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
9use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
10use rustc_middle::span_bug;
11use rustc_middle::thir::visit::Visitor;
12use rustc_middle::thir::*;
13use rustc_middle::ty::print::with_no_trimmed_paths;
14use rustc_middle::ty::{self, Ty, TyCtxt};
15use rustc_session::lint::Level;
16use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
17use rustc_span::def_id::{DefId, LocalDefId};
18use rustc_span::{Span, Symbol};
19
20use crate::errors::*;
21
22struct UnsafetyVisitor<'a, 'tcx> {
23    tcx: TyCtxt<'tcx>,
24    thir: &'a Thir<'tcx>,
25    /// The `HirId` of the current scope, which would be the `HirId`
26    /// of the current HIR node, modulo adjustments. Used for lint levels.
27    hir_context: HirId,
28    /// The current "safety context". This notably tracks whether we are in an
29    /// `unsafe` block, and whether it has been used.
30    safety_context: SafetyContext,
31    /// The `#[target_feature]` attributes of the body. Used for checking
32    /// calls to functions with `#[target_feature]` (RFC 2396).
33    body_target_features: &'tcx [TargetFeature],
34    /// When inside the LHS of an assignment to a field, this is the type
35    /// of the LHS and the span of the assignment expression.
36    assignment_info: Option<Ty<'tcx>>,
37    in_union_destructure: bool,
38    typing_env: ty::TypingEnv<'tcx>,
39    inside_adt: bool,
40    warnings: &'a mut Vec<UnusedUnsafeWarning>,
41
42    /// Flag to ensure that we only suggest wrapping the entire function body in
43    /// an unsafe block once.
44    suggest_unsafe_block: bool,
45}
46
47impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
48    fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
49        let prev_context = mem::replace(&mut self.safety_context, safety_context);
50
51        f(self);
52
53        let safety_context = mem::replace(&mut self.safety_context, prev_context);
54        if let SafetyContext::UnsafeBlock { used, span, hir_id, nested_used_blocks } =
55            safety_context
56        {
57            if !used {
58                self.warn_unused_unsafe(hir_id, span, None);
59
60                if let SafetyContext::UnsafeBlock {
61                    nested_used_blocks: ref mut prev_nested_used_blocks,
62                    ..
63                } = self.safety_context
64                {
65                    prev_nested_used_blocks.extend(nested_used_blocks);
66                }
67            } else {
68                for block in nested_used_blocks {
69                    self.warn_unused_unsafe(
70                        block.hir_id,
71                        block.span,
72                        Some(UnusedUnsafeEnclosing::Block {
73                            span: self.tcx.sess.source_map().guess_head_span(span),
74                        }),
75                    );
76                }
77
78                match self.safety_context {
79                    SafetyContext::UnsafeBlock {
80                        nested_used_blocks: ref mut prev_nested_used_blocks,
81                        ..
82                    } => {
83                        prev_nested_used_blocks.push(NestedUsedBlock { hir_id, span });
84                    }
85                    _ => (),
86                }
87            }
88        }
89    }
90
91    fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool {
92        match kind {
93            // Allow calls to deprecated-safe unsafe functions if the caller is
94            // from an edition before 2024.
95            &UnsafeOpKind::CallToUnsafeFunction(Some(id))
96                if !span.at_least_rust_2024()
97                    && let Some(suggestion) = {
    {
        'done:
            {
            for i in ::rustc_hir::attrs::HasAttrs::get_attrs(id, &self.tcx) {
                #[allow(unused_imports)]
                use rustc_hir::attrs::AttributeKind::*;
                let i: &rustc_hir::Attribute = i;
                match i {
                    rustc_hir::Attribute::Parsed(RustcDeprecatedSafe2024 {
                        suggestion }) => {
                        break 'done Some(suggestion);
                    }
                    rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(self.tcx, id, RustcDeprecatedSafe2024{suggestion} => suggestion) =>
98            {
99                let sm = self.tcx.sess.source_map();
100                let guarantee = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("that {0}", suggestion))
    })format!("that {}", suggestion);
101                let suggestion = sm
102                    .indentation_before(span)
103                    .map(|indent| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}// FIXME: Audit that {1}.\n",
                indent, suggestion))
    })format!("{}// FIXME: Audit that {}.\n", indent, suggestion))
104                    .unwrap_or_default();
105
106                self.tcx.emit_node_span_lint(
107                    DEPRECATED_SAFE_2024,
108                    self.hir_context,
109                    span,
110                    CallToDeprecatedSafeFnRequiresUnsafe {
111                        span,
112                        function: { let _guard = NoTrimmedGuard::new(); self.tcx.def_path_str(id) }with_no_trimmed_paths!(self.tcx.def_path_str(id)),
113                        sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
114                            start_of_line_suggestion: suggestion,
115                            start_of_line: sm.span_extend_to_line(span).shrink_to_lo(),
116                            left: span.shrink_to_lo(),
117                            right: span.shrink_to_hi(),
118                            guarantee,
119                        },
120                    },
121                );
122                true
123            }
124            _ => false,
125        }
126    }
127
128    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
129        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
130        match self.safety_context {
131            SafetyContext::BuiltinUnsafeBlock => {}
132            SafetyContext::UnsafeBlock { ref mut used, .. } => {
133                // Mark this block as useful (even inside `unsafe fn`, where it is technically
134                // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
135                // default which will require those blocks:
136                // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
137                *used = true;
138            }
139            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
140            SafetyContext::UnsafeFn => {
141                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
142                if !deprecated_safe_fn {
143                    // unsafe_op_in_unsafe_fn is disallowed
144                    kind.emit_unsafe_op_in_unsafe_fn_lint(
145                        self.tcx,
146                        self.hir_context,
147                        span,
148                        self.suggest_unsafe_block,
149                    );
150                    self.suggest_unsafe_block = false;
151                }
152            }
153            SafetyContext::Safe => {
154                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
155                if !deprecated_safe_fn {
156                    kind.emit_requires_unsafe_err(
157                        self.tcx,
158                        span,
159                        self.hir_context,
160                        unsafe_op_in_unsafe_fn_allowed,
161                    );
162                }
163            }
164        }
165    }
166
167    fn warn_unused_unsafe(
168        &mut self,
169        hir_id: HirId,
170        block_span: Span,
171        enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
172    ) {
173        self.warnings.push(UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe });
174    }
175
176    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
177    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
178        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
179    }
180
181    /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
182    fn visit_inner_body(&mut self, def: LocalDefId) {
183        if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
184            // Run all other queries that depend on THIR.
185            self.tcx.ensure_done().mir_built(def);
186            let inner_thir = if self.tcx.sess.opts.unstable_opts.no_steal_thir {
187                &inner_thir.borrow()
188            } else {
189                // We don't have other use for the THIR. Steal it to reduce memory usage.
190                &inner_thir.steal()
191            };
192            let hir_context = self.tcx.local_def_id_to_hir_id(def);
193            let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
194            let mut inner_visitor = UnsafetyVisitor {
195                tcx: self.tcx,
196                thir: inner_thir,
197                hir_context,
198                safety_context,
199                body_target_features: self.body_target_features,
200                assignment_info: self.assignment_info,
201                in_union_destructure: false,
202                typing_env: self.typing_env,
203                inside_adt: false,
204                warnings: self.warnings,
205                suggest_unsafe_block: self.suggest_unsafe_block,
206            };
207            // params in THIR may be unsafe, e.g. a union pattern.
208            for param in &inner_thir.params {
209                if let Some(param_pat) = param.pat.as_deref() {
210                    inner_visitor.visit_pat(param_pat);
211                }
212            }
213            // Visit the body.
214            inner_visitor.visit_expr(&inner_thir[expr]);
215            // Unsafe blocks can be used in the inner body, make sure to take it into account
216            self.safety_context = inner_visitor.safety_context;
217        }
218    }
219}
220
221impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
222    fn thir(&self) -> &'a Thir<'tcx> {
223        self.thir
224    }
225
226    fn visit_block(&mut self, block: &'a Block) {
227        match block.safety_mode {
228            // compiler-generated unsafe code should not count towards the usefulness of
229            // an outer unsafe block
230            BlockSafety::BuiltinUnsafe => {
231                self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
232                    visit::walk_block(this, block)
233                });
234            }
235            BlockSafety::ExplicitUnsafe(hir_id) => {
236                let used = #[allow(non_exhaustive_omitted_patterns)] match self.tcx.lint_level_at_node(UNUSED_UNSAFE,
            hir_id).level {
    Level::Allow => true,
    _ => false,
}matches!(
237                    self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
238                    Level::Allow
239                );
240                self.in_safety_context(
241                    SafetyContext::UnsafeBlock {
242                        span: block.span,
243                        hir_id,
244                        used,
245                        nested_used_blocks: Vec::new(),
246                    },
247                    |this| visit::walk_block(this, block),
248                );
249            }
250            BlockSafety::Safe => {
251                visit::walk_block(self, block);
252            }
253        }
254    }
255
256    fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
257        if self.in_union_destructure {
258            match pat.kind {
259                PatKind::Missing => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
260                // binding to a variable allows getting stuff out of variable
261                PatKind::Binding { .. }
262                // match is conditional on having this value
263                | PatKind::Constant { .. }
264                | PatKind::Variant { .. }
265                | PatKind::Leaf { .. }
266                | PatKind::Deref { .. }
267                | PatKind::DerefPattern { .. }
268                | PatKind::Range { .. }
269                | PatKind::Slice { .. }
270                | PatKind::Array { .. }
271                | PatKind::Guard { .. }
272                // Never constitutes a witness of uninhabitedness.
273                | PatKind::Never => {
274                    self.requires_unsafe(pat.span, AccessToUnionField);
275                    return; // we can return here since this already requires unsafe
276                }
277                // wildcard doesn't read anything.
278                PatKind::Wild |
279                // these just wrap other patterns, which we recurse on below.
280                PatKind::Or { .. } |
281                PatKind::Error(_) => {}
282            }
283        };
284
285        match &pat.kind {
286            PatKind::Leaf { subpatterns, .. } => {
287                if let ty::Adt(adt_def, ..) = pat.ty.kind() {
288                    for pat in subpatterns {
289                        if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() {
290                            self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
291                        }
292                    }
293                    if adt_def.is_union() {
294                        let old_in_union_destructure =
295                            std::mem::replace(&mut self.in_union_destructure, true);
296                        visit::walk_pat(self, pat);
297                        self.in_union_destructure = old_in_union_destructure;
298                    } else {
299                        visit::walk_pat(self, pat);
300                    }
301                } else {
302                    visit::walk_pat(self, pat);
303                }
304            }
305            PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
306                for pat in subpatterns {
307                    let field = &pat.field;
308                    if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() {
309                        self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
310                    }
311                }
312                visit::walk_pat(self, pat);
313            }
314            PatKind::Binding { mode: BindingMode(ByRef::Yes(_, rm), _), ty, .. } => {
315                if self.inside_adt {
316                    let ty::Ref(_, ty, _) = ty.kind() else {
317                        ::rustc_middle::util::bug::span_bug_fmt(pat.span,
    format_args!("ByRef::Yes in pattern, but found non-reference type {0}",
        ty));span_bug!(
318                            pat.span,
319                            "ByRef::Yes in pattern, but found non-reference type {}",
320                            ty
321                        );
322                    };
323                    match rm {
324                        Mutability::Not => {
325                            if !ty.is_freeze(self.tcx, self.typing_env) {
326                                self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
327                            }
328                        }
329                        Mutability::Mut { .. } => {
330                            self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
331                        }
332                    }
333                }
334                visit::walk_pat(self, pat);
335            }
336            PatKind::Deref { .. } | PatKind::DerefPattern { .. } => {
337                let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
338                visit::walk_pat(self, pat);
339                self.inside_adt = old_inside_adt;
340            }
341            _ => {
342                visit::walk_pat(self, pat);
343            }
344        }
345    }
346
347    fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
348        // could we be in the LHS of an assignment to a field?
349        match expr.kind {
350            ExprKind::Field { .. }
351            | ExprKind::VarRef { .. }
352            | ExprKind::UpvarRef { .. }
353            | ExprKind::Scope { .. }
354            | ExprKind::Cast { .. } => {}
355
356            ExprKind::RawBorrow { .. }
357            | ExprKind::Adt { .. }
358            | ExprKind::Array { .. }
359            | ExprKind::Binary { .. }
360            | ExprKind::Block { .. }
361            | ExprKind::Borrow { .. }
362            | ExprKind::Literal { .. }
363            | ExprKind::NamedConst { .. }
364            | ExprKind::NonHirLiteral { .. }
365            | ExprKind::ZstLiteral { .. }
366            | ExprKind::ConstParam { .. }
367            | ExprKind::ConstBlock { .. }
368            | ExprKind::Deref { .. }
369            | ExprKind::Index { .. }
370            | ExprKind::NeverToAny { .. }
371            | ExprKind::PlaceTypeAscription { .. }
372            | ExprKind::ValueTypeAscription { .. }
373            | ExprKind::PlaceUnwrapUnsafeBinder { .. }
374            | ExprKind::ValueUnwrapUnsafeBinder { .. }
375            | ExprKind::WrapUnsafeBinder { .. }
376            | ExprKind::PointerCoercion { .. }
377            | ExprKind::Repeat { .. }
378            | ExprKind::StaticRef { .. }
379            | ExprKind::ThreadLocalRef { .. }
380            | ExprKind::Tuple { .. }
381            | ExprKind::Unary { .. }
382            | ExprKind::Call { .. }
383            | ExprKind::ByUse { .. }
384            | ExprKind::Assign { .. }
385            | ExprKind::AssignOp { .. }
386            | ExprKind::Break { .. }
387            | ExprKind::Closure { .. }
388            | ExprKind::Continue { .. }
389            | ExprKind::ConstContinue { .. }
390            | ExprKind::Return { .. }
391            | ExprKind::Become { .. }
392            | ExprKind::Yield { .. }
393            | ExprKind::Loop { .. }
394            | ExprKind::LoopMatch { .. }
395            | ExprKind::Let { .. }
396            | ExprKind::Match { .. }
397            | ExprKind::If { .. }
398            | ExprKind::InlineAsm { .. }
399            | ExprKind::LogicalOp { .. }
400            | ExprKind::Use { .. } => {
401                // We don't need to save the old value and restore it
402                // because all the place expressions can't have more
403                // than one child.
404                self.assignment_info = None;
405            }
406        };
407        match expr.kind {
408            ExprKind::Scope { value, hir_id, region_scope: _ } => {
409                let prev_id = self.hir_context;
410                self.hir_context = hir_id;
411                ensure_sufficient_stack(|| {
412                    self.visit_expr(&self.thir[value]);
413                });
414                self.hir_context = prev_id;
415                return; // don't visit the whole expression
416            }
417            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
418                let fn_ty = self.thir[fun].ty;
419                let sig = fn_ty.fn_sig(self.tcx);
420                let (callee_features, safe_target_features): (&[_], _) = match *fn_ty.kind() {
421                    ty::FnDef(func_id, ..) => {
422                        let cg_attrs = self.tcx.codegen_fn_attrs(func_id);
423                        (&cg_attrs.target_features, cg_attrs.safe_target_features)
424                    }
425                    _ => (&[], false),
426                };
427                if sig.safety().is_unsafe() && !safe_target_features {
428                    let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() {
429                        Some(*func_id)
430                    } else {
431                        None
432                    };
433                    self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
434                } else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
435                    if !self
436                        .tcx
437                        .is_target_feature_call_safe(callee_features, self.body_target_features)
438                    {
439                        let missing: Vec<_> = callee_features
440                            .iter()
441                            .copied()
442                            .filter(|feature| {
443                                feature.kind == TargetFeatureKind::Enabled
444                                    && !self
445                                        .body_target_features
446                                        .iter()
447                                        .any(|body_feature| body_feature.name == feature.name)
448                            })
449                            .map(|feature| feature.name)
450                            .collect();
451                        let build_enabled = self
452                            .tcx
453                            .sess
454                            .target_features
455                            .iter()
456                            .copied()
457                            .filter(|feature| missing.contains(feature))
458                            .collect();
459                        self.requires_unsafe(
460                            expr.span,
461                            CallToFunctionWith { function: func_did, missing, build_enabled },
462                        );
463                    }
464                }
465            }
466            ExprKind::RawBorrow { arg, .. } => {
467                if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
468                    && let ExprKind::Deref { arg } = self.thir[arg].kind
469                {
470                    // Taking a raw ref to a deref place expr is always safe.
471                    // Make sure the expression we're deref'ing is safe, though.
472                    visit::walk_expr(self, &self.thir[arg]);
473                    return;
474                }
475
476                // Secondly, we allow raw borrows of union field accesses. Peel
477                // any of those off, and recurse normally on the LHS, which should
478                // reject any unsafe operations within.
479                let mut peeled = arg;
480                while let ExprKind::Scope { value: arg, .. } = self.thir[peeled].kind
481                    && let ExprKind::Field { lhs, name: _, variant_index: _ } = self.thir[arg].kind
482                    && let ty::Adt(def, _) = &self.thir[lhs].ty.kind()
483                    && def.is_union()
484                {
485                    peeled = lhs;
486                }
487                visit::walk_expr(self, &self.thir[peeled]);
488                // And return so we don't recurse directly onto the union field access(es).
489                return;
490            }
491            ExprKind::Deref { arg } => {
492                if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
493                    self.thir[arg].kind
494                {
495                    if self.tcx.is_mutable_static(def_id) {
496                        self.requires_unsafe(expr.span, UseOfMutableStatic);
497                    } else if self.tcx.is_foreign_item(def_id) {
498                        match self.tcx.def_kind(def_id) {
499                            DefKind::Static { safety: hir::Safety::Safe, .. } => {}
500                            _ => self.requires_unsafe(expr.span, UseOfExternStatic),
501                        }
502                    }
503                } else if self.thir[arg].ty.is_raw_ptr() {
504                    self.requires_unsafe(expr.span, DerefOfRawPointer);
505                }
506            }
507            ExprKind::InlineAsm(box InlineAsmExpr {
508                asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
509                ref operands,
510                template: _,
511                options: _,
512                line_spans: _,
513            }) => {
514                // The `naked` attribute and the `naked_asm!` block form one atomic unit of
515                // unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block.
516                if let AsmMacro::Asm = asm_macro {
517                    self.requires_unsafe(expr.span, UseOfInlineAssembly);
518                }
519
520                // For inline asm, do not use `walk_expr`, since we want to handle the label block
521                // specially.
522                for op in &**operands {
523                    use rustc_middle::thir::InlineAsmOperand::*;
524                    match op {
525                        In { expr, reg: _ }
526                        | Out { expr: Some(expr), reg: _, late: _ }
527                        | InOut { expr, reg: _, late: _ } => self.visit_expr(&self.thir()[*expr]),
528                        SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
529                            self.visit_expr(&self.thir()[*in_expr]);
530                            if let Some(out_expr) = out_expr {
531                                self.visit_expr(&self.thir()[*out_expr]);
532                            }
533                        }
534                        Out { expr: None, reg: _, late: _ }
535                        | Const { value: _, span: _ }
536                        | SymFn { value: _ }
537                        | SymStatic { def_id: _ } => {}
538                        Label { block } => {
539                            // Label blocks are safe context.
540                            // `asm!()` is forced to be wrapped inside unsafe. If there's no special
541                            // treatment, the label blocks would also always be unsafe with no way
542                            // of opting out.
543                            self.in_safety_context(SafetyContext::Safe, |this| {
544                                visit::walk_block(this, &this.thir()[*block])
545                            });
546                        }
547                    }
548                }
549                return;
550            }
551            ExprKind::Adt(box AdtExpr {
552                adt_def,
553                variant_index,
554                args: _,
555                user_ty: _,
556                fields: _,
557                base: _,
558            }) => {
559                if adt_def.variant(variant_index).has_unsafe_fields() {
560                    self.requires_unsafe(expr.span, InitializingTypeWithUnsafeField)
561                }
562            }
563            ExprKind::Closure(box ClosureExpr {
564                closure_id,
565                args: _,
566                upvars: _,
567                movability: _,
568                fake_reads: _,
569            }) => {
570                self.visit_inner_body(closure_id);
571            }
572            ExprKind::ConstBlock { did, args: _ } => {
573                let def_id = did.expect_local();
574                self.visit_inner_body(def_id);
575            }
576            ExprKind::Field { lhs, variant_index, name } => {
577                let lhs = &self.thir[lhs];
578                if let ty::Adt(adt_def, _) = lhs.ty.kind() {
579                    if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
580                        self.requires_unsafe(expr.span, UseOfUnsafeField);
581                    } else if adt_def.is_union() {
582                        if let Some(assigned_ty) = self.assignment_info {
583                            if assigned_ty.needs_drop(self.tcx, self.typing_env) {
584                                // This would be unsafe, but should be outright impossible since we
585                                // reject such unions.
586                                if !self.tcx.dcx().has_errors().is_some() {
    {
        ::core::panicking::panic_fmt(format_args!("union fields that need dropping should be impossible: {0}",
                assigned_ty));
    }
};assert!(
587                                    self.tcx.dcx().has_errors().is_some(),
588                                    "union fields that need dropping should be impossible: {assigned_ty}"
589                                );
590                            }
591                        } else {
592                            self.requires_unsafe(expr.span, AccessToUnionField);
593                        }
594                    }
595                }
596            }
597            ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
598                let lhs = &self.thir[lhs];
599
600                // Check for accesses to union fields. Don't have any
601                // special handling for AssignOp since it causes a read *and*
602                // write to lhs.
603                if #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
    ExprKind::Assign { .. } => true,
    _ => false,
}matches!(expr.kind, ExprKind::Assign { .. }) {
604                    self.assignment_info = Some(lhs.ty);
605                    visit::walk_expr(self, lhs);
606                    self.assignment_info = None;
607                    visit::walk_expr(self, &self.thir()[rhs]);
608                    return; // We have already visited everything by now.
609                }
610            }
611            ExprKind::PlaceUnwrapUnsafeBinder { .. }
612            | ExprKind::ValueUnwrapUnsafeBinder { .. }
613            | ExprKind::WrapUnsafeBinder { .. } => {
614                self.requires_unsafe(expr.span, UnsafeBinderCast);
615            }
616            _ => {}
617        }
618        visit::walk_expr(self, expr);
619    }
620}
621
622#[derive(#[automatically_derived]
impl ::core::clone::Clone for SafetyContext {
    #[inline]
    fn clone(&self) -> SafetyContext {
        match self {
            SafetyContext::Safe => SafetyContext::Safe,
            SafetyContext::BuiltinUnsafeBlock =>
                SafetyContext::BuiltinUnsafeBlock,
            SafetyContext::UnsafeFn => SafetyContext::UnsafeFn,
            SafetyContext::UnsafeBlock {
                span: __self_0,
                hir_id: __self_1,
                used: __self_2,
                nested_used_blocks: __self_3 } =>
                SafetyContext::UnsafeBlock {
                    span: ::core::clone::Clone::clone(__self_0),
                    hir_id: ::core::clone::Clone::clone(__self_1),
                    used: ::core::clone::Clone::clone(__self_2),
                    nested_used_blocks: ::core::clone::Clone::clone(__self_3),
                },
        }
    }
}Clone)]
623enum SafetyContext {
624    Safe,
625    BuiltinUnsafeBlock,
626    UnsafeFn,
627    UnsafeBlock { span: Span, hir_id: HirId, used: bool, nested_used_blocks: Vec<NestedUsedBlock> },
628}
629
630#[derive(#[automatically_derived]
impl ::core::clone::Clone for NestedUsedBlock {
    #[inline]
    fn clone(&self) -> NestedUsedBlock {
        let _: ::core::clone::AssertParamIsClone<HirId>;
        let _: ::core::clone::AssertParamIsClone<Span>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NestedUsedBlock { }Copy)]
631struct NestedUsedBlock {
632    hir_id: HirId,
633    span: Span,
634}
635
636struct UnusedUnsafeWarning {
637    hir_id: HirId,
638    block_span: Span,
639    enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
640}
641
642#[derive(#[automatically_derived]
impl ::core::clone::Clone for UnsafeOpKind {
    #[inline]
    fn clone(&self) -> UnsafeOpKind {
        match self {
            UnsafeOpKind::CallToUnsafeFunction(__self_0) =>
                UnsafeOpKind::CallToUnsafeFunction(::core::clone::Clone::clone(__self_0)),
            UnsafeOpKind::UseOfInlineAssembly =>
                UnsafeOpKind::UseOfInlineAssembly,
            UnsafeOpKind::InitializingTypeWithUnsafeField =>
                UnsafeOpKind::InitializingTypeWithUnsafeField,
            UnsafeOpKind::UseOfMutableStatic =>
                UnsafeOpKind::UseOfMutableStatic,
            UnsafeOpKind::UseOfExternStatic =>
                UnsafeOpKind::UseOfExternStatic,
            UnsafeOpKind::UseOfUnsafeField => UnsafeOpKind::UseOfUnsafeField,
            UnsafeOpKind::DerefOfRawPointer =>
                UnsafeOpKind::DerefOfRawPointer,
            UnsafeOpKind::AccessToUnionField =>
                UnsafeOpKind::AccessToUnionField,
            UnsafeOpKind::MutationOfLayoutConstrainedField =>
                UnsafeOpKind::MutationOfLayoutConstrainedField,
            UnsafeOpKind::BorrowOfLayoutConstrainedField =>
                UnsafeOpKind::BorrowOfLayoutConstrainedField,
            UnsafeOpKind::CallToFunctionWith {
                function: __self_0, missing: __self_1, build_enabled: __self_2
                } =>
                UnsafeOpKind::CallToFunctionWith {
                    function: ::core::clone::Clone::clone(__self_0),
                    missing: ::core::clone::Clone::clone(__self_1),
                    build_enabled: ::core::clone::Clone::clone(__self_2),
                },
            UnsafeOpKind::UnsafeBinderCast => UnsafeOpKind::UnsafeBinderCast,
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for UnsafeOpKind {
    #[inline]
    fn eq(&self, other: &UnsafeOpKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (UnsafeOpKind::CallToUnsafeFunction(__self_0),
                    UnsafeOpKind::CallToUnsafeFunction(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (UnsafeOpKind::CallToFunctionWith {
                    function: __self_0,
                    missing: __self_1,
                    build_enabled: __self_2 },
                    UnsafeOpKind::CallToFunctionWith {
                    function: __arg1_0,
                    missing: __arg1_1,
                    build_enabled: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                _ => true,
            }
    }
}PartialEq)]
643enum UnsafeOpKind {
644    CallToUnsafeFunction(Option<DefId>),
645    UseOfInlineAssembly,
646    InitializingTypeWithUnsafeField,
647    UseOfMutableStatic,
648    UseOfExternStatic,
649    UseOfUnsafeField,
650    DerefOfRawPointer,
651    AccessToUnionField,
652    MutationOfLayoutConstrainedField,
653    BorrowOfLayoutConstrainedField,
654    CallToFunctionWith {
655        function: DefId,
656        /// Target features enabled in callee's `#[target_feature]` but missing in
657        /// caller's `#[target_feature]`.
658        missing: Vec<Symbol>,
659        /// Target features in `missing` that are enabled at compile time
660        /// (e.g., with `-C target-feature`).
661        build_enabled: Vec<Symbol>,
662    },
663    UnsafeBinderCast,
664}
665
666use UnsafeOpKind::*;
667
668impl UnsafeOpKind {
669    fn emit_unsafe_op_in_unsafe_fn_lint(
670        &self,
671        tcx: TyCtxt<'_>,
672        hir_id: HirId,
673        span: Span,
674        suggest_unsafe_block: bool,
675    ) {
676        if tcx.hir_opt_delegation_sig_id(hir_id.owner.def_id).is_some() {
677            // The body of the delegation item is synthesized, so it makes no sense
678            // to emit this lint.
679            return;
680        }
681        let parent_id = tcx.hir_get_parent_item(hir_id);
682        let parent_owner = tcx.hir_owner_node(parent_id);
683        let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
684            // Do not suggest for safe target_feature functions
685            #[allow(non_exhaustive_omitted_patterns)] match sig.header.safety {
    hir::HeaderSafety::Normal(hir::Safety::Unsafe) => true,
    _ => false,
}matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
686        });
687        let unsafe_not_inherited_note = if should_suggest {
688            suggest_unsafe_block.then(|| {
689                let body_span = tcx.hir_body(parent_owner.body_id().unwrap()).value.span;
690                UnsafeNotInheritedLintNote {
691                    signature_span: tcx.def_span(parent_id.def_id),
692                    body_span,
693                }
694            })
695        } else {
696            None
697        };
698        // FIXME: ideally we would want to trim the def paths, but this is not
699        // feasible with the current lint emission API (see issue #106126).
700        match self {
701            CallToUnsafeFunction(Some(did)) => tcx.emit_node_span_lint(
702                UNSAFE_OP_IN_UNSAFE_FN,
703                hir_id,
704                span,
705                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
706                    span,
707                    function: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(*did) }with_no_trimmed_paths!(tcx.def_path_str(*did)),
708                    unsafe_not_inherited_note,
709                },
710            ),
711            CallToUnsafeFunction(None) => tcx.emit_node_span_lint(
712                UNSAFE_OP_IN_UNSAFE_FN,
713                hir_id,
714                span,
715                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
716                    span,
717                    unsafe_not_inherited_note,
718                },
719            ),
720            UseOfInlineAssembly => tcx.emit_node_span_lint(
721                UNSAFE_OP_IN_UNSAFE_FN,
722                hir_id,
723                span,
724                UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
725                    span,
726                    unsafe_not_inherited_note,
727                },
728            ),
729            InitializingTypeWithUnsafeField => tcx.emit_node_span_lint(
730                UNSAFE_OP_IN_UNSAFE_FN,
731                hir_id,
732                span,
733                UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe {
734                    span,
735                    unsafe_not_inherited_note,
736                },
737            ),
738            UseOfMutableStatic => tcx.emit_node_span_lint(
739                UNSAFE_OP_IN_UNSAFE_FN,
740                hir_id,
741                span,
742                UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
743                    span,
744                    unsafe_not_inherited_note,
745                },
746            ),
747            UseOfExternStatic => tcx.emit_node_span_lint(
748                UNSAFE_OP_IN_UNSAFE_FN,
749                hir_id,
750                span,
751                UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
752                    span,
753                    unsafe_not_inherited_note,
754                },
755            ),
756            UseOfUnsafeField => tcx.emit_node_span_lint(
757                UNSAFE_OP_IN_UNSAFE_FN,
758                hir_id,
759                span,
760                UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe {
761                    span,
762                    unsafe_not_inherited_note,
763                },
764            ),
765            DerefOfRawPointer => tcx.emit_node_span_lint(
766                UNSAFE_OP_IN_UNSAFE_FN,
767                hir_id,
768                span,
769                UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
770                    span,
771                    unsafe_not_inherited_note,
772                },
773            ),
774            AccessToUnionField => tcx.emit_node_span_lint(
775                UNSAFE_OP_IN_UNSAFE_FN,
776                hir_id,
777                span,
778                UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
779                    span,
780                    unsafe_not_inherited_note,
781                },
782            ),
783            MutationOfLayoutConstrainedField => tcx.emit_node_span_lint(
784                UNSAFE_OP_IN_UNSAFE_FN,
785                hir_id,
786                span,
787                UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
788                    span,
789                    unsafe_not_inherited_note,
790                },
791            ),
792            BorrowOfLayoutConstrainedField => tcx.emit_node_span_lint(
793                UNSAFE_OP_IN_UNSAFE_FN,
794                hir_id,
795                span,
796                UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
797                    span,
798                    unsafe_not_inherited_note,
799                },
800            ),
801            CallToFunctionWith { function, missing, build_enabled } => tcx.emit_node_span_lint(
802                UNSAFE_OP_IN_UNSAFE_FN,
803                hir_id,
804                span,
805                UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
806                    span,
807                    function: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(*function) }with_no_trimmed_paths!(tcx.def_path_str(*function)),
808                    missing_target_features: DiagArgValue::StrListSepByAnd(
809                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
810                    ),
811                    missing_target_features_count: missing.len(),
812                    note: !build_enabled.is_empty(),
813                    build_target_features: DiagArgValue::StrListSepByAnd(
814                        build_enabled
815                            .iter()
816                            .map(|feature| Cow::from(feature.to_string()))
817                            .collect(),
818                    ),
819                    build_target_features_count: build_enabled.len(),
820                    unsafe_not_inherited_note,
821                },
822            ),
823            UnsafeBinderCast => tcx.emit_node_span_lint(
824                UNSAFE_OP_IN_UNSAFE_FN,
825                hir_id,
826                span,
827                UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe {
828                    span,
829                    unsafe_not_inherited_note,
830                },
831            ),
832        }
833    }
834
835    fn emit_requires_unsafe_err(
836        &self,
837        tcx: TyCtxt<'_>,
838        span: Span,
839        hir_context: HirId,
840        unsafe_op_in_unsafe_fn_allowed: bool,
841    ) {
842        let note_non_inherited = tcx.hir_parent_iter(hir_context).find(|(id, node)| {
843            if let hir::Node::Expr(block) = node
844                && let hir::ExprKind::Block(block, _) = block.kind
845                && let hir::BlockCheckMode::UnsafeBlock(_) = block.rules
846            {
847                true
848            } else if let Some(sig) = tcx.hir_fn_sig_by_hir_id(*id)
849                && #[allow(non_exhaustive_omitted_patterns)] match sig.header.safety {
    hir::HeaderSafety::Normal(hir::Safety::Unsafe) => true,
    _ => false,
}matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
850            {
851                true
852            } else {
853                false
854            }
855        });
856        let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited {
857            let span = tcx.hir_span(id);
858            let span = tcx.sess.source_map().guess_head_span(span);
859            Some(UnsafeNotInheritedNote { span })
860        } else {
861            None
862        };
863
864        let dcx = tcx.dcx();
865        match self {
866            CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
867                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
868                    span,
869                    unsafe_not_inherited_note,
870                    function: tcx.def_path_str(*did),
871                });
872            }
873            CallToUnsafeFunction(Some(did)) => {
874                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafe {
875                    span,
876                    unsafe_not_inherited_note,
877                    function: tcx.def_path_str(*did),
878                });
879            }
880            CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
881                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed {
882                    span,
883                    unsafe_not_inherited_note,
884                });
885            }
886            CallToUnsafeFunction(None) => {
887                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless {
888                    span,
889                    unsafe_not_inherited_note,
890                });
891            }
892            UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
893                dcx.emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
894                    span,
895                    unsafe_not_inherited_note,
896                });
897            }
898            UseOfInlineAssembly => {
899                dcx.emit_err(UseOfInlineAssemblyRequiresUnsafe { span, unsafe_not_inherited_note });
900            }
901            InitializingTypeWithUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
902                dcx.emit_err(
903                    InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
904                        span,
905                        unsafe_not_inherited_note,
906                    },
907                );
908            }
909            InitializingTypeWithUnsafeField => {
910                dcx.emit_err(InitializingTypeWithUnsafeFieldRequiresUnsafe {
911                    span,
912                    unsafe_not_inherited_note,
913                });
914            }
915            UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
916                dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
917                    span,
918                    unsafe_not_inherited_note,
919                });
920            }
921            UseOfMutableStatic => {
922                dcx.emit_err(UseOfMutableStaticRequiresUnsafe { span, unsafe_not_inherited_note });
923            }
924            UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
925                dcx.emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
926                    span,
927                    unsafe_not_inherited_note,
928                });
929            }
930            UseOfExternStatic => {
931                dcx.emit_err(UseOfExternStaticRequiresUnsafe { span, unsafe_not_inherited_note });
932            }
933            UseOfUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
934                dcx.emit_err(UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
935                    span,
936                    unsafe_not_inherited_note,
937                });
938            }
939            UseOfUnsafeField => {
940                dcx.emit_err(UseOfUnsafeFieldRequiresUnsafe { span, unsafe_not_inherited_note });
941            }
942            DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
943                dcx.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
944                    span,
945                    unsafe_not_inherited_note,
946                });
947            }
948            DerefOfRawPointer => {
949                dcx.emit_err(DerefOfRawPointerRequiresUnsafe { span, unsafe_not_inherited_note });
950            }
951            AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
952                dcx.emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
953                    span,
954                    unsafe_not_inherited_note,
955                });
956            }
957            AccessToUnionField => {
958                dcx.emit_err(AccessToUnionFieldRequiresUnsafe { span, unsafe_not_inherited_note });
959            }
960            MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
961                dcx.emit_err(
962                    MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
963                        span,
964                        unsafe_not_inherited_note,
965                    },
966                );
967            }
968            MutationOfLayoutConstrainedField => {
969                dcx.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe {
970                    span,
971                    unsafe_not_inherited_note,
972                });
973            }
974            BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
975                dcx.emit_err(
976                    BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
977                        span,
978                        unsafe_not_inherited_note,
979                    },
980                );
981            }
982            BorrowOfLayoutConstrainedField => {
983                dcx.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe {
984                    span,
985                    unsafe_not_inherited_note,
986                });
987            }
988            CallToFunctionWith { function, missing, build_enabled }
989                if unsafe_op_in_unsafe_fn_allowed =>
990            {
991                dcx.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
992                    span,
993                    missing_target_features: DiagArgValue::StrListSepByAnd(
994                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
995                    ),
996                    missing_target_features_count: missing.len(),
997                    note: !build_enabled.is_empty(),
998                    build_target_features: DiagArgValue::StrListSepByAnd(
999                        build_enabled
1000                            .iter()
1001                            .map(|feature| Cow::from(feature.to_string()))
1002                            .collect(),
1003                    ),
1004                    build_target_features_count: build_enabled.len(),
1005                    unsafe_not_inherited_note,
1006                    function: tcx.def_path_str(*function),
1007                });
1008            }
1009            CallToFunctionWith { function, missing, build_enabled } => {
1010                dcx.emit_err(CallToFunctionWithRequiresUnsafe {
1011                    span,
1012                    missing_target_features: DiagArgValue::StrListSepByAnd(
1013                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
1014                    ),
1015                    missing_target_features_count: missing.len(),
1016                    note: !build_enabled.is_empty(),
1017                    build_target_features: DiagArgValue::StrListSepByAnd(
1018                        build_enabled
1019                            .iter()
1020                            .map(|feature| Cow::from(feature.to_string()))
1021                            .collect(),
1022                    ),
1023                    build_target_features_count: build_enabled.len(),
1024                    unsafe_not_inherited_note,
1025                    function: tcx.def_path_str(*function),
1026                });
1027            }
1028            UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => {
1029                dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1030                    span,
1031                    unsafe_not_inherited_note,
1032                });
1033            }
1034            UnsafeBinderCast => {
1035                dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
1036            }
1037        }
1038    }
1039}
1040
1041pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
1042    // Closures and inline consts are handled by their owner, if it has a body
1043    if !!tcx.is_typeck_child(def.to_def_id()) {
    ::core::panicking::panic("assertion failed: !tcx.is_typeck_child(def.to_def_id())")
};assert!(!tcx.is_typeck_child(def.to_def_id()));
1044    // Also, don't safety check custom MIR
1045    if {
    {
        'done:
            {
            for i in ::rustc_hir::attrs::HasAttrs::get_attrs(def, &tcx) {
                #[allow(unused_imports)]
                use rustc_hir::attrs::AttributeKind::*;
                let i: &rustc_hir::Attribute = i;
                match i {
                    rustc_hir::Attribute::Parsed(CustomMir(..)) => {
                        break 'done Some(());
                    }
                    rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(tcx, def, CustomMir(..) => ()).is_some() {
1046        return;
1047    }
1048
1049    let Ok((thir, expr)) = tcx.thir_body(def) else { return };
1050    // Runs all other queries that depend on THIR.
1051    tcx.ensure_done().mir_built(def);
1052    let thir = if tcx.sess.opts.unstable_opts.no_steal_thir {
1053        &thir.borrow()
1054    } else {
1055        // We don't have other use for the THIR. Steal it to reduce memory usage.
1056        &thir.steal()
1057    };
1058
1059    let hir_id = tcx.local_def_id_to_hir_id(def);
1060    let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
1061        match fn_sig.header.safety {
1062            // We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
1063            // Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
1064            // to care about safety of the body.
1065            hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
1066            hir::HeaderSafety::Normal(safety) => match safety {
1067                hir::Safety::Unsafe => SafetyContext::UnsafeFn,
1068                hir::Safety::Safe => SafetyContext::Safe,
1069            },
1070        }
1071    });
1072    let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
1073    let mut warnings = Vec::new();
1074    let mut visitor = UnsafetyVisitor {
1075        tcx,
1076        thir,
1077        safety_context,
1078        hir_context: hir_id,
1079        body_target_features,
1080        assignment_info: None,
1081        in_union_destructure: false,
1082        // FIXME(#132279): we're clearly in a body here.
1083        typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
1084        inside_adt: false,
1085        warnings: &mut warnings,
1086        suggest_unsafe_block: true,
1087    };
1088    // params in THIR may be unsafe, e.g. a union pattern.
1089    for param in &thir.params {
1090        if let Some(param_pat) = param.pat.as_deref() {
1091            visitor.visit_pat(param_pat);
1092        }
1093    }
1094    // Visit the body.
1095    visitor.visit_expr(&thir[expr]);
1096
1097    warnings.sort_by_key(|w| w.block_span);
1098    for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
1099        let block_span = tcx.sess.source_map().guess_head_span(block_span);
1100        tcx.emit_node_span_lint(
1101            UNUSED_UNSAFE,
1102            hir_id,
1103            block_span,
1104            UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
1105        );
1106    }
1107}