rust/compiler/rustc_mir_transform/src/check_unsafety.rs

565 lines
22 KiB
Rust
Raw Normal View History

use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
2020-05-13 23:43:21 +02:00
use rustc_hir::hir_id::HirId;
use rustc_hir::intravisit;
use rustc_hir::Node;
2020-03-29 17:19:48 +02:00
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
2020-05-13 23:43:21 +02:00
use rustc_session::lint::Level;
use std::ops::Bound;
pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
body_did: LocalDefId,
violations: Vec<UnsafetyViolation>,
source_info: SourceInfo,
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
2019-02-08 14:53:55 +01:00
/// Mark an `unsafe` block as used, so we don't lint it.
2019-02-22 15:48:14 +01:00
used_unsafe: FxHashSet<hir::HirId>,
inherited_blocks: Vec<(hir::HirId, bool)>,
}
2019-06-11 12:47:30 +03:00
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn new(
body: &'a Body<'tcx>,
body_did: LocalDefId,
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
Self {
body,
body_did,
violations: vec![],
2020-05-06 10:30:11 +10:00
source_info: SourceInfo::outermost(body.span),
tcx,
param_env,
used_unsafe: Default::default(),
inherited_blocks: vec![],
}
}
}
impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
2019-12-22 17:42:04 -05:00
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
self.source_info = terminator.source_info;
match terminator.kind {
2019-12-22 17:42:04 -05:00
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
2020-06-02 09:15:24 +02:00
| TerminatorKind::FalseEdge { .. }
2019-12-22 17:42:04 -05:00
| TerminatorKind::FalseUnwind { .. } => {
// safe (at least as emitted during MIR construction)
}
TerminatorKind::Call { ref func, .. } => {
let func_ty = func.ty(self.body, self.tcx);
let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() {
2019-12-22 17:42:04 -05:00
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToUnsafeFunction,
2019-12-22 17:42:04 -05:00
)
}
2020-08-03 00:49:11 +02:00
if let ty::FnDef(func_id, _) = func_ty.kind() {
self.check_target_features(*func_id);
}
}
2020-02-14 18:17:50 +00:00
TerminatorKind::InlineAsm { .. } => self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UseOfInlineAssembly,
2020-02-14 18:17:50 +00:00
),
}
self.super_terminator(terminator, location);
}
2019-12-22 17:42:04 -05:00
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
self.source_info = statement.source_info;
match statement.kind {
2019-12-22 17:42:04 -05:00
StatementKind::Assign(..)
| StatementKind::FakeRead(..)
| StatementKind::SetDiscriminant { .. }
| StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
| StatementKind::Retag { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
2019-12-22 17:42:04 -05:00
| StatementKind::Nop => {
// safe (at least as emitted during MIR construction)
}
StatementKind::CopyNonOverlapping(..) => unreachable!(),
}
self.super_statement(statement, location);
}
2019-12-22 17:42:04 -05:00
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
match rvalue {
2019-12-22 17:42:04 -05:00
Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
&AggregateKind::Array(..) | &AggregateKind::Tuple => {}
&AggregateKind::Adt(adt_did, ..) => {
match self.tcx.layout_scalar_valid_range(adt_did) {
2019-12-22 17:42:04 -05:00
(Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::InitializingTypeWith,
2019-12-22 17:42:04 -05:00
),
}
}
2019-12-22 17:42:04 -05:00
&AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
let UnsafetyCheckResult { violations, unsafe_blocks } =
self.tcx.unsafety_check_result(def_id.expect_local());
2019-12-22 17:42:04 -05:00
self.register_violations(&violations, &unsafe_blocks);
}
},
2019-12-22 17:42:04 -05:00
_ => {}
}
self.super_rvalue(rvalue, location);
}
2019-12-22 17:42:04 -05:00
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
// On types with `scalar_valid_range`, prevent
// * `&mut x.field`
// * `x.field = y;`
// * `&x.field` if `field`'s type has interior mutability
// because either of these would allow modifying the layout constrained field and
// insert values that violate the layout constraints.
if context.is_mutating_use() || context.is_borrow() {
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
}
2020-11-21 13:20:34 +01:00
// Some checks below need the extra metainfo of the local declaration.
let decl = &self.body.local_decls[place.local];
// Check the base local: it might be an unsafe-to-access static. We only check derefs of the
// temporary holding the static pointer to avoid duplicate errors
// <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
// If the projection root is an artifical local that we introduced when
// desugaring `static`, give a more specific error message
// (avoid the general "raw pointer" clause below, that would only be confusing).
if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
if self.tcx.is_mutable_static(def_id) {
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UseOfMutableStatic,
);
return;
} else if self.tcx.is_foreign_item(def_id) {
2020-05-13 23:43:21 +02:00
self.require_unsafe(
2020-11-21 13:20:34 +01:00
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UseOfExternStatic,
2020-05-13 23:43:21 +02:00
);
2020-11-21 13:20:34 +01:00
return;
2020-05-13 23:43:21 +02:00
}
}
2020-11-21 13:20:34 +01:00
}
// Check for raw pointer `Deref`.
for (base, proj) in place.iter_projections() {
if proj == ProjectionElem::Deref {
let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
self.require_unsafe(
UnsafetyViolationKind::General,
2020-11-21 13:20:34 +01:00
UnsafetyViolationDetails::DerefOfRawPointer,
)
}
}
2020-11-21 13:20:34 +01:00
}
// Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
// whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
let mut saw_deref = false;
for (base, proj) in place.iter_projections().rev() {
if proj == ProjectionElem::Deref {
saw_deref = true;
continue;
}
let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_union() {
2020-11-21 13:20:34 +01:00
// If we did not hit a `Deref` yet and the overall place use is an assignment, the
// rules are different.
let assign_to_field = !saw_deref
&& matches!(
2020-10-26 19:35:01 +01:00
context,
PlaceContext::MutatingUse(
MutatingUseContext::Store
| MutatingUseContext::Drop
| MutatingUseContext::AsmOutput
)
);
2020-11-21 13:20:34 +01:00
// If this is just an assignment, determine if the assigned type needs dropping.
if assign_to_field {
// We have to check the actual type of the assignment, as that determines if the
// old value is being dropped.
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
let manually_drop = assigned_ty
.ty_adt_def()
.map_or(false, |adt_def| adt_def.is_manually_drop());
let nodrop = manually_drop
|| assigned_ty.is_copy_modulo_regions(
self.tcx.at(self.source_info.span),
self.param_env,
);
if !nodrop {
self.require_unsafe(
UnsafetyViolationKind::General,
2020-11-21 13:20:34 +01:00
UnsafetyViolationDetails::AssignToDroppingUnionField,
);
} else {
// write to non-drop union field, safe
}
2020-11-21 13:20:34 +01:00
} else {
self.require_unsafe(
UnsafetyViolationKind::General,
2020-11-21 13:20:34 +01:00
UnsafetyViolationDetails::AccessToUnionField,
)
}
}
}
}
}
impl<'tcx> UnsafetyChecker<'_, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
// Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
let source_info = self.source_info;
2020-05-13 23:43:21 +02:00
let lint_root = self.body.source_scopes[self.source_info.scope]
.local_data
.as_ref()
.assert_crate_local()
.lint_root;
2019-12-22 17:42:04 -05:00
self.register_violations(
&[UnsafetyViolation { source_info, lint_root, kind, details }],
2019-12-22 17:42:04 -05:00
&[],
);
}
2019-12-22 17:42:04 -05:00
fn register_violations(
&mut self,
violations: &[UnsafetyViolation],
unsafe_blocks: &[(hir::HirId, bool)],
) {
let safety = self.body.source_scopes[self.source_info.scope]
.local_data
.as_ref()
.assert_crate_local()
.safety;
2018-12-11 09:07:03 +01:00
let within_unsafe = match safety {
// `unsafe` blocks are required in safe code
2018-12-11 09:07:03 +01:00
Safety::Safe => {
for violation in violations {
2018-11-05 18:06:26 +01:00
match violation.kind {
UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => {
2020-05-03 23:11:34 +02:00
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
}
if !self.violations.contains(violation) {
self.violations.push(*violation)
}
2020-05-03 23:11:34 +02:00
}
false
}
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
Safety::FnUnsafe => {
2020-05-03 23:11:34 +02:00
for violation in violations {
let mut violation = *violation;
violation.kind = UnsafetyViolationKind::UnsafeFn;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
}
false
}
Safety::BuiltinUnsafe => true,
2019-02-22 15:48:14 +01:00
Safety::ExplicitUnsafe(hir_id) => {
// mark unsafe block as used if there are any unsafe operations inside
if !violations.is_empty() {
2019-02-22 15:48:14 +01:00
self.used_unsafe.insert(hir_id);
}
true
}
};
2019-12-22 17:42:04 -05:00
self.inherited_blocks.extend(
unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
);
}
fn check_mut_borrowing_layout_constrained_field(
&mut self,
place: Place<'tcx>,
2018-11-05 13:26:07 +01:00
is_mut_use: bool,
) {
for (place_base, elem) in place.iter_projections().rev() {
match elem {
// Modifications behind a dereference don't affect the value of
// the pointer.
ProjectionElem::Deref => return,
ProjectionElem::Field(..) => {
let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
2020-08-03 00:49:11 +02:00
if let ty::Adt(def, _) = ty.kind() {
if self.tcx.layout_scalar_valid_range(def.did)
!= (Bound::Unbounded, Bound::Unbounded)
{
let details = if is_mut_use {
UnsafetyViolationDetails::MutationOfLayoutConstrainedField
// Check `is_freeze` as late as possible to avoid cycle errors
// with opaque types.
} else if !place
.ty(self.body, self.tcx)
.ty
.is_freeze(self.tcx.at(self.source_info.span), self.param_env)
{
UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
} else {
continue;
};
self.require_unsafe(UnsafetyViolationKind::General, details);
}
}
}
_ => {}
}
}
}
/// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
/// the called function has target features the calling function hasn't.
fn check_target_features(&mut self, func_did: DefId) {
rustc: Allow safe #[target_feature] on wasm This commit updates the compiler's handling of the `#[target_feature]` attribute when applied to functions on WebAssembly-based targets. The compiler in general requires that any functions with `#[target_feature]` are marked as `unsafe` as well, but this commit relaxes the restriction for WebAssembly targets where the attribute can be applied to safe functions as well. The reason this is done is that the motivation for this feature of the compiler is not applicable for WebAssembly targets. In general the `#[target_feature]` attribute is used to enhance target CPU features enabled beyond the basic level for the rest of the compilation. If done improperly this means that your program could execute an instruction that the CPU you happen to be running on does not understand. This is considered undefined behavior where it is unknown what will happen (e.g. it's not a deterministic `SIGILL`). For WebAssembly, however, the target is different. It is not possible for a running WebAssembly program to execute an instruction that the engine does not understand. If this were the case then the program would not have validated in the first place and would not run at all. Even if this were allowed in some hypothetical future where engines have some form of runtime feature detection (which they do not right now) any implementation of such a feature would generate a trap if a module attempts to execute an instruction the module does not understand. This deterministic trap behavior would still not fall into the category of undefined behavior because the trap is deterministic. For these reasons the `#[target_feature]` attribute is now allowed on safe functions, but only for WebAssembly targets. This notably enables the wasm-SIMD intrinsics proposed for stabilization in #74372 to be marked as safe generally instead of today where they're all `unsafe` due to the historical implementation of `#[target_feature]` in the compiler.
2021-05-06 07:50:40 -07:00
// Unsafety isn't required on wasm targets. For more information see
// the corresponding check in typeck/src/collect.rs
if self.tcx.sess.target.options.is_like_wasm {
return;
}
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
// Is `callee_features` a subset of `calling_features`?
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToFunctionWith,
)
}
}
}
pub(crate) fn provide(providers: &mut Providers) {
2020-07-06 23:49:53 +02:00
*providers = Providers {
unsafety_check_result: |tcx, def_id| {
2020-07-21 22:54:18 +02:00
if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
tcx.unsafety_check_result_for_const_arg(def)
} else {
unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id))
}
2020-07-06 23:49:53 +02:00
},
2020-07-15 11:26:26 +02:00
unsafety_check_result_for_const_arg: |tcx, (did, param_did)| {
2020-07-15 10:50:54 +02:00
unsafety_check_result(
tcx,
ty::WithOptConstParam { did, const_param_did: Some(param_did) },
)
2020-07-06 23:49:53 +02:00
},
..*providers
};
}
struct UnusedUnsafeVisitor<'a> {
2019-02-22 15:48:14 +01:00
used_unsafe: &'a FxHashSet<hir::HirId>,
unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
}
impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> {
2019-11-30 15:08:22 +01:00
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
intravisit::walk_block(self, block);
2020-01-05 01:50:05 +01:00
if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
2019-02-22 15:48:14 +01:00
self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
}
}
}
2019-06-21 18:12:39 +02:00
fn check_unused_unsafe(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
used_unsafe: &FxHashSet<hir::HirId>,
2019-06-21 18:12:39 +02:00
unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
) {
let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().local_def_id_to_hir_id(def_id));
let body_id = match body_id {
Some(body) => body,
None => {
debug!("check_unused_unsafe({:?}) - no body found", def_id);
2019-12-22 17:42:04 -05:00
return;
}
};
let body = tcx.hir().body(body_id);
2019-12-22 17:42:04 -05:00
debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
2019-12-22 17:42:04 -05:00
let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
intravisit::Visitor::visit_body(&mut visitor, body);
}
2020-07-06 23:49:53 +02:00
fn unsafety_check_result<'tcx>(
tcx: TyCtxt<'tcx>,
2020-07-15 10:50:54 +02:00
def: ty::WithOptConstParam<LocalDefId>,
2020-07-06 23:49:53 +02:00
) -> &'tcx UnsafetyCheckResult {
debug!("unsafety_violations({:?})", def);
// N.B., this borrow is valid because all the consumers of
// `mir_built` force this.
2020-07-06 23:49:53 +02:00
let body = &tcx.mir_built(def).borrow();
2020-07-06 23:49:53 +02:00
let param_env = tcx.param_env(def.did);
let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env);
checker.visit_body(&body);
2020-07-06 23:49:53 +02:00
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
tcx.arena.alloc(UnsafetyCheckResult {
violations: checker.violations.into(),
2019-12-22 17:42:04 -05:00
unsafe_blocks: checker.inherited_blocks.into(),
2020-07-06 23:49:53 +02:00
})
}
2019-02-22 15:48:14 +01:00
/// Returns the `HirId` for an enclosing scope that is also `unsafe`.
fn is_enclosed(
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'_>,
used_unsafe: &FxHashSet<hir::HirId>,
id: hir::HirId,
unsafe_op_in_unsafe_fn_allowed: bool,
) -> Option<(&'static str, hir::HirId)> {
let parent_id = tcx.hir().get_parent_node(id);
if parent_id != id {
if used_unsafe.contains(&parent_id) {
Some(("block", parent_id))
2018-08-25 15:56:16 +01:00
} else if let Some(Node::Item(&hir::Item {
2019-12-22 17:42:04 -05:00
kind: hir::ItemKind::Fn(ref sig, _, _), ..
})) = tcx.hir().find(parent_id)
{
if sig.header.unsafety == hir::Unsafety::Unsafe && unsafe_op_in_unsafe_fn_allowed {
Some(("fn", parent_id))
2020-05-03 23:11:34 +02:00
} else {
None
}
} else {
is_enclosed(tcx, used_unsafe, parent_id, unsafe_op_in_unsafe_fn_allowed)
}
} else {
None
}
}
2019-06-14 00:48:52 +03:00
fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
2020-03-09 11:42:37 -07:00
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
2020-02-06 01:27:46 +10:00
let msg = "unnecessary `unsafe` block";
let mut db = lint.build(msg);
db.span_label(span, msg);
if let Some((kind, id)) =
is_enclosed(tcx, used_unsafe, id, unsafe_op_in_unsafe_fn_allowed(tcx, id))
{
db.span_label(
2020-03-09 11:42:37 -07:00
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` {}", kind),
);
}
db.emit();
});
}
2020-05-17 17:02:57 +02:00
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug!("check_unsafety({:?})", def_id);
2017-11-11 02:20:53 +09:00
// closures are handled by their parent fn.
2020-05-17 17:02:57 +02:00
if tcx.is_closure(def_id.to_def_id()) {
2017-11-11 02:20:53 +09:00
return;
}
2020-05-17 17:02:57 +02:00
let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
let (description, note) = details.description_and_note();
// Report an error.
2020-05-13 23:43:21 +02:00
let unsafe_fn_msg =
2020-05-19 00:18:50 +02:00
if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
2020-05-13 23:43:21 +02:00
match kind {
UnsafetyViolationKind::General => {
2020-05-03 23:11:34 +02:00
// once
struct_span_err!(
2019-12-22 17:42:04 -05:00
tcx.sess,
source_info.span,
E0133,
2020-05-13 23:43:21 +02:00
"{} is unsafe and requires unsafe{} block",
2020-05-03 23:11:34 +02:00
description,
2020-05-13 23:43:21 +02:00
unsafe_fn_msg,
2019-12-22 17:42:04 -05:00
)
.span_label(source_info.span, description)
.note(note)
2019-12-22 17:42:04 -05:00
.emit();
}
2020-05-13 23:43:21 +02:00
UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
2020-05-03 23:11:34 +02:00
UNSAFE_OP_IN_UNSAFE_FN,
2020-05-13 23:43:21 +02:00
lint_root,
2020-05-03 23:11:34 +02:00
source_info.span,
|lint| {
lint.build(&format!(
"{} is unsafe and requires unsafe block (error E0133)",
description,
2020-05-03 23:11:34 +02:00
))
.span_label(source_info.span, description)
.note(note)
2020-05-03 23:11:34 +02:00
.emit();
},
),
}
}
2020-04-16 14:24:52 +02:00
let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default();
for &(block_id, is_used) in unsafe_blocks.iter() {
if is_used {
unsafe_used.insert(block_id);
} else {
unsafe_unused.push(block_id);
}
}
// The unused unsafe blocks might not be in source order; sort them so that the unused unsafe
// error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass.
unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id));
2020-04-16 14:24:52 +02:00
for &block_id in &unsafe_unused {
report_unused_unsafe(tcx, &unsafe_used, block_id);
}
}
2020-05-13 23:43:21 +02:00
fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
}