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