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