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