rustc_mir_build/
check_unsafety.rs

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