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