Auto merge of #93678 - steffahn:better_unsafe_diagnostics, r=nagisa

Improve `unused_unsafe` lint

I’m going to add some motivation and explanation below, particularly pointing the changes in behavior from this PR.

_Edit:_ Looking for existing issues, looks like this PR fixes #88260.

_Edit2:_ Now also contains code that closes #90776.
This commit is contained in:
bors 2022-02-20 21:15:11 +00:00
commit 45e2c2881d
11 changed files with 3210 additions and 245 deletions

View File

@ -599,13 +599,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
if self.conv == Conv::CCmseNonSecureCall {
// This will probably get ignored on all targets but those supporting the TrustZone-M
// extension (thumbv8m targets).
unsafe {
llvm::AddCallSiteAttrString(
callsite,
llvm::AttributePlace::Function,
cstr::cstr!("cmse_nonsecure_call"),
);
}
llvm::AddCallSiteAttrString(
callsite,
llvm::AttributePlace::Function,
cstr::cstr!("cmse_nonsecure_call"),
);
}
}
}

View File

@ -3,6 +3,10 @@ use rustc_hir::intravisit::nested_filter::NestedFilter;
/// Do not visit nested item-like things, but visit nested things
/// that are inside of an item-like.
///
/// Notably, possible occurrences of bodies in non-item-like things
/// include: closures/generators, inline `const {}` blocks, and
/// constant arguments of types, e.g. in `let _: [(); /* HERE */];`.
///
/// **This is the most common choice.** A very common pattern is
/// to use `visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item

View File

@ -202,6 +202,77 @@ impl<'a> LintDiagnosticBuilder<'a> {
}
}
pub fn explain_lint_level_source<'s>(
sess: &'s Session,
lint: &'static Lint,
level: Level,
src: LintLevelSource,
err: &mut DiagnosticBuilder<'s>,
) {
let name = lint.name_lower();
match src {
LintLevelSource::Default => {
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
let flag = match orig_level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => "-A",
Level::ForceWarn => "--force-warn",
};
let hyphen_case_lint_name = name.replace('_', "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
}
}
LintLevelSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(rationale.as_str());
}
sess.diag_span_note_once(
err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
}
pub fn struct_lint_level<'s, 'd>(
sess: &'s Session,
lint: &'static Lint,
@ -277,69 +348,9 @@ pub fn struct_lint_level<'s, 'd>(
}
}
let name = lint.name_lower();
match src {
LintLevelSource::Default => {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
let flag = match orig_level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => "-A",
Level::ForceWarn => "--force-warn",
};
let hyphen_case_lint_name = name.replace('_', "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
}
}
LintLevelSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(rationale.as_str());
}
sess.diag_span_note_once(
&mut err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
explain_lint_level_source(sess, lint, level, src, &mut err);
let name = lint.name_lower();
let is_force_warn = matches!(level, Level::ForceWarn);
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });

View File

