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