• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::build::ExprCategory;
2 use crate::errors::*;
3 use rustc_middle::thir::visit::{self, Visitor};
4 
5 use rustc_hir as hir;
6 use rustc_middle::mir::BorrowKind;
7 use rustc_middle::thir::*;
8 use rustc_middle::ty::print::with_no_trimmed_paths;
9 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
10 use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
11 use rustc_session::lint::Level;
12 use rustc_span::def_id::{DefId, LocalDefId};
13 use rustc_span::symbol::Symbol;
14 use rustc_span::Span;
15 
16 use std::ops::Bound;
17 
18 struct UnsafetyVisitor<'a, 'tcx> {
19     tcx: TyCtxt<'tcx>,
20     thir: &'a Thir<'tcx>,
21     /// The `HirId` of the current scope, which would be the `HirId`
22     /// of the current HIR node, modulo adjustments. Used for lint levels.
23     hir_context: hir::HirId,
24     /// The current "safety context". This notably tracks whether we are in an
25     /// `unsafe` block, and whether it has been used.
26     safety_context: SafetyContext,
27     body_unsafety: BodyUnsafety,
28     /// The `#[target_feature]` attributes of the body. Used for checking
29     /// calls to functions with `#[target_feature]` (RFC 2396).
30     body_target_features: &'tcx [Symbol],
31     /// When inside the LHS of an assignment to a field, this is the type
32     /// of the LHS and the span of the assignment expression.
33     assignment_info: Option<(Ty<'tcx>, Span)>,
34     in_union_destructure: bool,
35     param_env: ParamEnv<'tcx>,
36     inside_adt: bool,
37 }
38 
39 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self))40     fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
41         if let (
42             SafetyContext::UnsafeBlock { span: enclosing_span, .. },
43             SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
44         ) = (self.safety_context, safety_context)
45         {
46             self.warn_unused_unsafe(
47                 hir_id,
48                 block_span,
49                 Some(UnusedUnsafeEnclosing::Block {
50                     span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
51                 }),
52             );
53             f(self);
54         } else {
55             let prev_context = self.safety_context;
56             self.safety_context = safety_context;
57 
58             f(self);
59 
60             if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
61                 self.warn_unused_unsafe(
62                     hir_id,
63                     span,
64                     if self.unsafe_op_in_unsafe_fn_allowed() {
65                         self.body_unsafety
66                             .unsafe_fn_sig_span()
67                             .map(|span| UnusedUnsafeEnclosing::Function { span })
68                     } else {
69                         None
70                     },
71                 );
72             }
73             self.safety_context = prev_context;
74         }
75     }
76 
requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind)77     fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
78         let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
79         match self.safety_context {
80             SafetyContext::BuiltinUnsafeBlock => {}
81             SafetyContext::UnsafeBlock { ref mut used, .. } => {
82                 // Mark this block as useful (even inside `unsafe fn`, where it is technically
83                 // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
84                 // default which will require those blocks:
85                 // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
86                 *used = true;
87             }
88             SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
89             SafetyContext::UnsafeFn => {
90                 // unsafe_op_in_unsafe_fn is disallowed
91                 kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
92             }
93             SafetyContext::Safe => {
94                 kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
95             }
96         }
97     }
98 
warn_unused_unsafe( &self, hir_id: hir::HirId, block_span: Span, enclosing_unsafe: Option<UnusedUnsafeEnclosing>, )99     fn warn_unused_unsafe(
100         &self,
101         hir_id: hir::HirId,
102         block_span: Span,
103         enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
104     ) {
105         let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
106         self.tcx.emit_spanned_lint(
107             UNUSED_UNSAFE,
108             hir_id,
109             block_span,
110             UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
111         );
112     }
113 
114     /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
unsafe_op_in_unsafe_fn_allowed(&self) -> bool115     fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
116         self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
117     }
118 
119     /// Handle closures/generators/inline-consts, which is unsafecked with their parent body.
visit_inner_body(&mut self, def: LocalDefId)120     fn visit_inner_body(&mut self, def: LocalDefId) {
121         if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
122             let inner_thir = &inner_thir.borrow();
123             let hir_context = self.tcx.hir().local_def_id_to_hir_id(def);
124             let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
125             inner_visitor.visit_expr(&inner_thir[expr]);
126             // Unsafe blocks can be used in the inner body, make sure to take it into account
127             self.safety_context = inner_visitor.safety_context;
128         }
129     }
130 }
131 
132 // Searches for accesses to layout constrained fields.
133 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
134     found: bool,
135     thir: &'a Thir<'tcx>,
136     tcx: TyCtxt<'tcx>,
137 }
138 
139 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self140     fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
141         Self { found: false, thir, tcx }
142     }
143 }
144 
145 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
thir(&self) -> &'a Thir<'tcx>146     fn thir(&self) -> &'a Thir<'tcx> {
147         self.thir
148     }
149 
visit_expr(&mut self, expr: &Expr<'tcx>)150     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
151         match expr.kind {
152             ExprKind::Field { lhs, .. } => {
153                 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
154                     if (Bound::Unbounded, Bound::Unbounded)
155                         != self.tcx.layout_scalar_valid_range(adt_def.did())
156                     {
157                         self.found = true;
158                     }
159                 }
160                 visit::walk_expr(self, expr);
161             }
162 
163             // Keep walking through the expression as long as we stay in the same
164             // place, i.e. the expression is a place expression and not a dereference
165             // (since dereferencing something leads us to a different place).
166             ExprKind::Deref { .. } => {}
167             ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
168                 visit::walk_expr(self, expr);
169             }
170 
171             _ => {}
172         }
173     }
174 }
175 
176 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
thir(&self) -> &'a Thir<'tcx>177     fn thir(&self) -> &'a Thir<'tcx> {
178         &self.thir
179     }
180 
visit_block(&mut self, block: &Block)181     fn visit_block(&mut self, block: &Block) {
182         match block.safety_mode {
183             // compiler-generated unsafe code should not count towards the usefulness of
184             // an outer unsafe block
185             BlockSafety::BuiltinUnsafe => {
186                 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
187                     visit::walk_block(this, block)
188                 });
189             }
190             BlockSafety::ExplicitUnsafe(hir_id) => {
191                 self.in_safety_context(
192                     SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
193                     |this| visit::walk_block(this, block),
194                 );
195             }
196             BlockSafety::Safe => {
197                 visit::walk_block(self, block);
198             }
199         }
200     }
201 
visit_pat(&mut self, pat: &Pat<'tcx>)202     fn visit_pat(&mut self, pat: &Pat<'tcx>) {
203         if self.in_union_destructure {
204             match pat.kind {
205                 // binding to a variable allows getting stuff out of variable
206                 PatKind::Binding { .. }
207                 // match is conditional on having this value
208                 | PatKind::Constant { .. }
209                 | PatKind::Variant { .. }
210                 | PatKind::Leaf { .. }
211                 | PatKind::Deref { .. }
212                 | PatKind::Range { .. }
213                 | PatKind::Slice { .. }
214                 | PatKind::Array { .. } => {
215                     self.requires_unsafe(pat.span, AccessToUnionField);
216                     return; // we can return here since this already requires unsafe
217                 }
218                 // wildcard doesn't take anything
219                 PatKind::Wild |
220                 // these just wrap other patterns
221                 PatKind::Or { .. } |
222                 PatKind::AscribeUserType { .. } => {}
223             }
224         };
225 
226         match &pat.kind {
227             PatKind::Leaf { .. } => {
228                 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
229                     if adt_def.is_union() {
230                         let old_in_union_destructure =
231                             std::mem::replace(&mut self.in_union_destructure, true);
232                         visit::walk_pat(self, pat);
233                         self.in_union_destructure = old_in_union_destructure;
234                     } else if (Bound::Unbounded, Bound::Unbounded)
235                         != self.tcx.layout_scalar_valid_range(adt_def.did())
236                     {
237                         let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
238                         visit::walk_pat(self, pat);
239                         self.inside_adt = old_inside_adt;
240                     } else {
241                         visit::walk_pat(self, pat);
242                     }
243                 } else {
244                     visit::walk_pat(self, pat);
245                 }
246             }
247             PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
248                 if self.inside_adt {
249                     let ty::Ref(_, ty, _) = ty.kind() else {
250                         span_bug!(
251                             pat.span,
252                             "BindingMode::ByRef in pattern, but found non-reference type {}",
253                             ty
254                         );
255                     };
256                     match borrow_kind {
257                         BorrowKind::Shallow | BorrowKind::Shared => {
258                             if !ty.is_freeze(self.tcx, self.param_env) {
259                                 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
260                             }
261                         }
262                         BorrowKind::Mut { .. } => {
263                             self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
264                         }
265                     }
266                 }
267                 visit::walk_pat(self, pat);
268             }
269             PatKind::Deref { .. } => {
270                 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
271                 visit::walk_pat(self, pat);
272                 self.inside_adt = old_inside_adt;
273             }
274             _ => {
275                 visit::walk_pat(self, pat);
276             }
277         }
278     }
279 
visit_expr(&mut self, expr: &Expr<'tcx>)280     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
281         // could we be in the LHS of an assignment to a field?
282         match expr.kind {
283             ExprKind::Field { .. }
284             | ExprKind::VarRef { .. }
285             | ExprKind::UpvarRef { .. }
286             | ExprKind::Scope { .. }
287             | ExprKind::Cast { .. } => {}
288 
289             ExprKind::AddressOf { .. }
290             | ExprKind::Adt { .. }
291             | ExprKind::Array { .. }
292             | ExprKind::Binary { .. }
293             | ExprKind::Block { .. }
294             | ExprKind::Borrow { .. }
295             | ExprKind::Literal { .. }
296             | ExprKind::NamedConst { .. }
297             | ExprKind::NonHirLiteral { .. }
298             | ExprKind::ZstLiteral { .. }
299             | ExprKind::ConstParam { .. }
300             | ExprKind::ConstBlock { .. }
301             | ExprKind::Deref { .. }
302             | ExprKind::Index { .. }
303             | ExprKind::NeverToAny { .. }
304             | ExprKind::PlaceTypeAscription { .. }
305             | ExprKind::ValueTypeAscription { .. }
306             | ExprKind::PointerCoercion { .. }
307             | ExprKind::Repeat { .. }
308             | ExprKind::StaticRef { .. }
309             | ExprKind::ThreadLocalRef { .. }
310             | ExprKind::Tuple { .. }
311             | ExprKind::Unary { .. }
312             | ExprKind::Call { .. }
313             | ExprKind::Assign { .. }
314             | ExprKind::AssignOp { .. }
315             | ExprKind::Break { .. }
316             | ExprKind::Closure { .. }
317             | ExprKind::Continue { .. }
318             | ExprKind::Return { .. }
319             | ExprKind::Become { .. }
320             | ExprKind::Yield { .. }
321             | ExprKind::Loop { .. }
322             | ExprKind::Let { .. }
323             | ExprKind::Match { .. }
324             | ExprKind::Box { .. }
325             | ExprKind::If { .. }
326             | ExprKind::InlineAsm { .. }
327             | ExprKind::OffsetOf { .. }
328             | ExprKind::LogicalOp { .. }
329             | ExprKind::Use { .. } => {
330                 // We don't need to save the old value and restore it
331                 // because all the place expressions can't have more
332                 // than one child.
333                 self.assignment_info = None;
334             }
335         };
336         match expr.kind {
337             ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
338                 let prev_id = self.hir_context;
339                 self.hir_context = hir_id;
340                 self.visit_expr(&self.thir[value]);
341                 self.hir_context = prev_id;
342                 return; // don't visit the whole expression
343             }
344             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
345                 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
346                     let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
347                         Some(*func_id)
348                     } else {
349                         None
350                     };
351                     self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
352                 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
353                     // If the called function has target features the calling function hasn't,
354                     // the call requires `unsafe`. Don't check this on wasm
355                     // targets, though. For more information on wasm see the
356                     // is_like_wasm check in hir_analysis/src/collect.rs
357                     if !self.tcx.sess.target.options.is_like_wasm
358                         && !self
359                             .tcx
360                             .codegen_fn_attrs(func_did)
361                             .target_features
362                             .iter()
363                             .all(|feature| self.body_target_features.contains(feature))
364                     {
365                         self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
366                     }
367                 }
368             }
369             ExprKind::Deref { arg } => {
370                 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
371                     if self.tcx.is_mutable_static(def_id) {
372                         self.requires_unsafe(expr.span, UseOfMutableStatic);
373                     } else if self.tcx.is_foreign_item(def_id) {
374                         self.requires_unsafe(expr.span, UseOfExternStatic);
375                     }
376                 } else if self.thir[arg].ty.is_unsafe_ptr() {
377                     self.requires_unsafe(expr.span, DerefOfRawPointer);
378                 }
379             }
380             ExprKind::InlineAsm { .. } => {
381                 self.requires_unsafe(expr.span, UseOfInlineAssembly);
382             }
383             ExprKind::Adt(box AdtExpr {
384                 adt_def,
385                 variant_index: _,
386                 substs: _,
387                 user_ty: _,
388                 fields: _,
389                 base: _,
390             }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
391                 (Bound::Unbounded, Bound::Unbounded) => {}
392                 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
393             },
394             ExprKind::Closure(box ClosureExpr {
395                 closure_id,
396                 substs: _,
397                 upvars: _,
398                 movability: _,
399                 fake_reads: _,
400             }) => {
401                 self.visit_inner_body(closure_id);
402             }
403             ExprKind::ConstBlock { did, substs: _ } => {
404                 let def_id = did.expect_local();
405                 self.visit_inner_body(def_id);
406             }
407             ExprKind::Field { lhs, .. } => {
408                 let lhs = &self.thir[lhs];
409                 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
410                     if let Some((assigned_ty, assignment_span)) = self.assignment_info {
411                         if assigned_ty.needs_drop(self.tcx, self.param_env) {
412                             // This would be unsafe, but should be outright impossible since we reject such unions.
413                             self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
414                         }
415                     } else {
416                         self.requires_unsafe(expr.span, AccessToUnionField);
417                     }
418                 }
419             }
420             ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
421                 let lhs = &self.thir[lhs];
422                 // First, check whether we are mutating a layout constrained field
423                 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
424                 visit::walk_expr(&mut visitor, lhs);
425                 if visitor.found {
426                     self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
427                 }
428 
429                 // Second, check for accesses to union fields
430                 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
431                 if matches!(expr.kind, ExprKind::Assign { .. }) {
432                     self.assignment_info = Some((lhs.ty, expr.span));
433                     visit::walk_expr(self, lhs);
434                     self.assignment_info = None;
435                     visit::walk_expr(self, &self.thir()[rhs]);
436                     return; // we have already visited everything by now
437                 }
438             }
439             ExprKind::Borrow { borrow_kind, arg } => {
440                 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
441                 visit::walk_expr(&mut visitor, expr);
442                 if visitor.found {
443                     match borrow_kind {
444                         BorrowKind::Shallow | BorrowKind::Shared
445                             if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
446                         {
447                             self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
448                         }
449                         BorrowKind::Mut { .. } => {
450                             self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
451                         }
452                         BorrowKind::Shallow | BorrowKind::Shared => {}
453                     }
454                 }
455             }
456             ExprKind::Let { expr: expr_id, .. } => {
457                 let let_expr = &self.thir[expr_id];
458                 if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
459                     self.requires_unsafe(expr.span, AccessToUnionField);
460                 }
461             }
462             _ => {}
463         }
464         visit::walk_expr(self, expr);
465     }
466 }
467 
468 #[derive(Clone, Copy)]
469 enum SafetyContext {
470     Safe,
471     BuiltinUnsafeBlock,
472     UnsafeFn,
473     UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
474 }
475 
476 #[derive(Clone, Copy)]
477 enum BodyUnsafety {
478     /// The body is not unsafe.
479     Safe,
480     /// The body is an unsafe function. The span points to
481     /// the signature of the function.
482     Unsafe(Span),
483 }
484 
485 impl BodyUnsafety {
486     /// Returns whether the body is unsafe.
is_unsafe(&self) -> bool487     fn is_unsafe(&self) -> bool {
488         matches!(self, BodyUnsafety::Unsafe(_))
489     }
490 
491     /// If the body is unsafe, returns the `Span` of its signature.
unsafe_fn_sig_span(self) -> Option<Span>492     fn unsafe_fn_sig_span(self) -> Option<Span> {
493         match self {
494             BodyUnsafety::Unsafe(span) => Some(span),
495             BodyUnsafety::Safe => None,
496         }
497     }
498 }
499 
500 #[derive(Clone, Copy, PartialEq)]
501 enum UnsafeOpKind {
502     CallToUnsafeFunction(Option<DefId>),
503     UseOfInlineAssembly,
504     InitializingTypeWith,
505     UseOfMutableStatic,
506     UseOfExternStatic,
507     DerefOfRawPointer,
508     AccessToUnionField,
509     MutationOfLayoutConstrainedField,
510     BorrowOfLayoutConstrainedField,
511     CallToFunctionWith(DefId),
512 }
513 
514 use UnsafeOpKind::*;
515 
516 impl UnsafeOpKind {
emit_unsafe_op_in_unsafe_fn_lint( &self, tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, )517     pub fn emit_unsafe_op_in_unsafe_fn_lint(
518         &self,
519         tcx: TyCtxt<'_>,
520         hir_id: hir::HirId,
521         span: Span,
522     ) {
523         // FIXME: ideally we would want to trim the def paths, but this is not
524         // feasible with the current lint emission API (see issue #106126).
525         match self {
526             CallToUnsafeFunction(Some(did)) => tcx.emit_spanned_lint(
527                 UNSAFE_OP_IN_UNSAFE_FN,
528                 hir_id,
529                 span,
530                 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
531                     span,
532                     function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
533                 },
534             ),
535             CallToUnsafeFunction(None) => tcx.emit_spanned_lint(
536                 UNSAFE_OP_IN_UNSAFE_FN,
537                 hir_id,
538                 span,
539                 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
540             ),
541             UseOfInlineAssembly => tcx.emit_spanned_lint(
542                 UNSAFE_OP_IN_UNSAFE_FN,
543                 hir_id,
544                 span,
545                 UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
546             ),
547             InitializingTypeWith => tcx.emit_spanned_lint(
548                 UNSAFE_OP_IN_UNSAFE_FN,
549                 hir_id,
550                 span,
551                 UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
552             ),
553             UseOfMutableStatic => tcx.emit_spanned_lint(
554                 UNSAFE_OP_IN_UNSAFE_FN,
555                 hir_id,
556                 span,
557                 UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
558             ),
559             UseOfExternStatic => tcx.emit_spanned_lint(
560                 UNSAFE_OP_IN_UNSAFE_FN,
561                 hir_id,
562                 span,
563                 UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
564             ),
565             DerefOfRawPointer => tcx.emit_spanned_lint(
566                 UNSAFE_OP_IN_UNSAFE_FN,
567                 hir_id,
568                 span,
569                 UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
570             ),
571             AccessToUnionField => tcx.emit_spanned_lint(
572                 UNSAFE_OP_IN_UNSAFE_FN,
573                 hir_id,
574                 span,
575                 UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
576             ),
577             MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
578                 UNSAFE_OP_IN_UNSAFE_FN,
579                 hir_id,
580                 span,
581                 UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
582             ),
583             BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
584                 UNSAFE_OP_IN_UNSAFE_FN,
585                 hir_id,
586                 span,
587                 UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
588             ),
589             CallToFunctionWith(did) => tcx.emit_spanned_lint(
590                 UNSAFE_OP_IN_UNSAFE_FN,
591                 hir_id,
592                 span,
593                 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
594                     span,
595                     function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
596                 },
597             ),
598         }
599     }
600 
emit_requires_unsafe_err( &self, tcx: TyCtxt<'_>, span: Span, unsafe_op_in_unsafe_fn_allowed: bool, )601     pub fn emit_requires_unsafe_err(
602         &self,
603         tcx: TyCtxt<'_>,
604         span: Span,
605         unsafe_op_in_unsafe_fn_allowed: bool,
606     ) {
607         match self {
608             CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
609                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
610                     span,
611                     function: &tcx.def_path_str(*did),
612                 });
613             }
614             CallToUnsafeFunction(Some(did)) => {
615                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
616                     span,
617                     function: &tcx.def_path_str(*did),
618                 });
619             }
620             CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
621                 tcx.sess.emit_err(
622                     CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
623                 );
624             }
625             CallToUnsafeFunction(None) => {
626                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
627             }
628             UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
629                 tcx.sess
630                     .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
631             }
632             UseOfInlineAssembly => {
633                 tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
634             }
635             InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
636                 tcx.sess
637                     .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
638             }
639             InitializingTypeWith => {
640                 tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
641             }
642             UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
643                 tcx.sess
644                     .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
645             }
646             UseOfMutableStatic => {
647                 tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
648             }
649             UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
650                 tcx.sess
651                     .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
652             }
653             UseOfExternStatic => {
654                 tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
655             }
656             DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
657                 tcx.sess
658                     .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
659             }
660             DerefOfRawPointer => {
661                 tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
662             }
663             AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
664                 tcx.sess
665                     .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
666             }
667             AccessToUnionField => {
668                 tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
669             }
670             MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
671                 tcx.sess.emit_err(
672                     MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
673                         span,
674                     },
675                 );
676             }
677             MutationOfLayoutConstrainedField => {
678                 tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
679             }
680             BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
681                 tcx.sess.emit_err(
682                     BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
683                 );
684             }
685             BorrowOfLayoutConstrainedField => {
686                 tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
687             }
688             CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
689                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
690                     span,
691                     function: &tcx.def_path_str(*did),
692                 });
693             }
694             CallToFunctionWith(did) => {
695                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
696                     span,
697                     function: &tcx.def_path_str(*did),
698                 });
699             }
700         }
701     }
702 }
703 
thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId)704 pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
705     // THIR unsafeck is gated under `-Z thir-unsafeck`
706     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
707         return;
708     }
709 
710     // Closures and inline consts are handled by their owner, if it has a body
711     if tcx.is_typeck_child(def.to_def_id()) {
712         return;
713     }
714 
715     let Ok((thir, expr)) = tcx.thir_body(def) else {
716         return
717     };
718     let thir = &thir.borrow();
719     // If `thir` is empty, a type error occurred, skip this body.
720     if thir.exprs.is_empty() {
721         return;
722     }
723 
724     let hir_id = tcx.hir().local_def_id_to_hir_id(def);
725     let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
726         if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
727             BodyUnsafety::Unsafe(fn_sig.span)
728         } else {
729             BodyUnsafety::Safe
730         }
731     });
732     let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
733     let safety_context =
734         if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
735     let mut visitor = UnsafetyVisitor {
736         tcx,
737         thir,
738         safety_context,
739         hir_context: hir_id,
740         body_unsafety,
741         body_target_features,
742         assignment_info: None,
743         in_union_destructure: false,
744         param_env: tcx.param_env(def),
745         inside_adt: false,
746     };
747     visitor.visit_expr(&thir[expr]);
748 }
749