rustc_mir_build/
check_unsafety.rs

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