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 = &inner_thir.steal();
207 let hir_context = self.tcx.local_def_id_to_hir_id(def);
208 let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
209 let mut inner_visitor = UnsafetyVisitor {
210 tcx: self.tcx,
211 thir: inner_thir,
212 hir_context,
213 safety_context,
214 body_target_features: self.body_target_features,
215 assignment_info: self.assignment_info,
216 in_union_destructure: false,
217 typing_env: self.typing_env,
218 inside_adt: false,
219 warnings: self.warnings,
220 suggest_unsafe_block: self.suggest_unsafe_block,
221 };
222 for param in &inner_thir.params {
224 if let Some(param_pat) = param.pat.as_deref() {
225 inner_visitor.visit_pat(param_pat);
226 }
227 }
228 inner_visitor.visit_expr(&inner_thir[expr]);
230 self.safety_context = inner_visitor.safety_context;
232 }
233 }
234}
235
236struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
238 found: bool,
239 thir: &'a Thir<'tcx>,
240 tcx: TyCtxt<'tcx>,
241}
242
243impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
244 fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
245 Self { found: false, thir, tcx }
246 }
247}
248
249impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
250 fn thir(&self) -> &'a Thir<'tcx> {
251 self.thir
252 }
253
254 fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
255 match expr.kind {
256 ExprKind::Field { lhs, .. } => {
257 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
258 if (Bound::Unbounded, Bound::Unbounded)
259 != self.tcx.layout_scalar_valid_range(adt_def.did())
260 {
261 self.found = true;
262 }
263 }
264 visit::walk_expr(self, expr);
265 }
266
267 ExprKind::Deref { .. } => {}
271 ref kind if ExprCategory::of(kind).is_none_or(|cat| cat == ExprCategory::Place) => {
272 visit::walk_expr(self, expr);
273 }
274
275 _ => {}
276 }
277 }
278}
279
280impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
281 fn thir(&self) -> &'a Thir<'tcx> {
282 self.thir
283 }
284
285 fn visit_block(&mut self, block: &'a Block) {
286 match block.safety_mode {
287 BlockSafety::BuiltinUnsafe => {
290 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
291 visit::walk_block(this, block)
292 });
293 }
294 BlockSafety::ExplicitUnsafe(hir_id) => {
295 let used = matches!(
296 self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
297 Level::Allow
298 );
299 self.in_safety_context(
300 SafetyContext::UnsafeBlock {
301 span: block.span,
302 hir_id,
303 used,
304 nested_used_blocks: Vec::new(),
305 },
306 |this| visit::walk_block(this, block),
307 );
308 }
309 BlockSafety::Safe => {
310 visit::walk_block(self, block);
311 }
312 }
313 }
314
315 fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
316 if self.in_union_destructure {
317 match pat.kind {
318 PatKind::Missing => unreachable!(),
319 PatKind::Binding { .. }
321 | PatKind::Constant { .. }
323 | PatKind::Variant { .. }
324 | PatKind::Leaf { .. }
325 | PatKind::Deref { .. }
326 | PatKind::DerefPattern { .. }
327 | PatKind::Range { .. }
328 | PatKind::Slice { .. }
329 | PatKind::Array { .. }
330 | PatKind::Never => {
332 self.requires_unsafe(pat.span, AccessToUnionField);
333 return; }
335 PatKind::Wild |
337 PatKind::Or { .. } |
339 PatKind::ExpandedConstant { .. } |
340 PatKind::AscribeUserType { .. } |
341 PatKind::Error(_) => {}
342 }
343 };
344
345 match &pat.kind {
346 PatKind::Leaf { subpatterns, .. } => {
347 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
348 for pat in subpatterns {
349 if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() {
350 self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
351 }
352 }
353 if adt_def.is_union() {
354 let old_in_union_destructure =
355 std::mem::replace(&mut self.in_union_destructure, true);
356 visit::walk_pat(self, pat);
357 self.in_union_destructure = old_in_union_destructure;
358 } else if (Bound::Unbounded, Bound::Unbounded)
359 != self.tcx.layout_scalar_valid_range(adt_def.did())
360 {
361 let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
362 visit::walk_pat(self, pat);
363 self.inside_adt = old_inside_adt;
364 } else {
365 visit::walk_pat(self, pat);
366 }
367 } else {
368 visit::walk_pat(self, pat);
369 }
370 }
371 PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
372 for pat in subpatterns {
373 let field = &pat.field;
374 if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() {
375 self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
376 }
377 }
378 visit::walk_pat(self, pat);
379 }
380 PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => {
381 if self.inside_adt {
382 let ty::Ref(_, ty, _) = ty.kind() else {
383 span_bug!(
384 pat.span,
385 "ByRef::Yes in pattern, but found non-reference type {}",
386 ty
387 );
388 };
389 match rm {
390 Mutability::Not => {
391 if !ty.is_freeze(self.tcx, self.typing_env) {
392 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
393 }
394 }
395 Mutability::Mut { .. } => {
396 self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
397 }
398 }
399 }
400 visit::walk_pat(self, pat);
401 }
402 PatKind::Deref { .. } | PatKind::DerefPattern { .. } => {
403 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
404 visit::walk_pat(self, pat);
405 self.inside_adt = old_inside_adt;
406 }
407 PatKind::ExpandedConstant { def_id, .. } => {
408 if let Some(def) = def_id.as_local()
409 && matches!(self.tcx.def_kind(def_id), DefKind::InlineConst)
410 {
411 self.visit_inner_body(def);
412 }
413 visit::walk_pat(self, pat);
414 }
415 _ => {
416 visit::walk_pat(self, pat);
417 }
418 }
419 }
420
421 fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
422 match expr.kind {
424 ExprKind::Field { .. }
425 | ExprKind::VarRef { .. }
426 | ExprKind::UpvarRef { .. }
427 | ExprKind::Scope { .. }
428 | ExprKind::Cast { .. } => {}
429
430 ExprKind::RawBorrow { .. }
431 | ExprKind::Adt { .. }
432 | ExprKind::Array { .. }
433 | ExprKind::Binary { .. }
434 | ExprKind::Block { .. }
435 | ExprKind::Borrow { .. }
436 | ExprKind::Literal { .. }
437 | ExprKind::NamedConst { .. }
438 | ExprKind::NonHirLiteral { .. }
439 | ExprKind::ZstLiteral { .. }
440 | ExprKind::ConstParam { .. }
441 | ExprKind::ConstBlock { .. }
442 | ExprKind::Deref { .. }
443 | ExprKind::Index { .. }
444 | ExprKind::NeverToAny { .. }
445 | ExprKind::PlaceTypeAscription { .. }
446 | ExprKind::ValueTypeAscription { .. }
447 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
448 | ExprKind::ValueUnwrapUnsafeBinder { .. }
449 | ExprKind::WrapUnsafeBinder { .. }
450 | ExprKind::PointerCoercion { .. }
451 | ExprKind::Repeat { .. }
452 | ExprKind::StaticRef { .. }
453 | ExprKind::ThreadLocalRef { .. }
454 | ExprKind::Tuple { .. }
455 | ExprKind::Unary { .. }
456 | ExprKind::Call { .. }
457 | ExprKind::ByUse { .. }
458 | ExprKind::Assign { .. }
459 | ExprKind::AssignOp { .. }
460 | ExprKind::Break { .. }
461 | ExprKind::Closure { .. }
462 | ExprKind::Continue { .. }
463 | ExprKind::Return { .. }
464 | ExprKind::Become { .. }
465 | ExprKind::Yield { .. }
466 | ExprKind::Loop { .. }
467 | ExprKind::Let { .. }
468 | ExprKind::Match { .. }
469 | ExprKind::Box { .. }
470 | ExprKind::If { .. }
471 | ExprKind::InlineAsm { .. }
472 | ExprKind::OffsetOf { .. }
473 | ExprKind::LogicalOp { .. }
474 | ExprKind::Use { .. } => {
475 self.assignment_info = None;
479 }
480 };
481 match expr.kind {
482 ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
483 let prev_id = self.hir_context;
484 self.hir_context = hir_id;
485 ensure_sufficient_stack(|| {
486 self.visit_expr(&self.thir[value]);
487 });
488 self.hir_context = prev_id;
489 return; }
491 ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
492 let fn_ty = self.thir[fun].ty;
493 let sig = fn_ty.fn_sig(self.tcx);
494 let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() {
495 ty::FnDef(func_id, ..) => {
496 let cg_attrs = self.tcx.codegen_fn_attrs(func_id);
497 (&cg_attrs.target_features, cg_attrs.safe_target_features)
498 }
499 _ => (&[], false),
500 };
501 if sig.safety().is_unsafe() && !safe_target_features {
502 let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() {
503 Some(*func_id)
504 } else {
505 None
506 };
507 self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
508 } else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
509 if !self
510 .tcx
511 .is_target_feature_call_safe(callee_features, self.body_target_features)
512 {
513 let missing: Vec<_> = callee_features
514 .iter()
515 .copied()
516 .filter(|feature| {
517 !feature.implied
518 && !self
519 .body_target_features
520 .iter()
521 .any(|body_feature| body_feature.name == feature.name)
522 })
523 .map(|feature| feature.name)
524 .collect();
525 let build_enabled = self
526 .tcx
527 .sess
528 .target_features
529 .iter()
530 .copied()
531 .filter(|feature| missing.contains(feature))
532 .collect();
533 self.requires_unsafe(
534 expr.span,
535 CallToFunctionWith { function: func_did, missing, build_enabled },
536 );
537 }
538 }
539 }
540 ExprKind::RawBorrow { arg, .. } => {
541 if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
542 && let ExprKind::Deref { arg } = self.thir[arg].kind
543 {
544 visit::walk_expr(self, &self.thir[arg]);
547 return;
548 }
549 }
550 ExprKind::Deref { arg } => {
551 if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
552 self.thir[arg].kind
553 {
554 if self.tcx.is_mutable_static(def_id) {
555 self.requires_unsafe(expr.span, UseOfMutableStatic);
556 } else if self.tcx.is_foreign_item(def_id) {
557 match self.tcx.def_kind(def_id) {
558 DefKind::Static { safety: hir::Safety::Safe, .. } => {}
559 _ => self.requires_unsafe(expr.span, UseOfExternStatic),
560 }
561 }
562 } else if self.thir[arg].ty.is_raw_ptr() {
563 self.requires_unsafe(expr.span, DerefOfRawPointer);
564 }
565 }
566 ExprKind::InlineAsm(box InlineAsmExpr {
567 asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
568 ref operands,
569 template: _,
570 options: _,
571 line_spans: _,
572 }) => {
573 if let AsmMacro::Asm = asm_macro {
576 self.requires_unsafe(expr.span, UseOfInlineAssembly);
577 }
578
579 for op in &**operands {
582 use rustc_middle::thir::InlineAsmOperand::*;
583 match op {
584 In { expr, reg: _ }
585 | Out { expr: Some(expr), reg: _, late: _ }
586 | InOut { expr, reg: _, late: _ } => self.visit_expr(&self.thir()[*expr]),
587 SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
588 self.visit_expr(&self.thir()[*in_expr]);
589 if let Some(out_expr) = out_expr {
590 self.visit_expr(&self.thir()[*out_expr]);
591 }
592 }
593 Out { expr: None, reg: _, late: _ }
594 | Const { value: _, span: _ }
595 | SymFn { value: _ }
596 | SymStatic { def_id: _ } => {}
597 Label { block } => {
598 self.in_safety_context(SafetyContext::Safe, |this| {
603 visit::walk_block(this, &this.thir()[*block])
604 });
605 }
606 }
607 }
608 return;
609 }
610 ExprKind::Adt(box AdtExpr {
611 adt_def,
612 variant_index,
613 args: _,
614 user_ty: _,
615 fields: _,
616 base: _,
617 }) => {
618 if adt_def.variant(variant_index).has_unsafe_fields() {
619 self.requires_unsafe(expr.span, InitializingTypeWithUnsafeField)
620 }
621 match self.tcx.layout_scalar_valid_range(adt_def.did()) {
622 (Bound::Unbounded, Bound::Unbounded) => {}
623 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
624 }
625 }
626 ExprKind::Closure(box ClosureExpr {
627 closure_id,
628 args: _,
629 upvars: _,
630 movability: _,
631 fake_reads: _,
632 }) => {
633 self.visit_inner_body(closure_id);
634 }
635 ExprKind::ConstBlock { did, args: _ } => {
636 let def_id = did.expect_local();
637 self.visit_inner_body(def_id);
638 }
639 ExprKind::Field { lhs, variant_index, name } => {
640 let lhs = &self.thir[lhs];
641 if let ty::Adt(adt_def, _) = lhs.ty.kind() {
642 if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
643 self.requires_unsafe(expr.span, UseOfUnsafeField);
644 } else if adt_def.is_union() {
645 if let Some(assigned_ty) = self.assignment_info {
646 if assigned_ty.needs_drop(self.tcx, self.typing_env) {
647 assert!(
650 self.tcx.dcx().has_errors().is_some(),
651 "union fields that need dropping should be impossible: {assigned_ty}"
652 );
653 }
654 } else {
655 self.requires_unsafe(expr.span, AccessToUnionField);
656 }
657 }
658 }
659 }
660 ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
661 let lhs = &self.thir[lhs];
662 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
664 visit::walk_expr(&mut visitor, lhs);
665 if visitor.found {
666 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
667 }
668
669 if matches!(expr.kind, ExprKind::Assign { .. }) {
673 self.assignment_info = Some(lhs.ty);
674 visit::walk_expr(self, lhs);
675 self.assignment_info = None;
676 visit::walk_expr(self, &self.thir()[rhs]);
677 return; }
679 }
680 ExprKind::Borrow { borrow_kind, arg } => {
681 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
682 visit::walk_expr(&mut visitor, expr);
683 if visitor.found {
684 match borrow_kind {
685 BorrowKind::Fake(_) | BorrowKind::Shared
686 if !self.thir[arg].ty.is_freeze(self.tcx, self.typing_env) =>
687 {
688 self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
689 }
690 BorrowKind::Mut { .. } => {
691 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
692 }
693 BorrowKind::Fake(_) | BorrowKind::Shared => {}
694 }
695 }
696 }
697 ExprKind::PlaceUnwrapUnsafeBinder { .. }
698 | ExprKind::ValueUnwrapUnsafeBinder { .. }
699 | ExprKind::WrapUnsafeBinder { .. } => {
700 self.requires_unsafe(expr.span, UnsafeBinderCast);
701 }
702 _ => {}
703 }
704 visit::walk_expr(self, expr);
705 }
706}
707
708#[derive(Clone)]
709enum SafetyContext {
710 Safe,
711 BuiltinUnsafeBlock,
712 UnsafeFn,
713 UnsafeBlock { span: Span, hir_id: HirId, used: bool, nested_used_blocks: Vec<NestedUsedBlock> },
714}
715
716#[derive(Clone, Copy)]
717struct NestedUsedBlock {
718 hir_id: HirId,
719 span: Span,
720}
721
722struct UnusedUnsafeWarning {
723 hir_id: HirId,
724 block_span: Span,
725 enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
726}
727
728#[derive(Clone, PartialEq)]
729enum UnsafeOpKind {
730 CallToUnsafeFunction(Option<DefId>),
731 UseOfInlineAssembly,
732 InitializingTypeWith,
733 InitializingTypeWithUnsafeField,
734 UseOfMutableStatic,
735 UseOfExternStatic,
736 UseOfUnsafeField,
737 DerefOfRawPointer,
738 AccessToUnionField,
739 MutationOfLayoutConstrainedField,
740 BorrowOfLayoutConstrainedField,
741 CallToFunctionWith {
742 function: DefId,
743 missing: Vec<Symbol>,
746 build_enabled: Vec<Symbol>,
749 },
750 UnsafeBinderCast,
751}
752
753use UnsafeOpKind::*;
754
755impl UnsafeOpKind {
756 fn emit_unsafe_op_in_unsafe_fn_lint(
757 &self,
758 tcx: TyCtxt<'_>,
759 hir_id: HirId,
760 span: Span,
761 suggest_unsafe_block: bool,
762 ) {
763 if tcx.hir_opt_delegation_sig_id(hir_id.owner.def_id).is_some() {
764 return;
767 }
768 let parent_id = tcx.hir_get_parent_item(hir_id);
769 let parent_owner = tcx.hir_owner_node(parent_id);
770 let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
771 matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
773 });
774 let unsafe_not_inherited_note = if should_suggest {
775 suggest_unsafe_block.then(|| {
776 let body_span = tcx.hir_body(parent_owner.body_id().unwrap()).value.span;
777 UnsafeNotInheritedLintNote {
778 signature_span: tcx.def_span(parent_id.def_id),
779 body_span,
780 }
781 })
782 } else {
783 None
784 };
785 match self {
788 CallToUnsafeFunction(Some(did)) => tcx.emit_node_span_lint(
789 UNSAFE_OP_IN_UNSAFE_FN,
790 hir_id,
791 span,
792 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
793 span,
794 function: with_no_trimmed_paths!(tcx.def_path_str(*did)),
795 unsafe_not_inherited_note,
796 },
797 ),
798 CallToUnsafeFunction(None) => tcx.emit_node_span_lint(
799 UNSAFE_OP_IN_UNSAFE_FN,
800 hir_id,
801 span,
802 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
803 span,
804 unsafe_not_inherited_note,
805 },
806 ),
807 UseOfInlineAssembly => tcx.emit_node_span_lint(
808 UNSAFE_OP_IN_UNSAFE_FN,
809 hir_id,
810 span,
811 UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
812 span,
813 unsafe_not_inherited_note,
814 },
815 ),
816 InitializingTypeWith => tcx.emit_node_span_lint(
817 UNSAFE_OP_IN_UNSAFE_FN,
818 hir_id,
819 span,
820 UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
821 span,
822 unsafe_not_inherited_note,
823 },
824 ),
825 InitializingTypeWithUnsafeField => tcx.emit_node_span_lint(
826 UNSAFE_OP_IN_UNSAFE_FN,
827 hir_id,
828 span,
829 UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe {
830 span,
831 unsafe_not_inherited_note,
832 },
833 ),
834 UseOfMutableStatic => tcx.emit_node_span_lint(
835 UNSAFE_OP_IN_UNSAFE_FN,
836 hir_id,
837 span,
838 UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
839 span,
840 unsafe_not_inherited_note,
841 },
842 ),
843 UseOfExternStatic => tcx.emit_node_span_lint(
844 UNSAFE_OP_IN_UNSAFE_FN,
845 hir_id,
846 span,
847 UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
848 span,
849 unsafe_not_inherited_note,
850 },
851 ),
852 UseOfUnsafeField => tcx.emit_node_span_lint(
853 UNSAFE_OP_IN_UNSAFE_FN,
854 hir_id,
855 span,
856 UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe {
857 span,
858 unsafe_not_inherited_note,
859 },
860 ),
861 DerefOfRawPointer => tcx.emit_node_span_lint(
862 UNSAFE_OP_IN_UNSAFE_FN,
863 hir_id,
864 span,
865 UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
866 span,
867 unsafe_not_inherited_note,
868 },
869 ),
870 AccessToUnionField => tcx.emit_node_span_lint(
871 UNSAFE_OP_IN_UNSAFE_FN,
872 hir_id,
873 span,
874 UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
875 span,
876 unsafe_not_inherited_note,
877 },
878 ),
879 MutationOfLayoutConstrainedField => tcx.emit_node_span_lint(
880 UNSAFE_OP_IN_UNSAFE_FN,
881 hir_id,
882 span,
883 UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
884 span,
885 unsafe_not_inherited_note,
886 },
887 ),
888 BorrowOfLayoutConstrainedField => tcx.emit_node_span_lint(
889 UNSAFE_OP_IN_UNSAFE_FN,
890 hir_id,
891 span,
892 UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
893 span,
894 unsafe_not_inherited_note,
895 },
896 ),
897 CallToFunctionWith { function, missing, build_enabled } => tcx.emit_node_span_lint(
898 UNSAFE_OP_IN_UNSAFE_FN,
899 hir_id,
900 span,
901 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
902 span,
903 function: with_no_trimmed_paths!(tcx.def_path_str(*function)),
904 missing_target_features: DiagArgValue::StrListSepByAnd(
905 missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
906 ),
907 missing_target_features_count: missing.len(),
908 note: !build_enabled.is_empty(),
909 build_target_features: DiagArgValue::StrListSepByAnd(
910 build_enabled
911 .iter()
912 .map(|feature| Cow::from(feature.to_string()))
913 .collect(),
914 ),
915 build_target_features_count: build_enabled.len(),
916 unsafe_not_inherited_note,
917 },
918 ),
919 UnsafeBinderCast => tcx.emit_node_span_lint(
920 UNSAFE_OP_IN_UNSAFE_FN,
921 hir_id,
922 span,
923 UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe {
924 span,
925 unsafe_not_inherited_note,
926 },
927 ),
928 }
929 }
930
931 fn emit_requires_unsafe_err(
932 &self,
933 tcx: TyCtxt<'_>,
934 span: Span,
935 hir_context: HirId,
936 unsafe_op_in_unsafe_fn_allowed: bool,
937 ) {
938 let note_non_inherited = tcx.hir_parent_iter(hir_context).find(|(id, node)| {
939 if let hir::Node::Expr(block) = node
940 && let hir::ExprKind::Block(block, _) = block.kind
941 && let hir::BlockCheckMode::UnsafeBlock(_) = block.rules
942 {
943 true
944 } else if let Some(sig) = tcx.hir_fn_sig_by_hir_id(*id)
945 && matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
946 {
947 true
948 } else {
949 false
950 }
951 });
952 let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited {
953 let span = tcx.hir_span(id);
954 let span = tcx.sess.source_map().guess_head_span(span);
955 Some(UnsafeNotInheritedNote { span })
956 } else {
957 None
958 };
959
960 let dcx = tcx.dcx();
961 match self {
962 CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
963 dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
964 span,
965 unsafe_not_inherited_note,
966 function: tcx.def_path_str(*did),
967 });
968 }
969 CallToUnsafeFunction(Some(did)) => {
970 dcx.emit_err(CallToUnsafeFunctionRequiresUnsafe {
971 span,
972 unsafe_not_inherited_note,
973 function: tcx.def_path_str(*did),
974 });
975 }
976 CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
977 dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed {
978 span,
979 unsafe_not_inherited_note,
980 });
981 }
982 CallToUnsafeFunction(None) => {
983 dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless {
984 span,
985 unsafe_not_inherited_note,
986 });
987 }
988 UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
989 dcx.emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
990 span,
991 unsafe_not_inherited_note,
992 });
993 }
994 UseOfInlineAssembly => {
995 dcx.emit_err(UseOfInlineAssemblyRequiresUnsafe { span, unsafe_not_inherited_note });
996 }
997 InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
998 dcx.emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
999 span,
1000 unsafe_not_inherited_note,
1001 });
1002 }
1003 InitializingTypeWith => {
1004 dcx.emit_err(InitializingTypeWithRequiresUnsafe {
1005 span,
1006 unsafe_not_inherited_note,
1007 });
1008 }
1009 InitializingTypeWithUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
1010 dcx.emit_err(
1011 InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1012 span,
1013 unsafe_not_inherited_note,
1014 },
1015 );
1016 }
1017 InitializingTypeWithUnsafeField => {
1018 dcx.emit_err(InitializingTypeWithUnsafeFieldRequiresUnsafe {
1019 span,
1020 unsafe_not_inherited_note,
1021 });
1022 }
1023 UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
1024 dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1025 span,
1026 unsafe_not_inherited_note,
1027 });
1028 }
1029 UseOfMutableStatic => {
1030 dcx.emit_err(UseOfMutableStaticRequiresUnsafe { span, unsafe_not_inherited_note });
1031 }
1032 UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
1033 dcx.emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1034 span,
1035 unsafe_not_inherited_note,
1036 });
1037 }
1038 UseOfExternStatic => {
1039 dcx.emit_err(UseOfExternStaticRequiresUnsafe { span, unsafe_not_inherited_note });
1040 }
1041 UseOfUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
1042 dcx.emit_err(UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1043 span,
1044 unsafe_not_inherited_note,
1045 });
1046 }
1047 UseOfUnsafeField => {
1048 dcx.emit_err(UseOfUnsafeFieldRequiresUnsafe { span, unsafe_not_inherited_note });
1049 }
1050 DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
1051 dcx.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1052 span,
1053 unsafe_not_inherited_note,
1054 });
1055 }
1056 DerefOfRawPointer => {
1057 dcx.emit_err(DerefOfRawPointerRequiresUnsafe { span, unsafe_not_inherited_note });
1058 }
1059 AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
1060 dcx.emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1061 span,
1062 unsafe_not_inherited_note,
1063 });
1064 }
1065 AccessToUnionField => {
1066 dcx.emit_err(AccessToUnionFieldRequiresUnsafe { span, unsafe_not_inherited_note });
1067 }
1068 MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
1069 dcx.emit_err(
1070 MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1071 span,
1072 unsafe_not_inherited_note,
1073 },
1074 );
1075 }
1076 MutationOfLayoutConstrainedField => {
1077 dcx.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe {
1078 span,
1079 unsafe_not_inherited_note,
1080 });
1081 }
1082 BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
1083 dcx.emit_err(
1084 BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1085 span,
1086 unsafe_not_inherited_note,
1087 },
1088 );
1089 }
1090 BorrowOfLayoutConstrainedField => {
1091 dcx.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe {
1092 span,
1093 unsafe_not_inherited_note,
1094 });
1095 }
1096 CallToFunctionWith { function, missing, build_enabled }
1097 if unsafe_op_in_unsafe_fn_allowed =>
1098 {
1099 dcx.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1100 span,
1101 missing_target_features: DiagArgValue::StrListSepByAnd(
1102 missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
1103 ),
1104 missing_target_features_count: missing.len(),
1105 note: !build_enabled.is_empty(),
1106 build_target_features: DiagArgValue::StrListSepByAnd(
1107 build_enabled
1108 .iter()
1109 .map(|feature| Cow::from(feature.to_string()))
1110 .collect(),
1111 ),
1112 build_target_features_count: build_enabled.len(),
1113 unsafe_not_inherited_note,
1114 function: tcx.def_path_str(*function),
1115 });
1116 }
1117 CallToFunctionWith { function, missing, build_enabled } => {
1118 dcx.emit_err(CallToFunctionWithRequiresUnsafe {
1119 span,
1120 missing_target_features: DiagArgValue::StrListSepByAnd(
1121 missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
1122 ),
1123 missing_target_features_count: missing.len(),
1124 note: !build_enabled.is_empty(),
1125 build_target_features: DiagArgValue::StrListSepByAnd(
1126 build_enabled
1127 .iter()
1128 .map(|feature| Cow::from(feature.to_string()))
1129 .collect(),
1130 ),
1131 build_target_features_count: build_enabled.len(),
1132 unsafe_not_inherited_note,
1133 function: tcx.def_path_str(*function),
1134 });
1135 }
1136 UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => {
1137 dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1138 span,
1139 unsafe_not_inherited_note,
1140 });
1141 }
1142 UnsafeBinderCast => {
1143 dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
1144 }
1145 }
1146 }
1147}
1148
1149pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
1150 assert!(!tcx.is_typeck_child(def.to_def_id()));
1152 if tcx.has_attr(def, sym::custom_mir) {
1154 return;
1155 }
1156
1157 let Ok((thir, expr)) = tcx.thir_body(def) else { return };
1158 tcx.ensure_done().mir_built(def);
1160 let thir = &thir.steal();
1161
1162 let hir_id = tcx.local_def_id_to_hir_id(def);
1163 let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
1164 match fn_sig.header.safety {
1165 hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
1169 hir::HeaderSafety::Normal(safety) => match safety {
1170 hir::Safety::Unsafe => SafetyContext::UnsafeFn,
1171 hir::Safety::Safe => SafetyContext::Safe,
1172 },
1173 }
1174 });
1175 let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
1176 let mut warnings = Vec::new();
1177 let mut visitor = UnsafetyVisitor {
1178 tcx,
1179 thir,
1180 safety_context,
1181 hir_context: hir_id,
1182 body_target_features,
1183 assignment_info: None,
1184 in_union_destructure: false,
1185 typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
1187 inside_adt: false,
1188 warnings: &mut warnings,
1189 suggest_unsafe_block: true,
1190 };
1191 for param in &thir.params {
1193 if let Some(param_pat) = param.pat.as_deref() {
1194 visitor.visit_pat(param_pat);
1195 }
1196 }
1197 visitor.visit_expr(&thir[expr]);
1199
1200 warnings.sort_by_key(|w| w.block_span);
1201 for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
1202 let block_span = tcx.sess.source_map().guess_head_span(block_span);
1203 tcx.emit_node_span_lint(
1204 UNUSED_UNSAFE,
1205 hir_id,
1206 block_span,
1207 UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
1208 );
1209 }
1210}