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:
commit
45e2c2881d
11 changed files with 3210 additions and 245 deletions
|
@ -599,13 +599,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
if self.conv == Conv::CCmseNonSecureCall {
|
if self.conv == Conv::CCmseNonSecureCall {
|
||||||
// This will probably get ignored on all targets but those supporting the TrustZone-M
|
// This will probably get ignored on all targets but those supporting the TrustZone-M
|
||||||
// extension (thumbv8m targets).
|
// extension (thumbv8m targets).
|
||||||
unsafe {
|
llvm::AddCallSiteAttrString(
|
||||||
llvm::AddCallSiteAttrString(
|
callsite,
|
||||||
callsite,
|
llvm::AttributePlace::Function,
|
||||||
llvm::AttributePlace::Function,
|
cstr::cstr!("cmse_nonsecure_call"),
|
||||||
cstr::cstr!("cmse_nonsecure_call"),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@ use rustc_hir::intravisit::nested_filter::NestedFilter;
|
||||||
/// Do not visit nested item-like things, but visit nested things
|
/// Do not visit nested item-like things, but visit nested things
|
||||||
/// that are inside of an item-like.
|
/// 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
|
/// **This is the most common choice.** A very common pattern is
|
||||||
/// to use `visit_all_item_likes()` as an outer loop,
|
/// to use `visit_all_item_likes()` as an outer loop,
|
||||||
/// and to have the visitor that visits the contents of each item
|
/// and to have the visitor that visits the contents of each item
|
||||||
|
|
|
@ -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>(
|
pub fn struct_lint_level<'s, 'd>(
|
||||||
sess: &'s Session,
|
sess: &'s Session,
|
||||||
lint: &'static Lint,
|
lint: &'static Lint,
|
||||||
|
@ -277,69 +348,9 @@ pub fn struct_lint_level<'s, 'd>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = lint.name_lower();
|
explain_lint_level_source(sess, lint, level, src, &mut err);
|
||||||
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
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let name = lint.name_lower();
|
||||||
let is_force_warn = matches!(level, Level::ForceWarn);
|
let is_force_warn = matches!(level, Level::ForceWarn);
|
||||||
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
|
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::mir::{Body, Promoted};
|
use crate::mir::{Body, Promoted};
|
||||||
use crate::ty::{self, Ty, TyCtxt};
|
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_data_structures::vec_map::VecMap;
|
||||||
use rustc_errors::ErrorReported;
|
use rustc_errors::ErrorReported;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -114,13 +114,44 @@ pub struct UnsafetyViolation {
|
||||||
pub details: UnsafetyViolationDetails,
|
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 {
|
pub struct UnsafetyCheckResult {
|
||||||
/// Violations that are propagated *upwards* from this function.
|
/// Violations that are propagated *upwards* from this function.
|
||||||
pub violations: Lrc<[UnsafetyViolation]>,
|
pub violations: Vec<UnsafetyViolation>,
|
||||||
/// `unsafe` blocks in this function, along with whether they are used. This is
|
|
||||||
/// used for the "unused_unsafe" lint.
|
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
|
||||||
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
|
///
|
||||||
|
/// 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! {
|
rustc_index::newtype_index! {
|
||||||
|
|
|
@ -3,8 +3,6 @@ use crate::build::ForGuard::OutsideGuard;
|
||||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::thir::*;
|
use rustc_middle::thir::*;
|
||||||
use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN;
|
|
||||||
use rustc_session::lint::Level;
|
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
@ -209,28 +207,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
block.unit()
|
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) {
|
fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
|
||||||
debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
|
debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
|
||||||
let new_unsafety = match safety_mode {
|
let new_unsafety = match safety_mode {
|
||||||
BlockSafety::Safe => None,
|
BlockSafety::Safe => return,
|
||||||
BlockSafety::BuiltinUnsafe => Some(Safety::BuiltinUnsafe),
|
BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
|
||||||
BlockSafety::ExplicitUnsafe(hir_id) => {
|
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);
|
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(new_unsafety));
|
||||||
self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::hir_id::HirId;
|
use rustc_hir::hir_id::HirId;
|
||||||
use rustc_hir::intravisit;
|
use rustc_hir::intravisit;
|
||||||
use rustc_hir::Node;
|
|
||||||
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
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::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
|
||||||
use rustc_session::lint::Level;
|
use rustc_session::lint::Level;
|
||||||
|
|
||||||
|
use std::collections::hash_map;
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
|
|
||||||
pub struct UnsafetyChecker<'a, 'tcx> {
|
pub struct UnsafetyChecker<'a, 'tcx> {
|
||||||
|
@ -21,9 +21,12 @@ pub struct UnsafetyChecker<'a, 'tcx> {
|
||||||
source_info: SourceInfo,
|
source_info: SourceInfo,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
/// Mark an `unsafe` block as used, so we don't lint it.
|
|
||||||
used_unsafe: FxHashSet<hir::HirId>,
|
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
|
||||||
inherited_blocks: Vec<(hir::HirId, bool)>,
|
///
|
||||||
|
/// 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> {
|
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||||
|
@ -40,8 +43,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||||
source_info: SourceInfo::outermost(body.span),
|
source_info: SourceInfo::outermost(body.span),
|
||||||
tcx,
|
tcx,
|
||||||
param_env,
|
param_env,
|
||||||
used_unsafe: Default::default(),
|
used_unsafe_blocks: Default::default(),
|
||||||
inherited_blocks: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,9 +125,9 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
|
&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.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()
|
.assert_crate_local()
|
||||||
.lint_root;
|
.lint_root;
|
||||||
self.register_violations(
|
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,
|
&mut self,
|
||||||
violations: &[UnsafetyViolation],
|
violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
|
||||||
unsafe_blocks: &[(hir::HirId, bool)],
|
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]
|
let safety = self.body.source_scopes[self.source_info.scope]
|
||||||
.local_data
|
.local_data
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.assert_crate_local()
|
.assert_crate_local()
|
||||||
.safety;
|
.safety;
|
||||||
let within_unsafe = match safety {
|
match safety {
|
||||||
// `unsafe` blocks are required in safe code
|
// `unsafe` blocks are required in safe code
|
||||||
Safety::Safe => {
|
Safety::Safe => violations.into_iter().for_each(|&violation| {
|
||||||
for violation in violations {
|
match violation.kind {
|
||||||
match violation.kind {
|
UnsafetyViolationKind::General => {}
|
||||||
UnsafetyViolationKind::General => {}
|
UnsafetyViolationKind::UnsafeFn => {
|
||||||
UnsafetyViolationKind::UnsafeFn => {
|
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
|
||||||
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.violations.contains(violation) {
|
|
||||||
self.violations.push(*violation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
if !self.violations.contains(&violation) {
|
||||||
}
|
self.violations.push(violation)
|
||||||
|
}
|
||||||
|
}),
|
||||||
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
|
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
|
||||||
Safety::FnUnsafe => {
|
Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
|
||||||
for violation in violations {
|
violation.kind = UnsafetyViolationKind::UnsafeFn;
|
||||||
let mut violation = *violation;
|
if !self.violations.contains(&violation) {
|
||||||
|
self.violations.push(violation)
|
||||||
violation.kind = UnsafetyViolationKind::UnsafeFn;
|
|
||||||
if !self.violations.contains(&violation) {
|
|
||||||
self.violations.push(violation)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
false
|
}),
|
||||||
}
|
Safety::BuiltinUnsafe => {}
|
||||||
Safety::BuiltinUnsafe => true,
|
Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|violation| {
|
||||||
Safety::ExplicitUnsafe(hir_id) => {
|
update_entry(
|
||||||
// mark unsafe block as used if there are any unsafe operations inside
|
self,
|
||||||
if !violations.is_empty() {
|
hir_id,
|
||||||
self.used_unsafe.insert(hir_id);
|
match self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, violation.lint_root).0
|
||||||
}
|
{
|
||||||
true
|
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(
|
fn check_mut_borrowing_layout_constrained_field(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -387,17 +400,64 @@ pub(crate) fn provide(providers: &mut Providers) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnusedUnsafeVisitor<'a> {
|
/// Context information for [`UnusedUnsafeVisitor`] traversal,
|
||||||
used_unsafe: &'a FxHashSet<hir::HirId>,
|
/// saves (innermost) relevant context
|
||||||
unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
|
#[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>) {
|
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 {
|
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(
|
fn check_unused_unsafe(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
def_id: LocalDefId,
|
def_id: LocalDefId,
|
||||||
used_unsafe: &FxHashSet<hir::HirId>,
|
used_unsafe_blocks: &FxHashMap<HirId, UsedUnsafeBlockData>,
|
||||||
unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
|
) -> 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(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 {
|
let Some(body_id) = body_id else {
|
||||||
debug!("check_unused_unsafe({:?}) - no body found", def_id);
|
debug!("check_unused_unsafe({:?}) - no body found", def_id);
|
||||||
return;
|
return vec![];
|
||||||
};
|
};
|
||||||
let body = tcx.hir().body(body_id);
|
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);
|
intravisit::Visitor::visit_body(&mut visitor, body);
|
||||||
|
|
||||||
|
unused_unsafes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsafety_check_result<'tcx>(
|
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);
|
let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env);
|
||||||
checker.visit_body(&body);
|
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 {
|
tcx.arena.alloc(UnsafetyCheckResult {
|
||||||
violations: checker.violations.into(),
|
violations: checker.violations,
|
||||||
unsafe_blocks: checker.inherited_blocks.into(),
|
used_unsafe_blocks: checker.used_unsafe_blocks,
|
||||||
|
unused_unsafes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `HirId` for an enclosing scope that is also `unsafe`.
|
fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
|
||||||
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) {
|
|
||||||
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
|
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
|
||||||
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
|
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
|
||||||
let msg = "unnecessary `unsafe` block";
|
let msg = "unnecessary `unsafe` block";
|
||||||
let mut db = lint.build(msg);
|
let mut db = lint.build(msg);
|
||||||
db.span_label(span, msg);
|
db.span_label(span, msg);
|
||||||
if let Some((kind, id)) =
|
match kind {
|
||||||
is_enclosed(tcx, used_unsafe, id, unsafe_op_in_unsafe_fn_allowed(tcx, id))
|
UnusedUnsafe::Unused => {}
|
||||||
{
|
UnusedUnsafe::InUnsafeBlock(id) => {
|
||||||
db.span_label(
|
db.span_label(
|
||||||
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
|
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
|
||||||
format!("because it's nested under this `unsafe` {}", kind),
|
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();
|
db.emit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -498,7 +572,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||||
return;
|
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() {
|
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
|
||||||
let (description, note) = details.description_and_note();
|
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, kind) in unused_unsafes.as_ref().unwrap() {
|
||||||
for &(block_id, is_used) in unsafe_blocks.iter() {
|
report_unused_unsafe(tcx, kind, block_id);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
61
src/test/ui/span/lint-unused-unsafe-thir.rs
Normal file
61
src/test/ui/span/lint-unused-unsafe-thir.rs
Normal 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() {}
|
|
@ -1,23 +1,23 @@
|
||||||
error: unnecessary `unsafe` block
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/lint-unused-unsafe.rs:19:13
|
--> $DIR/lint-unused-unsafe-thir.rs:21:13
|
||||||
|
|
|
|
||||||
LL | fn bad1() { unsafe {} }
|
LL | fn bad1() { unsafe {} }
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
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)]
|
LL | #![deny(unused_unsafe)]
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unnecessary `unsafe` block
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/lint-unused-unsafe.rs:20:13
|
--> $DIR/lint-unused-unsafe-thir.rs:22:13
|
||||||
|
|
|
|
||||||
LL | fn bad2() { unsafe { bad1() } }
|
LL | fn bad2() { unsafe { bad1() } }
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
||||||
error: 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 {} }
|
LL | unsafe fn bad3() { unsafe {} }
|
||||||
| ---------------- ^^^^^^ unnecessary `unsafe` block
|
| ---------------- ^^^^^^ unnecessary `unsafe` block
|
||||||
|
@ -25,13 +25,13 @@ LL | unsafe fn bad3() { unsafe {} }
|
||||||
| because it's nested under this `unsafe` fn
|
| because it's nested under this `unsafe` fn
|
||||||
|
|
||||||
error: unnecessary `unsafe` block
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/lint-unused-unsafe.rs:22:13
|
--> $DIR/lint-unused-unsafe-thir.rs:24:13
|
||||||
|
|
|
|
||||||
LL | fn bad4() { unsafe { callback(||{}) } }
|
LL | fn bad4() { unsafe { callback(||{}) } }
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
||||||
error: 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() } }
|
LL | unsafe fn bad5() { unsafe { unsf() } }
|
||||||
| ---------------- ^^^^^^ unnecessary `unsafe` block
|
| ---------------- ^^^^^^ unnecessary `unsafe` block
|
||||||
|
@ -39,7 +39,7 @@ LL | unsafe fn bad5() { unsafe { unsf() } }
|
||||||
| because it's nested under this `unsafe` fn
|
| because it's nested under this `unsafe` fn
|
||||||
|
|
||||||
error: unnecessary `unsafe` block
|
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
|
LL | unsafe { // don't put the warning here
|
||||||
| ------ because it's nested under this `unsafe` block
|
| ------ because it's nested under this `unsafe` block
|
||||||
|
@ -47,7 +47,7 @@ LL | unsafe {
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
||||||
error: unnecessary `unsafe` block
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/lint-unused-unsafe.rs:33:9
|
--> $DIR/lint-unused-unsafe-thir.rs:35:9
|
||||||
|
|
|
|
||||||
LL | unsafe {
|
LL | unsafe {
|
||||||
| ------ because it's nested under this `unsafe` block
|
| ------ because it's nested under this `unsafe` block
|
||||||
|
@ -55,7 +55,7 @@ LL | unsafe {
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
||||||
error: 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() {
|
LL | unsafe fn bad7() {
|
||||||
| ---------------- because it's nested under this `unsafe` fn
|
| ---------------- 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
|
@ -76,12 +76,10 @@ LL | unsafe {}
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
|
|
||||||
error: 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() } }
|
LL | unsafe { unsafe { unsf() } }
|
||||||
| ------ ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ unnecessary `unsafe` block
|
||||||
| |
|
|
||||||
| because it's nested under this `unsafe` block
|
|
||||||
|
|
||||||
error: unnecessary `unsafe` block
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
|
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
|
||||||
|
@ -91,6 +89,13 @@ LL | unsafe fn allow_level() {
|
||||||
...
|
...
|
||||||
LL | unsafe { unsf() }
|
LL | unsafe { unsf() }
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ 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
|
error: unnecessary `unsafe` block
|
||||||
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
|
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
|
||||||
|
@ -100,6 +105,13 @@ LL | unsafe fn nested_allow_level() {
|
||||||
...
|
...
|
||||||
LL | unsafe { unsf() }
|
LL | unsafe { unsf() }
|
||||||
| ^^^^^^ unnecessary `unsafe` block
|
| ^^^^^^ 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
|
error[E0133]: call to unsafe function is unsafe and requires unsafe block
|
||||||
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
|
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue