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