Skip to content

Commit 8809979

Browse files
steffahnLeSeulArtichaut
authored andcommitted
Port improved unused_unsafe check to -Zthir_unsafeck
1 parent 7210e46 commit 8809979

7 files changed

+2219
-440
lines changed

compiler/rustc_mir_build/src/check_unsafety.rs

+145-70
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
use crate::build::ExprCategory;
2+
use hir::intravisit;
3+
use rustc_data_structures::fx::FxHashMap;
24
use rustc_middle::thir::visit::{self, Visitor};
35

46
use rustc_errors::struct_span_err;
57
use rustc_hir as hir;
6-
use rustc_middle::mir::BorrowKind;
7-
use rustc_middle::thir::*;
8+
use rustc_middle::mir::{BorrowKind, UnusedUnsafe, UsedUnsafeBlockData};
89
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
10+
use rustc_middle::{lint, thir::*};
911
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
1012
use rustc_session::lint::Level;
1113
use rustc_span::def_id::{DefId, LocalDefId};
1214
use rustc_span::symbol::Symbol;
1315
use rustc_span::Span;
1416

1517
use std::borrow::Cow;
18+
use std::collections::hash_map;
1619
use std::ops::Bound;
1720

1821
struct UnsafetyVisitor<'a, 'tcx> {
@@ -24,7 +27,6 @@ struct UnsafetyVisitor<'a, 'tcx> {
2427
/// The current "safety context". This notably tracks whether we are in an
2528
/// `unsafe` block, and whether it has been used.
2629
safety_context: SafetyContext,
27-
body_unsafety: BodyUnsafety,
2830
/// The `#[target_feature]` attributes of the body. Used for checking
2931
/// calls to functions with `#[target_feature]` (RFC 2396).
3032
body_target_features: &'tcx [Symbol],
@@ -34,51 +36,41 @@ struct UnsafetyVisitor<'a, 'tcx> {
3436
in_union_destructure: bool,
3537
param_env: ParamEnv<'tcx>,
3638
inside_adt: bool,
39+
used_unsafe_blocks: &'a mut FxHashMap<hir::HirId, UsedUnsafeBlockData>,
3740
}
3841

3942
impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
4043
fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
41-
if let (
42-
SafetyContext::UnsafeBlock { span: enclosing_span, .. },
43-
SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
44-
) = (self.safety_context, safety_context)
45-
{
46-
self.warn_unused_unsafe(
47-
hir_id,
48-
block_span,
49-
Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
50-
);
51-
f(self);
52-
} else {
53-
let prev_context = self.safety_context;
54-
self.safety_context = safety_context;
44+
let prev_context = self.safety_context;
45+
self.safety_context = safety_context;
5546

56-
f(self);
47+
f(self);
5748

58-
if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
59-
self.warn_unused_unsafe(
60-
hir_id,
61-
span,
62-
if self.unsafe_op_in_unsafe_fn_allowed() {
63-
self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
64-
} else {
65-
None
66-
},
67-
);
68-
}
69-
self.safety_context = prev_context;
70-
}
49+
self.safety_context = prev_context;
7150
}
7251

7352
fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
53+
use UsedUnsafeBlockData::{AllAllowedInUnsafeFn, SomeDisallowedInUnsafeFn};
54+
7455
let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
7556
match self.safety_context {
7657
SafetyContext::BuiltinUnsafeBlock => {}
77-
SafetyContext::UnsafeBlock { ref mut used, .. } => {
78-
if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
79-
// Mark this block as useful
80-
*used = true;
81-
}
58+
SafetyContext::UnsafeBlock(hir_id) => {
59+
let new_usage = if unsafe_op_in_unsafe_fn_allowed {
60+
AllAllowedInUnsafeFn(self.hir_context)
61+
} else {
62+
SomeDisallowedInUnsafeFn
63+
};
64+
match self.used_unsafe_blocks.entry(hir_id) {
65+
hash_map::Entry::Occupied(mut entry) => {
66+
if new_usage == SomeDisallowedInUnsafeFn {
67+
*entry.get_mut() = SomeDisallowedInUnsafeFn;
68+
}
69+
}
70+
hash_map::Entry::Vacant(entry) => {
71+
entry.insert(new_usage);
72+
}
73+
};
8274
}
8375
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
8476
SafetyContext::UnsafeFn => {
@@ -117,24 +109,6 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
117109
}
118110
}
119111