@ -2,7 +2,7 @@
use crate::mir::{Body, Promoted};
use crate::ty::{self, Ty, TyCtxt};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_data_structures::vec_map::VecMap;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
@ -114,13 +114,44 @@ pub struct UnsafetyViolation {
pub details: UnsafetyViolationDetails,
}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnusedUnsafe {
/// `unsafe` block contains no unsafe operations
/// > ``unnecessary `unsafe` block``
Unused,
/// `unsafe` block nested under another (used) `unsafe` block
/// > ``… because it's nested under this `unsafe` block``
InUnsafeBlock(hir::HirId),
/// `unsafe` block nested under `unsafe fn`
/// > ``… because it's nested under this `unsafe fn` ``
///
/// the second HirId here indicates the first usage of the `unsafe` block,
/// which allows retrival of the LintLevelSource for why that operation would
/// have been permitted without the block
InUnsafeFn(hir::HirId, hir::HirId),
}
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UsedUnsafeBlockData {
SomeDisallowedInUnsafeFn,
// the HirId here indicates the first usage of the `unsafe` block
// (i.e. the one that's first encountered in the MIR traversal of the unsafety check)
AllAllowedInUnsafeFn(hir::HirId),
}
#[derive(TyEncodable, TyDecodable, HashStable, Debug)]
pub struct UnsafetyCheckResult {
/// Violations that are propagated *upwards* from this function.
pub violations: Lrc<[UnsafetyViolation]>,
/// `unsafe` blocks in this function, along with whether they are used. This is
/// used for the "unused_unsafe" lint.
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
pub violations: Vec<UnsafetyViolation>,
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
///
/// The keys are the used `unsafe` blocks, the UnusedUnsafeKind indicates whether
/// or not any of the usages happen at a place that doesn't allow `unsafe_op_in_unsafe_fn`.
pub used_unsafe_blocks: FxHashMap<hir::HirId, UsedUnsafeBlockData>,
/// This is `Some` iff the item is not a closure.
pub unused_unsafes: Option<Vec<(hir::HirId, UnusedUnsafe)>>,
}
rustc_index::newtype_index! {

View File

@ -3,8 +3,6 @@ use crate::build::ForGuard::OutsideGuard;
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
use rustc_session::lint::Level;
use rustc_span::Span;
impl<'a, 'tcx> Builder<'a, 'tcx> {
@ -209,28 +207,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.unit()
}
/// If we are changing the safety mode, create a new source scope
/// If we are entering an unsafe block, create a new source scope
fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
let new_unsafety = match safety_mode {
BlockSafety::Safe => None,
BlockSafety::BuiltinUnsafe => Some(Safety::BuiltinUnsafe),
BlockSafety::Safe => return,
BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
BlockSafety::ExplicitUnsafe(hir_id) => {
match self.in_scope_unsafe {
Safety::Safe => {}
// no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585)
Safety::FnUnsafe
if self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0
!= Level::Allow => {}
_ => return,
}
self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
Some(Safety::ExplicitUnsafe(hir_id))
Safety::ExplicitUnsafe(hir_id)
}
};
if let Some(unsafety) = new_unsafety {
self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
}
self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
}
}

View File

@ -1,17 +1,17 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::hir_id::HirId;
use rustc_hir::intravisit;
use rustc_hir::Node;
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_middle::{lint, mir::*};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use std::collections::hash_map;
use std::ops::Bound;
pub struct UnsafetyChecker<'a, 'tcx> {
@ -21,9 +21,12 @@ pub struct UnsafetyChecker<'a, 'tcx> {
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
/// Mark an `unsafe` block as used, so we don't lint it.
used_unsafe: FxHashSet<hir::HirId>,
inherited_blocks: Vec<(hir::HirId, bool)>,
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
///
/// The keys are the used `unsafe` blocks, the UnusedUnsafeKind indicates whether
/// or not any of the usages happen at a place that doesn't allow `unsafe_op_in_unsafe_fn`.
used_unsafe_blocks: FxHashMap<HirId, UsedUnsafeBlockData>,
}
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
@ -40,8 +43,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
source_info: SourceInfo::outermost(body.span),
tcx,
param_env,
used_unsafe: Default::default(),
inherited_blocks: vec![],
used_unsafe_blocks: Default::default(),
}
}
}
@ -123,9 +125,9 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
}
}
&AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
let UnsafetyCheckResult { violations, unsafe_blocks } =
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
self.tcx.unsafety_check_result(def_id.expect_local());
self.register_violations(&violations, &unsafe_blocks);
self.register_violations(violations, used_unsafe_blocks);
}
},
_ => {}
@ -251,61 +253,72 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
.assert_crate_local()
.lint_root;
self.register_violations(
&[UnsafetyViolation { source_info, lint_root, kind, details }],
&[],
[&UnsafetyViolation { source_info, lint_root, kind, details }],
[],
);
}
fn register_violations(
fn register_violations<'a>(
&mut self,
violations: &[UnsafetyViolation],
unsafe_blocks: &[(hir::HirId, bool)],
violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
new_used_unsafe_blocks: impl IntoIterator<Item = (&'a HirId, &'a UsedUnsafeBlockData)>,
) {
use UsedUnsafeBlockData::{AllAllowedInUnsafeFn, SomeDisallowedInUnsafeFn};
let update_entry = |this: &mut Self, hir_id, new_usage| {
match this.used_unsafe_blocks.entry(hir_id) {
hash_map::Entry::Occupied(mut entry) => {
if new_usage == SomeDisallowedInUnsafeFn {
*entry.get_mut() = SomeDisallowedInUnsafeFn;
}
}
hash_map::Entry::Vacant(entry) => {
entry.insert(new_usage);
}
};
};
let safety = self.body.source_scopes[self.source_info.scope]
.local_data
.as_ref()
.assert_crate_local()
.safety;
let within_unsafe = match safety {
match safety {
// `unsafe` blocks are required in safe code
Safety::Safe => {
for violation in violations {
match violation.kind {
UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
}
if !self.violations.contains(violation) {
self.violations.push(*violation)
Safety::Safe => violations.into_iter().for_each(|&violation| {
match violation.kind {
UnsafetyViolationKind::General => {}
UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
}
false
}
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
}),
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
Safety::FnUnsafe => {
for violation in violations {
let mut violation = *violation;
violation.kind = UnsafetyViolationKind::UnsafeFn;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
violation.kind = UnsafetyViolationKind::UnsafeFn;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
false
}
Safety::BuiltinUnsafe => true,
Safety::ExplicitUnsafe(hir_id) => {
// mark unsafe block as used if there are any unsafe operations inside
if !violations.is_empty() {
self.used_unsafe.insert(hir_id);
}
true
}
}),
Safety::BuiltinUnsafe => {}
Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|violation| {
update_entry(
self,
hir_id,
match self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, violation.lint_root).0
{
Level::Allow => AllAllowedInUnsafeFn(violation.lint_root),
_ => SomeDisallowedInUnsafeFn,
},
)
}),
};
self.inherited_blocks.extend(
unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
);
new_used_unsafe_blocks
.into_iter()
.for_each(|(&hir_id, &usage_data)| update_entry(self, hir_id, usage_data));
}
fn check_mut_borrowing_layout_constrained_field(
&mut self,
@ -387,17 +400,64 @@ pub(crate) fn provide(providers: &mut Providers) {
};
}
struct UnusedUnsafeVisitor<'a> {
used_unsafe: &'a FxHashSet<hir::HirId>,
unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
/// Context information for [`UnusedUnsafeVisitor`] traversal,
/// saves (innermost) relevant context
#[derive(Copy, Clone, Debug)]
enum Context {
Safe,
/// in an `unsafe fn`
UnsafeFn(HirId),
/// in a *used* `unsafe` block
/// (i.e. a block without unused-unsafe warning)
UnsafeBlock(HirId),
}
impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> {
struct UnusedUnsafeVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
used_unsafe_blocks: &'a FxHashMap<HirId, UsedUnsafeBlockData>,
context: Context,
unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>,
}
impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
intravisit::walk_block(self, block);
use UsedUnsafeBlockData::{AllAllowedInUnsafeFn, SomeDisallowedInUnsafeFn};
if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) {
(Level::Allow, _) => Some(SomeDisallowedInUnsafeFn),
_ => self.used_unsafe_blocks.get(&block.hir_id).copied(),
};
let unused_unsafe = match (self.context, used) {
(_, None) => UnusedUnsafe::Unused,
(Context::Safe, Some(_))
| (Context::UnsafeFn(_), Some(SomeDisallowedInUnsafeFn)) => {
let previous_context = self.context;
self.context = Context::UnsafeBlock(block.hir_id);
intravisit::walk_block(self, block);
self.context = previous_context;
return;
}
(Context::UnsafeFn(hir_id), Some(AllAllowedInUnsafeFn(lint_root))) => {
UnusedUnsafe::InUnsafeFn(hir_id, lint_root)
}
(Context::UnsafeBlock(hir_id), Some(_)) => UnusedUnsafe::InUnsafeBlock(hir_id),
};
self.unused_unsafes.push((block.hir_id, unused_unsafe));
}
intravisit::walk_block(self, block);
}
fn visit_fn(
&mut self,
fk: intravisit::FnKind<'tcx>,
_fd: &'tcx hir::FnDecl<'tcx>,
b: hir::BodyId,
_s: rustc_span::Span,
_id: HirId,
) {
if matches!(fk, intravisit::FnKind::Closure) {
self.visit_body(self.tcx.hir().body(b))
}
}
}
@ -405,20 +465,38 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> {
fn check_unused_unsafe(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
used_unsafe: &FxHashSet<hir::HirId>,
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));
used_unsafe_blocks: &FxHashMap<HirId, UsedUnsafeBlockData>,
) -> Vec<(HirId, UnusedUnsafe)> {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let body_id = tcx.hir().maybe_body_owned_by(hir_id);
let Some(body_id) = body_id else {
debug!("check_unused_unsafe({:?}) - no body found", def_id);
return;
return vec![];
};
let body = tcx.hir().body(body_id);
debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
let context = match tcx.hir().fn_sig_by_hir_id(hir_id) {
Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn(hir_id),
_ => Context::Safe,
};
debug!(
"check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})",
def_id, body, context, used_unsafe_blocks
);
let mut unused_unsafes = vec![];
let mut visitor = UnusedUnsafeVisitor {
tcx,
used_unsafe_blocks,
context,
unused_unsafes: &mut unused_unsafes,
};
intravisit::Visitor::visit_body(&mut visitor, body);
unused_unsafes
}
fn unsafety_check_result<'tcx>(
@ -436,56 +514,52 @@ fn unsafety_check_result<'tcx>(
let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env);
checker.visit_body(&body);
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
let unused_unsafes = (!tcx.is_closure(def.did.to_def_id()))
.then(|| check_unused_unsafe(tcx, def.did, &checker.used_unsafe_blocks));
tcx.arena.alloc(UnsafetyCheckResult {
violations: checker.violations.into(),
unsafe_blocks: checker.inherited_blocks.into(),
violations: checker.violations,
used_unsafe_blocks: checker.used_unsafe_blocks,
unused_unsafes,
})
}
/// Returns the `HirId` for an enclosing scope that is also `unsafe`.
fn is_enclosed(
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))
} else if let Some(Node::Item(&hir::Item {
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))
} else {
None
}
} else {
is_enclosed(tcx, used_unsafe, parent_id, unsafe_op_in_unsafe_fn_allowed)
}
} else {
None
}
}
fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
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(
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` {}", kind),
);
match kind {
UnusedUnsafe::Unused => {}
UnusedUnsafe::InUnsafeBlock(id) => {
db.span_label(
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` block"),
);
}
UnusedUnsafe::InUnsafeFn(id, usage_lint_root) => {
db.span_label(
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` fn"),
)
.note(
"this `unsafe` block does contain unsafe operations, \
but those are already allowed in an `unsafe fn`",
);
let (level, source) =
tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, usage_lint_root);
assert_eq!(level, Level::Allow);
lint::explain_lint_level_source(
tcx.sess,
UNSAFE_OP_IN_UNSAFE_FN,
Level::Allow,
source,
&mut db,
);
}
}
db.emit();
});
}
@ -498,7 +572,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
return;
}
let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
let (description, note) = details.description_and_note();
@ -539,20 +613,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
}
}
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));
for &block_id in &unsafe_unused {
report_unused_unsafe(tcx, &unsafe_used, block_id);
for &(block_id, kind) in unused_unsafes.as_ref().unwrap() {
report_unused_unsafe(tcx, kind, block_id);
}
}

View File

@ -0,0 +1,61 @@
// FIXME: This file is tracking old lint behavior that's still unchanged in the
// unstable -Zthir-unsafeck implementation. See lint-unused-unsafe.rs for more details.
//
// Exercise the unused_unsafe attribute in some positive and negative cases
// compile-flags: -Zthir-unsafeck
#![allow(dead_code)]
#![deny(unused_unsafe)]
mod foo {
extern "C" {
pub fn bar();
}
}
fn callback<T, F>(_f: F) -> T where F: FnOnce() -> T { panic!() }
unsafe fn unsf() {}
fn bad1() { unsafe {} } //~ ERROR: unnecessary `unsafe` block
fn bad2() { unsafe { bad1() } } //~ ERROR: unnecessary `unsafe` block
unsafe fn bad3() { unsafe {} } //~ ERROR: unnecessary `unsafe` block
fn bad4() { unsafe { callback(||{}) } } //~ ERROR: unnecessary `unsafe` block
unsafe fn bad5() { unsafe { unsf() } } //~ ERROR: unnecessary `unsafe` block
fn bad6() {
unsafe { // don't put the warning here
unsafe { //~ ERROR: unnecessary `unsafe` block
unsf()
}
}
}
unsafe fn bad7() {
unsafe { //~ ERROR: unnecessary `unsafe` block
unsafe { //~ ERROR: unnecessary `unsafe` block
unsf()
}
}
}
unsafe fn good0() { unsf() }
fn good1() { unsafe { unsf() } }
fn good2() {
/* bug uncovered when implementing warning about unused unsafe blocks. Be
sure that when purity is inherited that the source of the unsafe-ness
is tracked correctly */
unsafe {
unsafe fn what() -> Vec<String> { panic!() }
callback(|| {
what();
});
}
}
unsafe fn good3() { foo::bar() }
fn good4() { unsafe { foo::bar() } }
#[allow(unused_unsafe)] fn allowed() { unsafe {} }
fn main() {}

View File

@ -1,23 +1,23 @@
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:19:13
--> $DIR/lint-unused-unsafe-thir.rs:21:13
|
LL | fn bad1() { unsafe {} }
| ^^^^^^ unnecessary `unsafe` block
|
note: the lint level is defined here
--> $DIR/lint-unused-unsafe.rs:7:9
--> $DIR/lint-unused-unsafe-thir.rs:9:9
|
LL | #![deny(unused_unsafe)]
| ^^^^^^^^^^^^^
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:20:13
--> $DIR/lint-unused-unsafe-thir.rs:22:13
|
LL | fn bad2() { unsafe { bad1() } }
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:21:20
--> $DIR/lint-unused-unsafe-thir.rs:23:20
|
LL | unsafe fn bad3() { unsafe {} }
| ---------------- ^^^^^^ unnecessary `unsafe` block
@ -25,13 +25,13 @@ LL | unsafe fn bad3() { unsafe {} }
| because it's nested under this `unsafe` fn
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:22:13
--> $DIR/lint-unused-unsafe-thir.rs:24:13
|
LL | fn bad4() { unsafe { callback(||{}) } }
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:23:20
--> $DIR/lint-unused-unsafe-thir.rs:25:20
|
LL | unsafe fn bad5() { unsafe { unsf() } }
| ---------------- ^^^^^^ unnecessary `unsafe` block
@ -39,7 +39,7 @@ LL | unsafe fn bad5() { unsafe { unsf() } }
| because it's nested under this `unsafe` fn
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:26:9
--> $DIR/lint-unused-unsafe-thir.rs:28:9
|
LL | unsafe { // don't put the warning here
| ------ because it's nested under this `unsafe` block
@ -47,7 +47,7 @@ LL | unsafe {
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:33:9
--> $DIR/lint-unused-unsafe-thir.rs:35:9
|
LL | unsafe {
| ------ because it's nested under this `unsafe` block
@ -55,7 +55,7 @@ LL | unsafe {
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/lint-unused-unsafe.rs:32:5
--> $DIR/lint-unused-unsafe-thir.rs:34:5
|
LL | unsafe fn bad7() {
| ---------------- because it's nested under this `unsafe` fn

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -76,12 +76,10 @@ LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:14
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:5
|
LL | unsafe { unsafe { unsf() } }
| ------ ^^^^^^ unnecessary `unsafe` block
| |
| because it's nested under this `unsafe` block
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
@ -91,6 +89,13 @@ LL | unsafe fn allow_level() {
...
LL | unsafe { unsf() }
| ^^^^^^ unnecessary `unsafe` block
|
= note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
note: the lint level is defined here
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:51:9
|
LL | #[allow(unsafe_op_in_unsafe_fn)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: unnecessary `unsafe` block
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
@ -100,6 +105,13 @@ LL | unsafe fn nested_allow_level() {
...
LL | unsafe { unsf() }
| ^^^^^^ unnecessary `unsafe` block
|
= note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
note: the lint level is defined here
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:63:13
|
LL | #[allow(unsafe_op_in_unsafe_fn)]
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0133]: call to unsafe function is unsafe and requires unsafe block
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5