1 use rustc_data_structures
::fx
::FxHashSet
;
2 use rustc_errors
::struct_span_err
;
4 use rustc_hir
::def_id
::{DefId, LocalDefId}
;
5 use rustc_hir
::hir_id
::HirId
;
6 use rustc_hir
::intravisit
;
8 use rustc_middle
::mir
::visit
::{MutatingUseContext, PlaceContext, Visitor}
;
9 use rustc_middle
::mir
::*;
10 use rustc_middle
::ty
::cast
::CastTy
;
11 use rustc_middle
::ty
::query
::Providers
;
12 use rustc_middle
::ty
::{self, TyCtxt}
;
13 use rustc_session
::lint
::builtin
::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}
;
14 use rustc_session
::lint
::Level
;
15 use rustc_span
::symbol
::sym
;
19 use crate::const_eval
::is_min_const_fn
;
22 pub struct UnsafetyChecker
<'a
, 'tcx
> {
27 violations
: Vec
<UnsafetyViolation
>,
28 source_info
: SourceInfo
,
30 param_env
: ty
::ParamEnv
<'tcx
>,
31 /// Mark an `unsafe` block as used, so we don't lint it.
32 used_unsafe
: FxHashSet
<hir
::HirId
>,
33 inherited_blocks
: Vec
<(hir
::HirId
, bool
)>,
36 impl<'a
, 'tcx
> UnsafetyChecker
<'a
, 'tcx
> {
43 param_env
: ty
::ParamEnv
<'tcx
>,
47 assert
!(const_context
);
55 source_info
: SourceInfo
::outermost(body
.span
),
58 used_unsafe
: Default
::default(),
59 inherited_blocks
: vec
![],
64 impl<'a
, 'tcx
> Visitor
<'tcx
> for UnsafetyChecker
<'a
, 'tcx
> {
65 fn visit_terminator(&mut self, terminator
: &Terminator
<'tcx
>, location
: Location
) {
66 self.source_info
= terminator
.source_info
;
67 match terminator
.kind
{
68 TerminatorKind
::Goto { .. }
69 | TerminatorKind
::SwitchInt { .. }
70 | TerminatorKind
::Drop { .. }
71 | TerminatorKind
::Yield { .. }
72 | TerminatorKind
::Assert { .. }
73 | TerminatorKind
::DropAndReplace { .. }
74 | TerminatorKind
::GeneratorDrop
75 | TerminatorKind
::Resume
76 | TerminatorKind
::Abort
77 | TerminatorKind
::Return
78 | TerminatorKind
::Unreachable
79 | TerminatorKind
::FalseEdge { .. }
80 | TerminatorKind
::FalseUnwind { .. }
=> {
81 // safe (at least as emitted during MIR construction)
84 TerminatorKind
::Call { ref func, .. }
=> {
85 let func_ty
= func
.ty(self.body
, self.tcx
);
86 let sig
= func_ty
.fn_sig(self.tcx
);
87 if let hir
::Unsafety
::Unsafe
= sig
.unsafety() {
89 UnsafetyViolationKind
::GeneralAndConstFn
,
90 UnsafetyViolationDetails
::CallToUnsafeFunction
,
94 if let ty
::FnDef(func_id
, _
) = func_ty
.kind
{
95 self.check_target_features(func_id
);
99 TerminatorKind
::InlineAsm { .. }
=> self.require_unsafe(
100 UnsafetyViolationKind
::General
,
101 UnsafetyViolationDetails
::UseOfInlineAssembly
,
104 self.super_terminator(terminator
, location
);
107 fn visit_statement(&mut self, statement
: &Statement
<'tcx
>, location
: Location
) {
108 self.source_info
= statement
.source_info
;
109 match statement
.kind
{
110 StatementKind
::Assign(..)
111 | StatementKind
::FakeRead(..)
112 | StatementKind
::SetDiscriminant { .. }
113 | StatementKind
::StorageLive(..)
114 | StatementKind
::StorageDead(..)
115 | StatementKind
::Retag { .. }
116 | StatementKind
::AscribeUserType(..)
117 | StatementKind
::Coverage(..)
118 | StatementKind
::Nop
=> {
119 // safe (at least as emitted during MIR construction)
122 StatementKind
::LlvmInlineAsm { .. }
=> self.require_unsafe(
123 UnsafetyViolationKind
::General
,
124 UnsafetyViolationDetails
::UseOfInlineAssembly
,
127 self.super_statement(statement
, location
);
130 fn visit_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>, location
: Location
) {
132 Rvalue
::Aggregate(box ref aggregate
, _
) => match aggregate
{
133 &AggregateKind
::Array(..) | &AggregateKind
::Tuple
=> {}
134 &AggregateKind
::Adt(ref def
, ..) => {
135 match self.tcx
.layout_scalar_valid_range(def
.did
) {
136 (Bound
::Unbounded
, Bound
::Unbounded
) => {}
137 _
=> self.require_unsafe(
138 UnsafetyViolationKind
::GeneralAndConstFn
,
139 UnsafetyViolationDetails
::InitializingTypeWith
,
143 &AggregateKind
::Closure(def_id
, _
) | &AggregateKind
::Generator(def_id
, _
, _
) => {
144 let UnsafetyCheckResult { violations, unsafe_blocks }
=
145 self.tcx
.unsafety_check_result(def_id
.expect_local());
146 self.register_violations(&violations
, &unsafe_blocks
);
149 // casting pointers to ints is unsafe in const fn because the const evaluator cannot
150 // possibly know what the result of various operations like `address / 2` would be
151 // pointers during const evaluation have no integral address, only an abstract one
152 Rvalue
::Cast(CastKind
::Misc
, ref operand
, cast_ty
)
153 if self.const_context
&& self.tcx
.features().const_raw_ptr_to_usize_cast
=>
155 let operand_ty
= operand
.ty(self.body
, self.tcx
);
156 let cast_in
= CastTy
::from_ty(operand_ty
).expect("bad input type for cast");
157 let cast_out
= CastTy
::from_ty(cast_ty
).expect("bad output type for cast");
158 match (cast_in
, cast_out
) {
159 (CastTy
::Ptr(_
) | CastTy
::FnPtr
, CastTy
::Int(_
)) => {
161 UnsafetyViolationKind
::General
,
162 UnsafetyViolationDetails
::CastOfPointerToInt
,
170 self.super_rvalue(rvalue
, location
);
173 fn visit_place(&mut self, place
: &Place
<'tcx
>, context
: PlaceContext
, _location
: Location
) {
174 // On types with `scalar_valid_range`, prevent
177 // * `&x.field` if `field`'s type has interior mutability
178 // because either of these would allow modifying the layout constrained field and
179 // insert values that violate the layout constraints.
180 if context
.is_mutating_use() || context
.is_borrow() {
181 self.check_mut_borrowing_layout_constrained_field(*place
, context
.is_mutating_use());
184 if context
.is_borrow() {
185 if util
::is_disaligned(self.tcx
, self.body
, self.param_env
, *place
) {
187 UnsafetyViolationKind
::BorrowPacked
,
188 UnsafetyViolationDetails
::BorrowOfPackedField
,
193 for (i
, elem
) in place
.projection
.iter().enumerate() {
194 let proj_base
= &place
.projection
[..i
];
195 if context
.is_borrow() {
196 if util
::is_disaligned(self.tcx
, self.body
, self.param_env
, *place
) {
198 UnsafetyViolationKind
::BorrowPacked
,
199 UnsafetyViolationDetails
::BorrowOfPackedField
,
203 let source_info
= self.source_info
;
204 if let [] = proj_base
{
205 let decl
= &self.body
.local_decls
[place
.local
];
207 if let Some(box LocalInfo
::StaticRef { def_id, .. }
) = decl
.local_info
{
208 if self.tcx
.is_mutable_static(def_id
) {
210 UnsafetyViolationKind
::General
,
211 UnsafetyViolationDetails
::UseOfMutableStatic
,
214 } else if self.tcx
.is_foreign_item(def_id
) {
216 UnsafetyViolationKind
::General
,
217 UnsafetyViolationDetails
::UseOfExternStatic
,
222 // Internal locals are used in the `move_val_init` desugaring.
223 // We want to check unsafety against the source info of the
224 // desugaring, rather than the source info of the RHS.
225 self.source_info
= self.body
.local_decls
[place
.local
].source_info
;
229 let base_ty
= Place
::ty_from(place
.local
, proj_base
, self.body
, self.tcx
).ty
;
231 ty
::RawPtr(..) => self.require_unsafe(
232 UnsafetyViolationKind
::GeneralAndConstFn
,
233 UnsafetyViolationDetails
::DerefOfRawPointer
,
237 if context
== PlaceContext
::MutatingUse(MutatingUseContext
::Store
)
238 || context
== PlaceContext
::MutatingUse(MutatingUseContext
::Drop
)
239 || context
== PlaceContext
::MutatingUse(MutatingUseContext
::AsmOutput
)
241 let elem_ty
= match elem
{
242 ProjectionElem
::Field(_
, ty
) => ty
,
244 self.source_info
.span
,
245 "non-field projection {:?} from union?",
249 if !elem_ty
.is_copy_modulo_regions(
250 self.tcx
.at(self.source_info
.span
),
254 UnsafetyViolationKind
::GeneralAndConstFn
,
255 UnsafetyViolationDetails
::AssignToNonCopyUnionField
,
258 // write to non-move union, safe
262 UnsafetyViolationKind
::GeneralAndConstFn
,
263 UnsafetyViolationDetails
::AccessToUnionField
,
270 self.source_info
= source_info
;
275 impl<'a
, 'tcx
> UnsafetyChecker
<'a
, 'tcx
> {
276 fn require_unsafe(&mut self, kind
: UnsafetyViolationKind
, details
: UnsafetyViolationDetails
) {
277 let source_info
= self.source_info
;
278 let lint_root
= self.body
.source_scopes
[self.source_info
.scope
]
281 .assert_crate_local()
283 self.register_violations(
284 &[UnsafetyViolation { source_info, lint_root, kind, details }
],
289 fn register_violations(
291 violations
: &[UnsafetyViolation
],
292 unsafe_blocks
: &[(hir
::HirId
, bool
)],
294 let safety
= self.body
.source_scopes
[self.source_info
.scope
]
297 .assert_crate_local()
299 let within_unsafe
= match safety
{
300 // `unsafe` blocks are required in safe code
302 for violation
in violations
{
303 let mut violation
= *violation
;
304 match violation
.kind
{
305 UnsafetyViolationKind
::GeneralAndConstFn
306 | UnsafetyViolationKind
::General
=> {}
307 UnsafetyViolationKind
::BorrowPacked
=> {
308 if self.min_const_fn
{
309 // const fns don't need to be backwards compatible and can
310 // emit these violations as a hard error instead of a backwards
312 violation
.kind
= UnsafetyViolationKind
::General
;
315 UnsafetyViolationKind
::UnsafeFn
316 | UnsafetyViolationKind
::UnsafeFnBorrowPacked
=> {
317 bug
!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
320 if !self.violations
.contains(&violation
) {
321 self.violations
.push(violation
)
326 // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
327 Safety
::FnUnsafe
if self.tcx
.features().unsafe_block_in_unsafe_fn
=> {
328 for violation
in violations
{
329 let mut violation
= *violation
;
331 if violation
.kind
== UnsafetyViolationKind
::BorrowPacked
{
332 violation
.kind
= UnsafetyViolationKind
::UnsafeFnBorrowPacked
;
334 violation
.kind
= UnsafetyViolationKind
::UnsafeFn
;
336 if !self.violations
.contains(&violation
) {
337 self.violations
.push(violation
)
342 // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
343 Safety
::BuiltinUnsafe
| Safety
::FnUnsafe
=> true,
344 Safety
::ExplicitUnsafe(hir_id
) => {
345 // mark unsafe block as used if there are any unsafe operations inside
346 if !violations
.is_empty() {
347 self.used_unsafe
.insert(hir_id
);
349 // only some unsafety is allowed in const fn
350 if self.min_const_fn
{
351 for violation
in violations
{
352 match violation
.kind
{
353 // these unsafe things are stable in const fn
354 UnsafetyViolationKind
::GeneralAndConstFn
=> {}
355 // these things are forbidden in const fns
356 UnsafetyViolationKind
::General
357 | UnsafetyViolationKind
::BorrowPacked
=> {
358 let mut violation
= *violation
;
359 // const fns don't need to be backwards compatible and can
360 // emit these violations as a hard error instead of a backwards
362 violation
.kind
= UnsafetyViolationKind
::General
;
363 if !self.violations
.contains(&violation
) {
364 self.violations
.push(violation
)
367 UnsafetyViolationKind
::UnsafeFn
368 | UnsafetyViolationKind
::UnsafeFnBorrowPacked
=> bug
!(
369 "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
377 self.inherited_blocks
.extend(
378 unsafe_blocks
.iter().map(|&(hir_id
, is_used
)| (hir_id
, is_used
&& !within_unsafe
)),
381 fn check_mut_borrowing_layout_constrained_field(
386 let mut cursor
= place
.projection
.as_ref();
387 while let &[ref proj_base @
.., elem
] = cursor
{
391 // Modifications behind a dereference don't affect the value of
393 ProjectionElem
::Deref
=> return,
394 ProjectionElem
::Field(..) => {
396 Place
::ty_from(place
.local
, proj_base
, &self.body
.local_decls
, self.tcx
).ty
;
397 if let ty
::Adt(def
, _
) = ty
.kind
{
398 if self.tcx
.layout_scalar_valid_range(def
.did
)
399 != (Bound
::Unbounded
, Bound
::Unbounded
)
401 let details
= if is_mut_use
{
402 UnsafetyViolationDetails
::MutationOfLayoutConstrainedField
404 // Check `is_freeze` as late as possible to avoid cycle errors
405 // with opaque types.
407 .ty(self.body
, self.tcx
)
409 .is_freeze(self.tcx
.at(self.source_info
.span
), self.param_env
)
411 UnsafetyViolationDetails
::BorrowOfLayoutConstrainedField
415 self.require_unsafe(UnsafetyViolationKind
::GeneralAndConstFn
, details
);
424 /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
425 /// the called function has target features the calling function hasn't.
426 fn check_target_features(&mut self, func_did
: DefId
) {
427 let callee_features
= &self.tcx
.codegen_fn_attrs(func_did
).target_features
;
428 let self_features
= &self.tcx
.codegen_fn_attrs(self.body_did
).target_features
;
430 // Is `callee_features` a subset of `calling_features`?
431 if !callee_features
.iter().all(|feature
| self_features
.contains(feature
)) {
433 UnsafetyViolationKind
::GeneralAndConstFn
,
434 UnsafetyViolationDetails
::CallToFunctionWith
,
440 pub(crate) fn provide(providers
: &mut Providers
) {
441 *providers
= Providers
{
442 unsafety_check_result
: |tcx
, def_id
| {
443 if let Some(def
) = ty
::WithOptConstParam
::try_lookup(def_id
, tcx
) {
444 tcx
.unsafety_check_result_for_const_arg(def
)
446 unsafety_check_result(tcx
, ty
::WithOptConstParam
::unknown(def_id
))
449 unsafety_check_result_for_const_arg
: |tcx
, (did
, param_did
)| {
450 unsafety_check_result(
452 ty
::WithOptConstParam { did, const_param_did: Some(param_did) }
,
455 unsafe_derive_on_repr_packed
,
460 struct UnusedUnsafeVisitor
<'a
> {
461 used_unsafe
: &'a FxHashSet
<hir
::HirId
>,
462 unsafe_blocks
: &'a
mut Vec
<(hir
::HirId
, bool
)>,
465 impl<'a
, 'tcx
> intravisit
::Visitor
<'tcx
> for UnusedUnsafeVisitor
<'a
> {
466 type Map
= intravisit
::ErasedMap
<'tcx
>;
468 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
469 intravisit
::NestedVisitorMap
::None
472 fn visit_block(&mut self, block
: &'tcx hir
::Block
<'tcx
>) {
473 intravisit
::walk_block(self, block
);
475 if let hir
::BlockCheckMode
::UnsafeBlock(hir
::UnsafeSource
::UserProvided
) = block
.rules
{
476 self.unsafe_blocks
.push((block
.hir_id
, self.used_unsafe
.contains(&block
.hir_id
)));
481 fn check_unused_unsafe(
484 used_unsafe
: &FxHashSet
<hir
::HirId
>,
485 unsafe_blocks
: &mut Vec
<(hir
::HirId
, bool
)>,
487 let body_id
= tcx
.hir().maybe_body_owned_by(tcx
.hir().local_def_id_to_hir_id(def_id
));
489 let body_id
= match body_id
{
492 debug
!("check_unused_unsafe({:?}) - no body found", def_id
);
496 let body
= tcx
.hir().body(body_id
);
497 debug
!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id
, body
, used_unsafe
);
499 let mut visitor
= UnusedUnsafeVisitor { used_unsafe, unsafe_blocks }
;
500 intravisit
::Visitor
::visit_body(&mut visitor
, body
);
503 fn unsafety_check_result
<'tcx
>(
505 def
: ty
::WithOptConstParam
<LocalDefId
>,
506 ) -> &'tcx UnsafetyCheckResult
{
507 debug
!("unsafety_violations({:?})", def
);
509 // N.B., this borrow is valid because all the consumers of
510 // `mir_built` force this.
511 let body
= &tcx
.mir_built(def
).borrow();
513 let param_env
= tcx
.param_env(def
.did
);
515 let id
= tcx
.hir().local_def_id_to_hir_id(def
.did
);
516 let (const_context
, min_const_fn
) = match tcx
.hir().body_owner_kind(id
) {
517 hir
::BodyOwnerKind
::Closure
=> (false, false),
518 hir
::BodyOwnerKind
::Fn
=> {
519 (tcx
.is_const_fn_raw(def
.did
.to_def_id()), is_min_const_fn(tcx
, def
.did
.to_def_id()))
521 hir
::BodyOwnerKind
::Const
| hir
::BodyOwnerKind
::Static(_
) => (true, false),
524 UnsafetyChecker
::new(const_context
, min_const_fn
, body
, def
.did
, tcx
, param_env
);
525 checker
.visit_body(&body
);
527 check_unused_unsafe(tcx
, def
.did
, &checker
.used_unsafe
, &mut checker
.inherited_blocks
);
529 tcx
.arena
.alloc(UnsafetyCheckResult
{
530 violations
: checker
.violations
.into(),
531 unsafe_blocks
: checker
.inherited_blocks
.into(),
535 fn unsafe_derive_on_repr_packed(tcx
: TyCtxt
<'_
>, def_id
: LocalDefId
) {
536 let lint_hir_id
= tcx
.hir().local_def_id_to_hir_id(def_id
);
538 tcx
.struct_span_lint_hir(SAFE_PACKED_BORROWS
, lint_hir_id
, tcx
.def_span(def_id
), |lint
| {
539 // FIXME: when we make this a hard error, this should have its
541 let message
= if tcx
.generics_of(def_id
).own_requires_monomorphization() {
542 "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
543 type or const parameters (error E0133)"
546 "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
547 does not derive Copy (error E0133)"
550 lint
.build(&message
).emit()
554 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
557 used_unsafe
: &FxHashSet
<hir
::HirId
>,
559 ) -> Option
<(String
, hir
::HirId
)> {
560 let parent_id
= tcx
.hir().get_parent_node(id
);
562 if used_unsafe
.contains(&parent_id
) {
563 Some(("block".to_string(), parent_id
))
564 } else if let Some(Node
::Item(&hir
::Item
{
565 kind
: hir
::ItemKind
::Fn(ref sig
, _
, _
), ..
566 })) = tcx
.hir().find(parent_id
)
568 if sig
.header
.unsafety
== hir
::Unsafety
::Unsafe
569 && !tcx
.features().unsafe_block_in_unsafe_fn
571 Some(("fn".to_string(), parent_id
))
576 is_enclosed(tcx
, used_unsafe
, parent_id
)
583 fn report_unused_unsafe(tcx
: TyCtxt
<'_
>, used_unsafe
: &FxHashSet
<hir
::HirId
>, id
: hir
::HirId
) {
584 let span
= tcx
.sess
.source_map().guess_head_span(tcx
.hir().span(id
));
585 tcx
.struct_span_lint_hir(UNUSED_UNSAFE
, id
, span
, |lint
| {
586 let msg
= "unnecessary `unsafe` block";
587 let mut db
= lint
.build(msg
);
588 db
.span_label(span
, msg
);
589 if let Some((kind
, id
)) = is_enclosed(tcx
, used_unsafe
, id
) {
591 tcx
.sess
.source_map().guess_head_span(tcx
.hir().span(id
)),
592 format
!("because it's nested under this `unsafe` {}", kind
),
599 fn builtin_derive_def_id(tcx
: TyCtxt
<'_
>, def_id
: DefId
) -> Option
<DefId
> {
600 debug
!("builtin_derive_def_id({:?})", def_id
);
601 if let Some(impl_def_id
) = tcx
.impl_of_method(def_id
) {
602 if tcx
.has_attr(impl_def_id
, sym
::automatically_derived
) {
603 debug
!("builtin_derive_def_id({:?}) - is {:?}", def_id
, impl_def_id
);
606 debug
!("builtin_derive_def_id({:?}) - not automatically derived", def_id
);
610 debug
!("builtin_derive_def_id({:?}) - not a method", def_id
);
615 pub fn check_unsafety(tcx
: TyCtxt
<'_
>, def_id
: LocalDefId
) {
616 debug
!("check_unsafety({:?})", def_id
);
618 // closures are handled by their parent fn.
619 if tcx
.is_closure(def_id
.to_def_id()) {
623 let UnsafetyCheckResult { violations, unsafe_blocks }
= tcx
.unsafety_check_result(def_id
);
625 for &UnsafetyViolation { source_info, lint_root, kind, details }
in violations
.iter() {
626 let (description
, note
) = details
.description_and_note();
630 if unsafe_op_in_unsafe_fn_allowed(tcx
, lint_root
) { " function or" }
else { "" }
;
633 UnsafetyViolationKind
::GeneralAndConstFn
| UnsafetyViolationKind
::General
=> {
639 "{} is unsafe and requires unsafe{} block",
643 .span_label(source_info
.span
, description
)
647 UnsafetyViolationKind
::BorrowPacked
=> {
648 if let Some(impl_def_id
) = builtin_derive_def_id(tcx
, def_id
.to_def_id()) {
649 // If a method is defined in the local crate,
650 // the impl containing that method should also be.
651 tcx
.ensure().unsafe_derive_on_repr_packed(impl_def_id
.expect_local());
653 tcx
.struct_span_lint_hir(
659 "{} is unsafe and requires unsafe{} block (error E0133)",
660 description
, unsafe_fn_msg
,
668 UnsafetyViolationKind
::UnsafeFn
=> tcx
.struct_span_lint_hir(
669 UNSAFE_OP_IN_UNSAFE_FN
,
674 "{} is unsafe and requires unsafe block (error E0133)",
677 .span_label(source_info
.span
, description
)
682 UnsafetyViolationKind
::UnsafeFnBorrowPacked
=> {
683 // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
684 // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
685 // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
686 // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
688 // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
689 // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
690 // should only issue a warning for the sake of backwards compatibility.
692 // The solution those 2 expectations is to always take the minimum of both lints.
693 // This prevent any new errors (unless both lints are explicitely set to `deny`).
694 let lint
= if tcx
.lint_level_at_node(SAFE_PACKED_BORROWS
, lint_root
).0
695 <= tcx
.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN
, lint_root
).0
699 UNSAFE_OP_IN_UNSAFE_FN
701 tcx
.struct_span_lint_hir(&lint
, lint_root
, source_info
.span
, |lint
| {
703 "{} is unsafe and requires unsafe block (error E0133)",
706 .span_label(source_info
.span
, description
)
714 let (mut unsafe_used
, mut unsafe_unused
): (FxHashSet
<_
>, Vec
<_
>) = Default
::default();
715 for &(block_id
, is_used
) in unsafe_blocks
.iter() {
717 unsafe_used
.insert(block_id
);
719 unsafe_unused
.push(block_id
);
722 // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe
723 // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass.
724 unsafe_unused
.sort_by_cached_key(|hir_id
| tcx
.hir().span(*hir_id
));
726 for &block_id
in &unsafe_unused
{
727 report_unused_unsafe(tcx
, &unsafe_used
, block_id
);
731 fn unsafe_op_in_unsafe_fn_allowed(tcx
: TyCtxt
<'_
>, id
: HirId
) -> bool
{
732 tcx
.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN
, id
).0 == Level
::Allow