• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use rustc_data_structures::unord::{UnordItems, UnordSet};
2 use rustc_hir as hir;
3 use rustc_hir::def::DefKind;
4 use rustc_hir::def_id::{DefId, LocalDefId};
5 use rustc_hir::hir_id::HirId;
6 use rustc_hir::intravisit;
7 use rustc_hir::{BlockCheckMode, ExprKind, Node};
8 use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
9 use rustc_middle::mir::*;
10 use rustc_middle::query::Providers;
11 use rustc_middle::ty::{self, TyCtxt};
12 use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
13 use rustc_session::lint::Level;
14 
15 use std::ops::Bound;
16 
17 use crate::errors;
18 
19 pub struct UnsafetyChecker<'a, 'tcx> {
20     body: &'a Body<'tcx>,
21     body_did: LocalDefId,
22     violations: Vec<UnsafetyViolation>,
23     source_info: SourceInfo,
24     tcx: TyCtxt<'tcx>,
25     param_env: ty::ParamEnv<'tcx>,
26 
27     /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
28     used_unsafe_blocks: UnordSet<HirId>,
29 }
30 
31 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
new( body: &'a Body<'tcx>, body_did: LocalDefId, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Self32     fn new(
33         body: &'a Body<'tcx>,
34         body_did: LocalDefId,
35         tcx: TyCtxt<'tcx>,
36         param_env: ty::ParamEnv<'tcx>,
37     ) -> Self {
38         Self {
39             body,
40             body_did,
41             violations: vec![],
42             source_info: SourceInfo::outermost(body.span),
43             tcx,
44             param_env,
45             used_unsafe_blocks: Default::default(),
46         }
47     }
48 }
49 
50 impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location)51     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
52         self.source_info = terminator.source_info;
53         match terminator.kind {
54             TerminatorKind::Goto { .. }
55             | TerminatorKind::SwitchInt { .. }
56             | TerminatorKind::Drop { .. }
57             | TerminatorKind::Yield { .. }
58             | TerminatorKind::Assert { .. }
59             | TerminatorKind::GeneratorDrop
60             | TerminatorKind::Resume
61             | TerminatorKind::Terminate
62             | TerminatorKind::Return
63             | TerminatorKind::Unreachable
64             | TerminatorKind::FalseEdge { .. }
65             | TerminatorKind::FalseUnwind { .. } => {
66                 // safe (at least as emitted during MIR construction)
67             }
68 
69             TerminatorKind::Call { ref func, .. } => {
70                 let func_ty = func.ty(self.body, self.tcx);
71                 let func_id =
72                     if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None };
73                 let sig = func_ty.fn_sig(self.tcx);
74                 if let hir::Unsafety::Unsafe = sig.unsafety() {
75                     self.require_unsafe(
76                         UnsafetyViolationKind::General,
77                         UnsafetyViolationDetails::CallToUnsafeFunction,
78                     )
79                 }
80 
81                 if let Some(func_id) = func_id {
82                     self.check_target_features(*func_id);
83                 }
84             }
85 
86             TerminatorKind::InlineAsm { .. } => self.require_unsafe(
87                 UnsafetyViolationKind::General,
88                 UnsafetyViolationDetails::UseOfInlineAssembly,
89             ),
90         }
91         self.super_terminator(terminator, location);
92     }
93 
visit_statement(&mut self, statement: &Statement<'tcx>, location: Location)94     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
95         self.source_info = statement.source_info;
96         match statement.kind {
97             StatementKind::Assign(..)
98             | StatementKind::FakeRead(..)
99             | StatementKind::SetDiscriminant { .. }
100             | StatementKind::Deinit(..)
101             | StatementKind::StorageLive(..)
102             | StatementKind::StorageDead(..)
103             | StatementKind::Retag { .. }
104             | StatementKind::PlaceMention(..)
105             | StatementKind::Coverage(..)
106             | StatementKind::Intrinsic(..)
107             | StatementKind::ConstEvalCounter
108             | StatementKind::Nop => {
109                 // safe (at least as emitted during MIR construction)
110             }
111             // `AscribeUserType` just exists to help MIR borrowck.
112             // It has no semantics, and everything is already reported by `PlaceMention`.
113             StatementKind::AscribeUserType(..) => return,
114         }
115         self.super_statement(statement, location);
116     }
117 
visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location)118     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
119         match rvalue {
120             Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
121                 &AggregateKind::Array(..) | &AggregateKind::Tuple => {}
122                 &AggregateKind::Adt(adt_did, ..) => {
123                     match self.tcx.layout_scalar_valid_range(adt_did) {
124                         (Bound::Unbounded, Bound::Unbounded) => {}
125                         _ => self.require_unsafe(
126                             UnsafetyViolationKind::General,
127                             UnsafetyViolationDetails::InitializingTypeWith,
128                         ),
129                     }
130                 }
131                 &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
132                     let def_id = def_id.expect_local();
133                     let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
134                         self.tcx.unsafety_check_result(def_id);
135                     self.register_violations(violations, used_unsafe_blocks.items().copied());
136                 }
137             },
138             _ => {}
139         }
140         self.super_rvalue(rvalue, location);
141     }
142 
visit_operand(&mut self, op: &Operand<'tcx>, location: Location)143     fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
144         if let Operand::Constant(constant) = op {
145             let maybe_uneval = match constant.literal {
146                 ConstantKind::Val(..) | ConstantKind::Ty(_) => None,
147                 ConstantKind::Unevaluated(uv, _) => Some(uv),
148             };
149 
150             if let Some(uv) = maybe_uneval {
151                 if uv.promoted.is_none() {
152                     let def_id = uv.def;
153                     if self.tcx.def_kind(def_id) == DefKind::InlineConst {
154                         let local_def_id = def_id.expect_local();
155                         let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
156                             self.tcx.unsafety_check_result(local_def_id);
157                         self.register_violations(violations, used_unsafe_blocks.items().copied());
158                     }
159                 }
160             }
161         }
162         self.super_operand(op, location);
163     }
164 
visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location)165     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
166         // On types with `scalar_valid_range`, prevent
167         // * `&mut x.field`
168         // * `x.field = y;`
169         // * `&x.field` if `field`'s type has interior mutability
170         // because either of these would allow modifying the layout constrained field and
171         // insert values that violate the layout constraints.
172         if context.is_mutating_use() || context.is_borrow() {
173             self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
174         }
175 
176         // Some checks below need the extra meta info of the local declaration.
177         let decl = &self.body.local_decls[place.local];
178 
179         // Check the base local: it might be an unsafe-to-access static. We only check derefs of the
180         // temporary holding the static pointer to avoid duplicate errors
181         // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
182         if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
183             // If the projection root is an artificial local that we introduced when
184             // desugaring `static`, give a more specific error message
185             // (avoid the general "raw pointer" clause below, that would only be confusing).
186             if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
187                 if self.tcx.is_mutable_static(def_id) {
188                     self.require_unsafe(
189                         UnsafetyViolationKind::General,
190                         UnsafetyViolationDetails::UseOfMutableStatic,
191                     );
192                     return;
193                 } else if self.tcx.is_foreign_item(def_id) {
194                     self.require_unsafe(
195                         UnsafetyViolationKind::General,
196                         UnsafetyViolationDetails::UseOfExternStatic,
197                     );
198                     return;
199                 }
200             }
201         }
202 
203         // Check for raw pointer `Deref`.
204         for (base, proj) in place.iter_projections() {
205             if proj == ProjectionElem::Deref {
206                 let base_ty = base.ty(self.body, self.tcx).ty;
207                 if base_ty.is_unsafe_ptr() {
208                     self.require_unsafe(
209                         UnsafetyViolationKind::General,
210                         UnsafetyViolationDetails::DerefOfRawPointer,
211                     )
212                 }
213             }
214         }
215 
216         // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
217         // whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
218         let mut saw_deref = false;
219         for (base, proj) in place.iter_projections().rev() {
220             if proj == ProjectionElem::Deref {
221                 saw_deref = true;
222                 continue;
223             }
224 
225             let base_ty = base.ty(self.body, self.tcx).ty;
226             if base_ty.is_union() {
227                 // If we did not hit a `Deref` yet and the overall place use is an assignment, the
228                 // rules are different.
229                 let assign_to_field = !saw_deref
230                     && matches!(
231                         context,
232                         PlaceContext::MutatingUse(
233                             MutatingUseContext::Store
234                                 | MutatingUseContext::Drop
235                                 | MutatingUseContext::AsmOutput
236                         )
237                     );
238                 // If this is just an assignment, determine if the assigned type needs dropping.
239                 if assign_to_field {
240                     // We have to check the actual type of the assignment, as that determines if the
241                     // old value is being dropped.
242                     let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
243                     if assigned_ty.needs_drop(self.tcx, self.param_env) {
244                         // This would be unsafe, but should be outright impossible since we reject such unions.
245                         self.tcx.sess.delay_span_bug(
246                             self.source_info.span,
247                             format!("union fields that need dropping should be impossible: {assigned_ty}")
248                         );
249                     }
250                 } else {
251                     self.require_unsafe(
252                         UnsafetyViolationKind::General,
253                         UnsafetyViolationDetails::AccessToUnionField,
254                     )
255                 }
256             }
257         }
258     }
259 }
260 
261 impl<'tcx> UnsafetyChecker<'_, 'tcx> {
require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails)262     fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
263         // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
264         assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
265 
266         let source_info = self.source_info;
267         let lint_root = self.body.source_scopes[self.source_info.scope]
268             .local_data
269             .as_ref()
270             .assert_crate_local()
271             .lint_root;
272         self.register_violations(
273             [&UnsafetyViolation { source_info, lint_root, kind, details }],
274             UnordItems::empty(),
275         );
276     }
277 
register_violations<'a>( &mut self, violations: impl IntoIterator<Item = &'a UnsafetyViolation>, new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>, )278     fn register_violations<'a>(
279         &mut self,
280         violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
281         new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>,
282     ) {
283         let safety = self.body.source_scopes[self.source_info.scope]
284             .local_data
285             .as_ref()
286             .assert_crate_local()
287             .safety;
288         match safety {
289             // `unsafe` blocks are required in safe code
290             Safety::Safe => violations.into_iter().for_each(|&violation| {
291                 match violation.kind {
292                     UnsafetyViolationKind::General => {}
293                     UnsafetyViolationKind::UnsafeFn => {
294                         bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
295                     }
296                 }
297                 if !self.violations.contains(&violation) {
298                     self.violations.push(violation)
299                 }
300             }),
301             // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
302             Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
303                 violation.kind = UnsafetyViolationKind::UnsafeFn;
304                 if !self.violations.contains(&violation) {
305                     self.violations.push(violation)
306                 }
307             }),
308             Safety::BuiltinUnsafe => {}
309             Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| {
310                 self.used_unsafe_blocks.insert(hir_id);
311             }),
312         };
313 
314         self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks);
315     }
check_mut_borrowing_layout_constrained_field( &mut self, place: Place<'tcx>, is_mut_use: bool, )316     fn check_mut_borrowing_layout_constrained_field(
317         &mut self,
318         place: Place<'tcx>,
319         is_mut_use: bool,
320     ) {
321         for (place_base, elem) in place.iter_projections().rev() {
322             match elem {
323                 // Modifications behind a dereference don't affect the value of
324                 // the pointer.
325                 ProjectionElem::Deref => return,
326                 ProjectionElem::Field(..) => {
327                     let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
328                     if let ty::Adt(def, _) = ty.kind() {
329                         if self.tcx.layout_scalar_valid_range(def.did())
330                             != (Bound::Unbounded, Bound::Unbounded)
331                         {
332                             let details = if is_mut_use {
333                                 UnsafetyViolationDetails::MutationOfLayoutConstrainedField
334 
335                             // Check `is_freeze` as late as possible to avoid cycle errors
336                             // with opaque types.
337                             } else if !place
338                                 .ty(self.body, self.tcx)
339                                 .ty
340                                 .is_freeze(self.tcx, self.param_env)
341                             {
342                                 UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
343                             } else {
344                                 continue;
345                             };
346                             self.require_unsafe(UnsafetyViolationKind::General, details);
347                         }
348                     }
349                 }
350                 _ => {}
351             }
352         }
353     }
354 
355     /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
356     /// the called function has target features the calling function hasn't.
check_target_features(&mut self, func_did: DefId)357     fn check_target_features(&mut self, func_did: DefId) {
358         // Unsafety isn't required on wasm targets. For more information see
359         // the corresponding check in typeck/src/collect.rs
360         if self.tcx.sess.target.options.is_like_wasm {
361             return;
362         }
363 
364         let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
365         // The body might be a constant, so it doesn't have codegen attributes.
366         let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features;
367 
368         // Is `callee_features` a subset of `calling_features`?
369         if !callee_features.iter().all(|feature| self_features.contains(feature)) {
370             self.require_unsafe(
371                 UnsafetyViolationKind::General,
372                 UnsafetyViolationDetails::CallToFunctionWith,
373             )
374         }
375     }
376 }
377 
provide(providers: &mut Providers)378 pub(crate) fn provide(providers: &mut Providers) {
379     *providers = Providers { unsafety_check_result, ..*providers };
380 }
381 
382 /// Context information for [`UnusedUnsafeVisitor`] traversal,
383 /// saves (innermost) relevant context
384 #[derive(Copy, Clone, Debug)]
385 enum Context {
386     Safe,
387     /// in an `unsafe fn`
388     UnsafeFn(HirId),
389     /// in a *used* `unsafe` block
390     /// (i.e. a block without unused-unsafe warning)
391     UnsafeBlock(HirId),
392 }
393 
394 struct UnusedUnsafeVisitor<'a, 'tcx> {
395     tcx: TyCtxt<'tcx>,
396     used_unsafe_blocks: &'a UnordSet<HirId>,
397     context: Context,
398     unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>,
399 }
400 
401 impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
visit_block(&mut self, block: &'tcx hir::Block<'tcx>)402     fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
403         if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
404             let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) {
405                 (Level::Allow, _) => true,
406                 _ => self.used_unsafe_blocks.contains(&block.hir_id),
407             };
408             let unused_unsafe = match (self.context, used) {
409                 (_, false) => UnusedUnsafe::Unused,
410                 (Context::Safe, true) | (Context::UnsafeFn(_), true) => {
411                     let previous_context = self.context;
412                     self.context = Context::UnsafeBlock(block.hir_id);
413                     intravisit::walk_block(self, block);
414                     self.context = previous_context;
415                     return;
416                 }
417                 (Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id),
418             };
419             self.unused_unsafes.push((block.hir_id, unused_unsafe));
420         }
421         intravisit::walk_block(self, block);
422     }
423 
visit_inline_const(&mut self, c: &'tcx hir::ConstBlock)424     fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
425         self.visit_body(self.tcx.hir().body(c.body))
426     }
427 
visit_fn( &mut self, fk: intravisit::FnKind<'tcx>, _fd: &'tcx hir::FnDecl<'tcx>, b: hir::BodyId, _s: rustc_span::Span, _id: LocalDefId, )428     fn visit_fn(
429         &mut self,
430         fk: intravisit::FnKind<'tcx>,
431         _fd: &'tcx hir::FnDecl<'tcx>,
432         b: hir::BodyId,
433         _s: rustc_span::Span,
434         _id: LocalDefId,
435     ) {
436         if matches!(fk, intravisit::FnKind::Closure) {
437             self.visit_body(self.tcx.hir().body(b))
438         }
439     }
440 }
441 
check_unused_unsafe( tcx: TyCtxt<'_>, def_id: LocalDefId, used_unsafe_blocks: &UnordSet<HirId>, ) -> Vec<(HirId, UnusedUnsafe)>442 fn check_unused_unsafe(
443     tcx: TyCtxt<'_>,
444     def_id: LocalDefId,
445     used_unsafe_blocks: &UnordSet<HirId>,
446 ) -> Vec<(HirId, UnusedUnsafe)> {
447     let body_id = tcx.hir().maybe_body_owned_by(def_id);
448 
449     let Some(body_id) = body_id else {
450         debug!("check_unused_unsafe({:?}) - no body found", def_id);
451         return vec![];
452     };
453 
454     let body = tcx.hir().body(body_id);
455     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
456     let context = match tcx.hir().fn_sig_by_hir_id(hir_id) {
457         Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn(hir_id),
458         _ => Context::Safe,
459     };
460 
461     debug!(
462         "check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})",
463         def_id, body, context, used_unsafe_blocks
464     );
465 
466     let mut unused_unsafes = vec![];
467 
468     let mut visitor = UnusedUnsafeVisitor {
469         tcx,
470         used_unsafe_blocks,
471         context,
472         unused_unsafes: &mut unused_unsafes,
473     };
474     intravisit::Visitor::visit_body(&mut visitor, body);
475 
476     unused_unsafes
477 }
478 
unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult479 fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult {
480     debug!("unsafety_violations({:?})", def);
481 
482     // N.B., this borrow is valid because all the consumers of
483     // `mir_built` force this.
484     let body = &tcx.mir_built(def).borrow();
485 
486     if body.is_custom_mir() {
487         return tcx.arena.alloc(UnsafetyCheckResult {
488             violations: Vec::new(),
489             used_unsafe_blocks: Default::default(),
490             unused_unsafes: Some(Vec::new()),
491         });
492     }
493 
494     let param_env = tcx.param_env(def);
495 
496     let mut checker = UnsafetyChecker::new(body, def, tcx, param_env);
497     checker.visit_body(&body);
498 
499     let unused_unsafes = (!tcx.is_typeck_child(def.to_def_id()))
500         .then(|| check_unused_unsafe(tcx, def, &checker.used_unsafe_blocks));
501 
502     tcx.arena.alloc(UnsafetyCheckResult {
503         violations: checker.violations,
504         used_unsafe_blocks: checker.used_unsafe_blocks,
505         unused_unsafes,
506     })
507 }
508 
report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId)509 fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
510     let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
511     let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind {
512         Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
513     } else {
514         None
515     };
516     tcx.emit_spanned_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent });
517 }
518 
check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId)519 pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
520     debug!("check_unsafety({:?})", def_id);
521 
522     // closures and inline consts are handled by their parent fn.
523     if tcx.is_typeck_child(def_id.to_def_id()) {
524         return;
525     }
526 
527     let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
528     // Only suggest wrapping the entire function body in an unsafe block once
529     let mut suggest_unsafe_block = true;
530 
531     for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
532         let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
533 
534         match kind {
535             UnsafetyViolationKind::General => {
536                 let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root);
537                 let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| {
538                     if let Node::Expr(block) = node
539                         && let ExprKind::Block(block, _) = block.kind
540                         && let BlockCheckMode::UnsafeBlock(_) = block.rules
541                     {
542                         true
543                     }
544                     else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
545                         && sig.header.is_unsafe()
546                     {
547                         true
548                     } else {
549                         false
550                     }
551                 });
552                 let enclosing = if let Some((id, _)) = note_non_inherited {
553                     Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
554                 } else {
555                     None
556                 };
557                 tcx.sess.emit_err(errors::RequiresUnsafe {
558                     span: source_info.span,
559                     enclosing,
560                     details,
561                     op_in_unsafe_fn_allowed,
562                 });
563             }
564             UnsafetyViolationKind::UnsafeFn => {
565                 tcx.emit_spanned_lint(
566                     UNSAFE_OP_IN_UNSAFE_FN,
567                     lint_root,
568                     source_info.span,
569                     errors::UnsafeOpInUnsafeFn {
570                         details,
571                         suggest_unsafe_block: suggest_unsafe_block.then(|| {
572                             let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
573                             let fn_sig = tcx
574                                 .hir()
575                                 .fn_sig_by_hir_id(hir_id)
576                                 .expect("this violation only occurs in fn");
577                             let body = tcx.hir().body_owned_by(def_id);
578                             let body_span = tcx.hir().body(body).value.span;
579                             let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi();
580                             let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo();
581                             (start, end, fn_sig.span)
582                         }),
583                     },
584                 );
585                 suggest_unsafe_block = false;
586             }
587         }
588     }
589 
590     for &(block_id, kind) in unused_unsafes.as_ref().unwrap() {
591         report_unused_unsafe(tcx, kind, block_id);
592     }
593 }
594 
unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool595 fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
596     tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
597 }
598