diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /compiler/rustc_mir_build/src/check_unsafety.rs | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip |
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_build/src/check_unsafety.rs')
-rw-r--r-- | compiler/rustc_mir_build/src/check_unsafety.rs | 264 |
1 files changed, 172 insertions, 92 deletions
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 7b888dcbc..fcb563607 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -13,6 +13,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use std::mem; use std::ops::Bound; struct UnsafetyVisitor<'a, 'tcx> { @@ -24,7 +25,6 @@ struct UnsafetyVisitor<'a, 'tcx> { /// The current "safety context". This notably tracks whether we are in an /// `unsafe` block, and whether it has been used. safety_context: SafetyContext, - body_unsafety: BodyUnsafety, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). body_target_features: &'tcx [Symbol], @@ -34,43 +34,54 @@ struct UnsafetyVisitor<'a, 'tcx> { in_union_destructure: bool, param_env: ParamEnv<'tcx>, inside_adt: bool, + warnings: &'a mut Vec<UnusedUnsafeWarning>, + + /// Flag to ensure that we only suggest wrapping the entire function body in + /// an unsafe block once. + suggest_unsafe_block: bool, } impl<'tcx> UnsafetyVisitor<'_, 'tcx> { fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) { - if let ( - SafetyContext::UnsafeBlock { span: enclosing_span, .. }, - SafetyContext::UnsafeBlock { span: block_span, hir_id, .. }, - ) = (self.safety_context, safety_context) - { - self.warn_unused_unsafe( - hir_id, - block_span, - Some(UnusedUnsafeEnclosing::Block { - span: self.tcx.sess.source_map().guess_head_span(enclosing_span), - }), - ); - f(self); - } else { - let prev_context = self.safety_context; - self.safety_context = safety_context; + let prev_context = mem::replace(&mut self.safety_context, safety_context); - f(self); + f(self); - if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context { - self.warn_unused_unsafe( - hir_id, - span, - if self.unsafe_op_in_unsafe_fn_allowed() { - self.body_unsafety - .unsafe_fn_sig_span() - .map(|span| UnusedUnsafeEnclosing::Function { span }) - } else { - None - }, - ); + let safety_context = mem::replace(&mut self.safety_context, prev_context); + if let SafetyContext::UnsafeBlock { used, span, hir_id, nested_used_blocks } = + safety_context + { + if !used { + self.warn_unused_unsafe(hir_id, span, None); + + if let SafetyContext::UnsafeBlock { + nested_used_blocks: ref mut prev_nested_used_blocks, + .. + } = self.safety_context + { + prev_nested_used_blocks.extend(nested_used_blocks); + } + } else { + for block in nested_used_blocks { + self.warn_unused_unsafe( + block.hir_id, + block.span, + Some(UnusedUnsafeEnclosing::Block { + span: self.tcx.sess.source_map().guess_head_span(span), + }), + ); + } + + match self.safety_context { + SafetyContext::UnsafeBlock { + nested_used_blocks: ref mut prev_nested_used_blocks, + .. + } => { + prev_nested_used_blocks.push(NestedUsedBlock { hir_id, span }); + } + _ => (), + } } - self.safety_context = prev_context; } } @@ -88,7 +99,13 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {} SafetyContext::UnsafeFn => { // unsafe_op_in_unsafe_fn is disallowed - kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span); + kind.emit_unsafe_op_in_unsafe_fn_lint( + self.tcx, + self.hir_context, + span, + self.suggest_unsafe_block, + ); + self.suggest_unsafe_block = false; } SafetyContext::Safe => { kind.emit_requires_unsafe_err( @@ -102,18 +119,12 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } fn warn_unused_unsafe( - &self, + &mut self, hir_id: hir::HirId, block_span: Span, enclosing_unsafe: Option<UnusedUnsafeEnclosing>, ) { - let block_span = self.tcx.sess.source_map().guess_head_span(block_span); - self.tcx.emit_spanned_lint( - UNUSED_UNSAFE, - hir_id, - block_span, - UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, - ); + self.warnings.push(UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe }); } /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. @@ -121,12 +132,21 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow } - /// Handle closures/generators/inline-consts, which is unsafecked with their parent body. + /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body. fn visit_inner_body(&mut self, def: LocalDefId) { if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) { - let inner_thir = &inner_thir.borrow(); + // Runs all other queries that depend on THIR. + self.tcx.ensure_with_value().mir_built(def); + let inner_thir = &inner_thir.steal(); let hir_context = self.tcx.hir().local_def_id_to_hir_id(def); - let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self }; + let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe); + let mut inner_visitor = UnsafetyVisitor { + thir: inner_thir, + hir_context, + safety_context, + warnings: self.warnings, + ..*self + }; inner_visitor.visit_expr(&inner_thir[expr]); // Unsafe blocks can be used in the inner body, make sure to take it into account self.safety_context = inner_visitor.safety_context; @@ -193,8 +213,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }); } BlockSafety::ExplicitUnsafe(hir_id) => { + let used = + matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _)); self.in_safety_context( - SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false }, + SafetyContext::UnsafeBlock { + span: block.span, + hir_id, + used, + nested_used_blocks: Vec::new(), + }, |this| visit::walk_block(this, block), ); } @@ -224,7 +251,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Wild | // these just wrap other patterns PatKind::Or { .. } | - PatKind::AscribeUserType { .. } => {} + PatKind::InlineConstant { .. } | + PatKind::AscribeUserType { .. } | + PatKind::Error(_) => {} } }; @@ -276,6 +305,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; } + PatKind::InlineConstant { def, .. } => { + self.visit_inner_body(*def); + visit::walk_pat(self, pat); + } _ => { visit::walk_pat(self, pat); } @@ -372,7 +405,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } ExprKind::Deref { arg } => { - if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind { + if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) = + self.thir[arg].kind + { if self.tcx.is_mutable_static(def_id) { self.requires_unsafe(expr.span, UseOfMutableStatic); } else if self.tcx.is_foreign_item(def_id) { @@ -411,7 +446,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } ExprKind::Field { lhs, .. } => { let lhs = &self.thir[lhs]; - if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() { + if let ty::Adt(adt_def, _) = lhs.ty.kind() + && adt_def.is_union() + { if let Some((assigned_ty, assignment_span)) = self.assignment_info { if assigned_ty.needs_drop(self.tcx, self.param_env) { // This would be unsafe, but should be outright impossible since we reject such unions. @@ -458,48 +495,35 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } } - ExprKind::Let { expr: expr_id, .. } => { - let let_expr = &self.thir[expr_id]; - if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() { - self.requires_unsafe(expr.span, AccessToUnionField); - } - } _ => {} } visit::walk_expr(self, expr); } } -#[derive(Clone, Copy)] +#[derive(Clone)] enum SafetyContext { Safe, BuiltinUnsafeBlock, UnsafeFn, - UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool }, + UnsafeBlock { + span: Span, + hir_id: hir::HirId, + used: bool, + nested_used_blocks: Vec<NestedUsedBlock>, + }, } #[derive(Clone, Copy)] -enum BodyUnsafety { - /// The body is not unsafe. - Safe, - /// The body is an unsafe function. The span points to - /// the signature of the function. - Unsafe(Span), +struct NestedUsedBlock { + hir_id: hir::HirId, + span: Span, } -impl BodyUnsafety { - /// Returns whether the body is unsafe. - fn is_unsafe(&self) -> bool { - matches!(self, BodyUnsafety::Unsafe(_)) - } - - /// If the body is unsafe, returns the `Span` of its signature. - fn unsafe_fn_sig_span(self) -> Option<Span> { - match self { - BodyUnsafety::Unsafe(span) => Some(span), - BodyUnsafety::Safe => None, - } - } +struct UnusedUnsafeWarning { + hir_id: hir::HirId, + block_span: Span, + enclosing_unsafe: Option<UnusedUnsafeEnclosing>, } #[derive(Clone, Copy, PartialEq)] @@ -524,7 +548,22 @@ impl UnsafeOpKind { tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, + suggest_unsafe_block: bool, ) { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_owner = tcx.hir().owner(parent_id); + let should_suggest = parent_owner.fn_sig().map_or(false, |sig| sig.header.is_unsafe()); + let unsafe_not_inherited_note = if should_suggest { + suggest_unsafe_block.then(|| { + let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span; + UnsafeNotInheritedLintNote { + signature_span: tcx.def_span(parent_id.def_id), + body_span, + } + }) + } else { + None + }; // FIXME: ideally we would want to trim the def paths, but this is not // feasible with the current lint emission API (see issue #106126). match self { @@ -535,61 +574,89 @@ impl UnsafeOpKind { UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { span, function: &with_no_trimmed_paths!(tcx.def_path_str(*did)), + unsafe_not_inherited_note, }, ), CallToUnsafeFunction(None) => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span }, + UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { + span, + unsafe_not_inherited_note, + }, ), UseOfInlineAssembly => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span }, + UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), InitializingTypeWith => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span }, + UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), UseOfMutableStatic => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span }, + UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), UseOfExternStatic => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span }, + UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), DerefOfRawPointer => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span }, + UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), AccessToUnionField => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span }, + UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), MutationOfLayoutConstrainedField => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span }, + UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, span, - UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span }, + UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, ), CallToFunctionWith(did) => tcx.emit_spanned_lint( UNSAFE_OP_IN_UNSAFE_FN, @@ -598,6 +665,7 @@ impl UnsafeOpKind { UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { span, function: &with_no_trimmed_paths!(tcx.def_path_str(*did)), + unsafe_not_inherited_note, }, ), } @@ -616,8 +684,7 @@ impl UnsafeOpKind { && let hir::BlockCheckMode::UnsafeBlock(_) = block.rules { true - } - else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) + } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) && sig.header.is_unsafe() { true @@ -784,34 +851,47 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { } let Ok((thir, expr)) = tcx.thir_body(def) else { return }; - let thir = &thir.borrow(); + // Runs all other queries that depend on THIR. + tcx.ensure_with_value().mir_built(def); + let thir = &thir.steal(); // If `thir` is empty, a type error occurred, skip this body. if thir.exprs.is_empty() { return; } let hir_id = tcx.hir().local_def_id_to_hir_id(def); - let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| { + let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { if fn_sig.header.unsafety == hir::Unsafety::Unsafe { - BodyUnsafety::Unsafe(fn_sig.span) + SafetyContext::UnsafeFn } else { - BodyUnsafety::Safe + SafetyContext::Safe } }); let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features; - let safety_context = - if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }; + let mut warnings = Vec::new(); let mut visitor = UnsafetyVisitor { tcx, thir, safety_context, hir_context: hir_id, - body_unsafety, body_target_features, assignment_info: None, in_union_destructure: false, param_env: tcx.param_env(def), inside_adt: false, + warnings: &mut warnings, + suggest_unsafe_block: true, }; visitor.visit_expr(&thir[expr]); + + warnings.sort_by_key(|w| w.block_span); + for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings { + let block_span = tcx.sess.source_map().guess_head_span(block_span); + tcx.emit_spanned_lint( + UNUSED_UNSAFE, + hir_id, + block_span, + UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, + ); + } } |