rust/compiler/rustc_mir/src/transform/check_unsafety.rs

631 lines
25 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::cast::CastTy;
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;
use crate::const_eval::is_min_const_fn;
pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
body_did: LocalDefId,
const_context: bool,
min_const_fn: bool,
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(
const_context: bool,
min_const_fn: bool,
body: &'a Body<'tcx>,
body_did: LocalDefId,
2019-06-14 00:48:52 +03:00
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
// sanity check
if min_const_fn {
assert!(const_context);
}
Self {
body,
body_did,
const_context,
min_const_fn,
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<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, '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::GeneralAndConstFn,
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::LlvmInlineAsm { .. } => self.require_unsafe(
2019-12-22 17:42:04 -05:00
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UseOfInlineAssembly,
2019-12-22 17:42:04 -05:00
),
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(ref def, ..) => {
match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe(
UnsafetyViolationKind::GeneralAndConstFn,
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);
}
},
// casting pointers to ints is unsafe in const fn because the const evaluator cannot
// possibly know what the result of various operations like `address / 2` would be
// pointers during const evaluation have no integral address, only an abstract one
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty)
2019-12-22 17:42:04 -05:00
if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast =>
{
let operand_ty = operand.ty(self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
2020-01-28 01:58:14 +01:00
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::CastOfPointerToInt,
2019-12-22 17:42:04 -05:00
);
}
_ => {}
}
}
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::GeneralAndConstFn,
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.ty_adt_def().map_or(false, |adt| adt.is_union()) {
// 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::GeneralAndConstFn,
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::GeneralAndConstFn,
UnsafetyViolationDetails::AccessToUnionField,
)
}
}
}
}
}
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
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 {
2019-12-22 17:42:04 -05:00
UnsafetyViolationKind::GeneralAndConstFn
| 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);
}
// only some unsafety is allowed in const fn
if self.min_const_fn {
for violation in violations {
match violation.kind {
2018-11-05 18:06:26 +01:00
// these unsafe things are stable in const fn
2019-12-22 17:42:04 -05:00
UnsafetyViolationKind::GeneralAndConstFn => {}
2018-11-30 18:18:42 +01:00
// these things are forbidden in const fns
UnsafetyViolationKind::General => {
let mut violation = *violation;
2018-11-05 18:06:26 +01:00
// const fns don't need to be backwards compatible and can
// emit these violations as a hard error instead of a backwards
// compat lint
violation.kind = UnsafetyViolationKind::General;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
2019-12-22 17:42:04 -05:00
}
UnsafetyViolationKind::UnsafeFn => bug!(
2020-05-03 23:11:34 +02:00
"`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
),
}
}
}
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::GeneralAndConstFn, 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) {
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::GeneralAndConstFn,
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<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
2020-03-11 12:05:32 +01:00
type Map = intravisit::ErasedMap<'tcx>;
2020-01-07 17:25:33 +01:00
2020-02-09 15:32:00 +01:00
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}
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 id = tcx.hir().local_def_id_to_hir_id(def.did);
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
hir::BodyOwnerKind::Closure => (false, false),
hir::BodyOwnerKind::Fn => {
2020-07-06 23:49:53 +02:00
(tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
}
2019-12-22 17:42:04 -05:00
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
};
let mut checker =
2020-07-06 23:49:53 +02:00
UnsafetyChecker::new(const_context, min_const_fn, 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 {
2019-12-22 17:42:04 -05:00
UnsafetyViolationKind::GeneralAndConstFn | 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
}