120-
fn warn_unused_unsafe(
121-
&self,
122-
hir_id: hir::HirId,
123-
block_span: Span,
124-
enclosing_unsafe: Option<(Span, &'static str)>,
125-
) {
126-
let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
127-
self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
128-
let msg = "unnecessary `unsafe` block";
129-
let mut db = lint.build(msg);
130-
db.span_label(block_span, msg);
131-
if let Some((span, kind)) = enclosing_unsafe {
132-
db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
133-
}
134-
db.emit();
135-
});
136-
}
137-
138112
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
139113
fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
140114
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
@@ -200,10 +174,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
200174
});
201175
}
202176
BlockSafety::ExplicitUnsafe(hir_id) => {
203-
self.in_safety_context(
204-
SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
205-
|this| visit::walk_block(this, block),
206-
);
177+
self.in_safety_context(SafetyContext::UnsafeBlock(hir_id), |this| {
178+
visit::walk_block(this, block)
179+
});
207180
}
208181
BlockSafety::Safe => {
209182
visit::walk_block(self, block);
@@ -421,8 +394,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
421394
});
422395
let closure_thir = &closure_thir.borrow();
423396
let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id);
424-
let mut closure_visitor =
425-
UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
397+
let mut closure_visitor = UnsafetyVisitor {
398+
thir: closure_thir,
399+
hir_context,
400+
used_unsafe_blocks: self.used_unsafe_blocks,
401+
..*self
402+
};
426403
closure_visitor.visit_expr(&closure_thir[expr]);
427404
// Unsafe blocks can be used in closures, make sure to take it into account
428405
self.safety_context = closure_visitor.safety_context;
@@ -495,7 +472,7 @@ enum SafetyContext {
495472
Safe,
496473
BuiltinUnsafeBlock,
497474
UnsafeFn,
498-
UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
475+
UnsafeBlock(hir::HirId),
499476
}
500477

501478
#[derive(Clone, Copy)]
@@ -512,14 +489,6 @@ impl BodyUnsafety {
512489
fn is_unsafe(&self) -> bool {
513490
matches!(self, BodyUnsafety::Unsafe(_))
514491
}
515-
516-
/// If the body is unsafe, returns the `Span` of its signature.
517-
fn unsafe_fn_sig_span(self) -> Option<Span> {
518-
match self {
519-
BodyUnsafety::Unsafe(span) => Some(span),
520-
BodyUnsafety::Safe => None,
521-
}
522-
}
523492
}
524493

525494
#[derive(Clone, Copy, PartialEq)]
@@ -616,6 +585,106 @@ impl UnsafeOpKind {
616585
}
617586
}
618587

588+
/// Context information for [`UnusedUnsafeVisitor`] traversal,
589+
/// saves (innermost) relevant context
590+
#[derive(Copy, Clone, Debug)]
591+
enum Context {
592+
Safe,
593+
/// in an `unsafe fn`
594+
UnsafeFn(hir::HirId),
595+
/// in a *used* `unsafe` block
596+
/// (i.e. a block without unused-unsafe warning)
597+
UnsafeBlock(hir::HirId),
598+
}
599+
600+
struct UnusedUnsafeVisitor<'a, 'tcx> {
601+
tcx: TyCtxt<'tcx>,
602+
used_unsafe_blocks: &'a FxHashMap<hir::HirId, UsedUnsafeBlockData>,
603+
context: Context,
604+
}
605+
606+
impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
607+
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
608+
use UsedUnsafeBlockData::{AllAllowedInUnsafeFn, SomeDisallowedInUnsafeFn};
609+
610+
if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
611+
let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) {
612+
(Level::Allow, _) => Some(SomeDisallowedInUnsafeFn),
613+
_ => self.used_unsafe_blocks.get(&block.hir_id).copied(),
614+
};
615+
let unused_unsafe = match (self.context, used) {
616+
(_, None) => UnusedUnsafe::Unused,
617+
(Context::Safe, Some(_))
618+
| (Context::UnsafeFn(_), Some(SomeDisallowedInUnsafeFn)) => {
619+
let previous_context = self.context;
620+
self.context = Context::UnsafeBlock(block.hir_id);
621+
intravisit::walk_block(self, block);
622+
self.context = previous_context;
623+
return;
624+
}
625+
(Context::UnsafeFn(hir_id), Some(AllAllowedInUnsafeFn(lint_root))) => {
626+
UnusedUnsafe::InUnsafeFn(hir_id, lint_root)
627+
}
628+
(Context::UnsafeBlock(hir_id), Some(_)) => UnusedUnsafe::InUnsafeBlock(hir_id),
629+
};
630+
report_unused_unsafe(self.tcx, block.hir_id, unused_unsafe);
631+
}
632+
intravisit::walk_block(self, block);
633+
}
634+
635+
fn visit_fn(
636+
&mut self,
637+
fk: intravisit::FnKind<'tcx>,
638+
_fd: &'tcx hir::FnDecl<'tcx>,
639+
b: hir::BodyId,
640+
_s: rustc_span::Span,
641+
_id: hir::HirId,
642+
) {
643+
if matches!(fk, intravisit::FnKind::Closure) {
644+
self.visit_body(self.tcx.hir().body(b))
645+
}
646+
}
647+
}
648+
649+
fn report_unused_unsafe(tcx: TyCtxt<'_>, id: hir::HirId, kind: UnusedUnsafe) {
650+
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
651+
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
652+
let msg = "unnecessary `unsafe` block";
653+
let mut db = lint.build(msg);
654+
db.span_label(span, msg);
655+
match kind {
656+
UnusedUnsafe::Unused => {}
657+
UnusedUnsafe::InUnsafeBlock(id) => {
658+
db.span_label(
659+
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
660+
format!("because it's nested under this `unsafe` block"),
661+
);
662+
}
663+
UnusedUnsafe::InUnsafeFn(id, usage_lint_root) => {
664+
db.span_label(
665+
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
666+
format!("because it's nested under this `unsafe` fn"),
667+
)
668+
.note(
669+
"this `unsafe` block does contain unsafe operations, \
670+
but those are already allowed in an `unsafe fn`",
671+
);
672+
let (level, source) =
673+
tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, usage_lint_root);
674+
assert_eq!(level, Level::Allow);
675+
lint::explain_lint_level_source(
676+
UNSAFE_OP_IN_UNSAFE_FN,
677+
Level::Allow,
678+
source,
679+
&mut db,
680+
);
681+
}
682+
}
683+
684+
db.emit();
685+
});
686+
}
687+
619688
pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
620689
// THIR unsafeck is gated under `-Z thir-unsafeck`
621690
if !tcx.sess.opts.unstable_opts.thir_unsafeck {
@@ -655,14 +724,20 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
655724
thir,
656725
safety_context,
657726
hir_context: hir_id,
658-
body_unsafety,
659727
body_target_features,
660728
assignment_info: None,
661729
in_union_destructure: false,
662730
param_env: tcx.param_env(def.did),
663731
inside_adt: false,
732+
used_unsafe_blocks: &mut Default::default(),
664733
};
665734
visitor.visit_expr(&thir[expr]);
735+
let mut visitor2 = UnusedUnsafeVisitor {
736+
tcx,
737+
used_unsafe_blocks: visitor.used_unsafe_blocks,
738+
context: if body_unsafety.is_unsafe() { Context::UnsafeFn(hir_id) } else { Context::Safe },
739+
};
740+
intravisit::Visitor::visit_body(&mut visitor2, tcx.hir().body(tcx.hir().body_owned_by(hir_id)));
666741
}
667742

668743
pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {

src/test/ui/span/lint-unused-unsafe-thir.rs

-61
This file was deleted.

0 commit comments

Comments
 (0)