]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/check_unsafety.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / check_unsafety.rs
CommitLineData
353b0b11 1use rustc_data_structures::unord::{UnordItems, UnordSet};
dfeec247
XL
2use rustc_errors::struct_span_err;
3use rustc_hir as hir;
9c376795 4use rustc_hir::def::DefKind;
f9f354fc
XL
5use rustc_hir::def_id::{DefId, LocalDefId};
6use rustc_hir::hir_id::HirId;
dfeec247 7use rustc_hir::intravisit;
487cf647 8use rustc_hir::{BlockCheckMode, ExprKind, Node};
ba9703b0 9use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
f2b60f7d 10use rustc_middle::mir::*;
ba9703b0
XL
11use rustc_middle::ty::query::Providers;
12use rustc_middle::ty::{self, TyCtxt};
cdc7bbd5 13use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
f9f354fc 14use rustc_session::lint::Level;
ea8adc8c 15
0731742a
XL
16use std::ops::Bound;
17
dc9dc135
XL
18pub struct UnsafetyChecker<'a, 'tcx> {
19 body: &'a Body<'tcx>,
f9f354fc 20 body_did: LocalDefId,
ea8adc8c
XL
21 violations: Vec<UnsafetyViolation>,
22 source_info: SourceInfo,
dc9dc135 23 tcx: TyCtxt<'tcx>,
ea8adc8c 24 param_env: ty::ParamEnv<'tcx>,
5e7ed085
FG
25
26 /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
353b0b11 27 used_unsafe_blocks: UnordSet<HirId>,
ea8adc8c
XL
28}
29
dc9dc135 30impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
b7449926 31 fn new(
dc9dc135 32 body: &'a Body<'tcx>,
f9f354fc 33 body_did: LocalDefId,
dc9dc135 34 tcx: TyCtxt<'tcx>,
b7449926
XL
35 param_env: ty::ParamEnv<'tcx>,
36 ) -> Self {
ea8adc8c 37 Self {
dc9dc135 38 body,
f9f354fc 39 body_did,
ea8adc8c 40 violations: vec![],
f9f354fc 41 source_info: SourceInfo::outermost(body.span),
ea8adc8c
XL
42 tcx,
43 param_env,
5e7ed085 44 used_unsafe_blocks: Default::default(),
ea8adc8c
XL
45 }
46 }
47}
48
a2a8927a 49impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
dfeec247 50 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
ea8adc8c
XL
51 self.source_info = terminator.source_info;
52 match terminator.kind {
dfeec247
XL
53 TerminatorKind::Goto { .. }
54 | TerminatorKind::SwitchInt { .. }
55 | TerminatorKind::Drop { .. }
56 | TerminatorKind::Yield { .. }
57 | TerminatorKind::Assert { .. }
dfeec247
XL
58 | TerminatorKind::GeneratorDrop
59 | TerminatorKind::Resume
353b0b11 60 | TerminatorKind::Terminate
dfeec247
XL
61 | TerminatorKind::Return
62 | TerminatorKind::Unreachable
f035d41b 63 | TerminatorKind::FalseEdge { .. }
dfeec247 64 | TerminatorKind::FalseUnwind { .. } => {
ea8adc8c
XL
65 // safe (at least as emitted during MIR construction)
66 }
67
68 TerminatorKind::Call { ref func, .. } => {
dc9dc135 69 let func_ty = func.ty(self.body, self.tcx);
04454e1e
FG
70 let func_id =
71 if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None };
ea8adc8c
XL
72 let sig = func_ty.fn_sig(self.tcx);
73 if let hir::Unsafety::Unsafe = sig.unsafety() {
dfeec247 74 self.require_unsafe(
cdc7bbd5 75 UnsafetyViolationKind::General,
3dfed10e 76 UnsafetyViolationDetails::CallToUnsafeFunction,
dfeec247 77 )
ea8adc8c 78 }
f9f354fc 79
04454e1e 80 if let Some(func_id) = func_id {
1b1a35ee 81 self.check_target_features(*func_id);
f9f354fc 82 }
ea8adc8c 83 }
f9f354fc
XL
84
85 TerminatorKind::InlineAsm { .. } => self.require_unsafe(
f9f354fc 86 UnsafetyViolationKind::General,
3dfed10e 87 UnsafetyViolationDetails::UseOfInlineAssembly,
f9f354fc 88 ),
ea8adc8c 89 }
48663c56 90 self.super_terminator(terminator, location);
ea8adc8c
XL
91 }
92
dfeec247 93 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
ea8adc8c
XL
94 self.source_info = statement.source_info;
95 match statement.kind {
dfeec247
XL
96 StatementKind::Assign(..)
97 | StatementKind::FakeRead(..)
98 | StatementKind::SetDiscriminant { .. }
04454e1e 99 | StatementKind::Deinit(..)
dfeec247
XL
100 | StatementKind::StorageLive(..)
101 | StatementKind::StorageDead(..)
102 | StatementKind::Retag { .. }
353b0b11 103 | StatementKind::PlaceMention(..)
3dfed10e 104 | StatementKind::Coverage(..)
487cf647 105 | StatementKind::Intrinsic(..)
9ffffee4 106 | StatementKind::ConstEvalCounter
dfeec247 107 | StatementKind::Nop => {
ea8adc8c
XL
108 // safe (at least as emitted during MIR construction)
109 }
353b0b11
FG
110 // `AscribeUserType` just exists to help MIR borrowck.
111 // It has no semantics, and everything is already reported by `PlaceMention`.
112 StatementKind::AscribeUserType(..) => return,
ea8adc8c 113 }
48663c56 114 self.super_statement(statement, location);
ea8adc8c
XL
115 }
116
dfeec247 117 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
9fa01778 118 match rvalue {
dfeec247
XL
119 Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
120 &AggregateKind::Array(..) | &AggregateKind::Tuple => {}
a2a8927a
XL
121 &AggregateKind::Adt(adt_did, ..) => {
122 match self.tcx.layout_scalar_valid_range(adt_did) {
dfeec247
XL
123 (Bound::Unbounded, Bound::Unbounded) => {}
124 _ => self.require_unsafe(
cdc7bbd5 125 UnsafetyViolationKind::General,
3dfed10e 126 UnsafetyViolationDetails::InitializingTypeWith,
dfeec247 127 ),
0731742a
XL
128 }
129 }
dfeec247 130 &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
9ffffee4 131 let def_id = def_id.expect_local();
5e7ed085 132 let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
064997fb 133 self.tcx.unsafety_check_result(def_id);
353b0b11 134 self.register_violations(violations, used_unsafe_blocks.items().copied());
dfeec247 135 }
9fa01778 136 },
dfeec247 137 _ => {}
ea8adc8c
XL
138 }
139 self.super_rvalue(rvalue, location);
140 }
141
9c376795
FG
142 fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
143 if let Operand::Constant(constant) = op {
144 let maybe_uneval = match constant.literal {
145 ConstantKind::Val(..) | ConstantKind::Ty(_) => None,
146 ConstantKind::Unevaluated(uv, _) => Some(uv),
147 };
148
149 if let Some(uv) = maybe_uneval {
150 if uv.promoted.is_none() {
151 let def_id = uv.def.def_id_for_type_of();
152 if self.tcx.def_kind(def_id) == DefKind::InlineConst {
153 let local_def_id = def_id.expect_local();
154 let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
155 self.tcx.unsafety_check_result(local_def_id);
353b0b11 156 self.register_violations(violations, used_unsafe_blocks.items().copied());
9c376795
FG
157 }
158 }
159 }
160 }
161 self.super_operand(op, location);
162 }
163
dfeec247 164 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
f9f354fc 165 // On types with `scalar_valid_range`, prevent
74b04a01
XL
166 // * `&mut x.field`
167 // * `x.field = y;`
168 // * `&x.field` if `field`'s type has interior mutability
169 // because either of these would allow modifying the layout constrained field and
170 // insert values that violate the layout constraints.
171 if context.is_mutating_use() || context.is_borrow() {
ba9703b0 172 self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
74b04a01
XL
173 }
174
5e7ed085 175 // Some checks below need the extra meta info of the local declaration.
fc512014
XL
176 let decl = &self.body.local_decls[place.local];
177
178 // Check the base local: it might be an unsafe-to-access static. We only check derefs of the
179 // temporary holding the static pointer to avoid duplicate errors
180 // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
181 if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
5e7ed085 182 // If the projection root is an artificial local that we introduced when
fc512014
XL
183 // desugaring `static`, give a more specific error message
184 // (avoid the general "raw pointer" clause below, that would only be confusing).
353b0b11 185 if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
fc512014
XL
186 if self.tcx.is_mutable_static(def_id) {
187 self.require_unsafe(
188 UnsafetyViolationKind::General,
189 UnsafetyViolationDetails::UseOfMutableStatic,
190 );
191 return;
192 } else if self.tcx.is_foreign_item(def_id) {
74b04a01 193 self.require_unsafe(
fc512014
XL
194 UnsafetyViolationKind::General,
195 UnsafetyViolationDetails::UseOfExternStatic,
dfeec247 196 );
fc512014 197 return;
0731742a 198 }
e1599b0c 199 }
fc512014
XL
200 }
201
202 // Check for raw pointer `Deref`.
203 for (base, proj) in place.iter_projections() {
204 if proj == ProjectionElem::Deref {
fc512014
XL
205 let base_ty = base.ty(self.body, self.tcx).ty;
206 if base_ty.is_unsafe_ptr() {
207 self.require_unsafe(
cdc7bbd5 208 UnsafetyViolationKind::General,
fc512014
XL
209 UnsafetyViolationDetails::DerefOfRawPointer,
210 )
0731742a 211 }
fc512014
XL
212 }
213 }
214
215 // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
216 // whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
217 let mut saw_deref = false;
218 for (base, proj) in place.iter_projections().rev() {
219 if proj == ProjectionElem::Deref {
220 saw_deref = true;
221 continue;
e1599b0c 222 }
fc512014
XL
223
224 let base_ty = base.ty(self.body, self.tcx).ty;
17df50a5 225 if base_ty.is_union() {
fc512014
XL
226 // If we did not hit a `Deref` yet and the overall place use is an assignment, the
227 // rules are different.
228 let assign_to_field = !saw_deref
229 && matches!(
230 context,
231 PlaceContext::MutatingUse(
232 MutatingUseContext::Store
233 | MutatingUseContext::Drop
234 | MutatingUseContext::AsmOutput
235 )
236 );
237 // If this is just an assignment, determine if the assigned type needs dropping.
238 if assign_to_field {
239 // We have to check the actual type of the assignment, as that determines if the
240 // old value is being dropped.
241 let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
064997fb
FG
242 if assigned_ty.needs_drop(self.tcx, self.param_env) {
243 // This would be unsafe, but should be outright impossible since we reject such unions.
244 self.tcx.sess.delay_span_bug(
245 self.source_info.span,
246 format!("union fields that need dropping should be impossible: {assigned_ty}")
fc512014 247 );
ea8adc8c 248 }
fc512014
XL
249 } else {
250 self.require_unsafe(
cdc7bbd5 251 UnsafetyViolationKind::General,
fc512014
XL
252 UnsafetyViolationDetails::AccessToUnionField,
253 )
ea8adc8c 254 }
ea8adc8c 255 }
e1599b0c 256 }
ea8adc8c
XL
257 }
258}
259
a2a8927a 260impl<'tcx> UnsafetyChecker<'_, 'tcx> {
3dfed10e 261 fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
cdc7bbd5
XL
262 // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
263 assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
264
ea8adc8c 265 let source_info = self.source_info;
f9f354fc
XL
266 let lint_root = self.body.source_scopes[self.source_info.scope]
267 .local_data
268 .as_ref()
269 .assert_crate_local()
270 .lint_root;
dfeec247 271 self.register_violations(
5e7ed085 272 [&UnsafetyViolation { source_info, lint_root, kind, details }],
353b0b11 273 UnordItems::empty(),
dfeec247 274 );
ea8adc8c
XL
275 }
276
5e7ed085 277 fn register_violations<'a>(
dfeec247 278 &mut self,
5e7ed085 279 violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
353b0b11 280 new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>,
dfeec247 281 ) {
60c5eb7d
XL
282 let safety = self.body.source_scopes[self.source_info.scope]
283 .local_data
284 .as_ref()
285 .assert_crate_local()
286 .safety;
5e7ed085 287 match safety {
0731742a 288 // `unsafe` blocks are required in safe code
5e7ed085
FG
289 Safety::Safe => violations.into_iter().for_each(|&violation| {
290 match violation.kind {
291 UnsafetyViolationKind::General => {}
292 UnsafetyViolationKind::UnsafeFn => {
293 bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
ea8adc8c
XL
294 }
295 }
5e7ed085
FG
296 if !self.violations.contains(&violation) {
297 self.violations.push(violation)
f9f354fc 298 }
5e7ed085
FG
299 }),
300 // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
301 Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
302 violation.kind = UnsafetyViolationKind::UnsafeFn;
303 if !self.violations.contains(&violation) {
304 self.violations.push(violation)
ea8adc8c 305 }
5e7ed085
FG
306 }),
307 Safety::BuiltinUnsafe => {}
f2b60f7d
FG
308 Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| {
309 self.used_unsafe_blocks.insert(hir_id);
5e7ed085 310 }),
ea8adc8c 311 };
5e7ed085 312
353b0b11 313 self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks);
ea8adc8c 314 }
0731742a
XL
315 fn check_mut_borrowing_layout_constrained_field(
316 &mut self,
ba9703b0 317 place: Place<'tcx>,
0731742a
XL
318 is_mut_use: bool,
319 ) {
5869c6ff 320 for (place_base, elem) in place.iter_projections().rev() {
e1599b0c 321 match elem {
74b04a01
XL
322 // Modifications behind a dereference don't affect the value of
323 // the pointer.
324 ProjectionElem::Deref => return,
0731742a 325 ProjectionElem::Field(..) => {
5869c6ff 326 let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
1b1a35ee 327 if let ty::Adt(def, _) = ty.kind() {
5e7ed085 328 if self.tcx.layout_scalar_valid_range(def.did())
ba9703b0
XL
329 != (Bound::Unbounded, Bound::Unbounded)
330 {
3dfed10e
XL
331 let details = if is_mut_use {
332 UnsafetyViolationDetails::MutationOfLayoutConstrainedField
74b04a01 333
ba9703b0
XL
334 // Check `is_freeze` as late as possible to avoid cycle errors
335 // with opaque types.
f035d41b
XL
336 } else if !place
337 .ty(self.body, self.tcx)
338 .ty
2b03887a 339 .is_freeze(self.tcx, self.param_env)
f035d41b 340 {
3dfed10e 341 UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
ba9703b0
XL
342 } else {
343 continue;
344 };
cdc7bbd5 345 self.require_unsafe(UnsafetyViolationKind::General, details);
ba9703b0 346 }
0731742a
XL
347 }
348 }
349 _ => {}
350 }
0731742a
XL
351 }
352 }
f9f354fc
XL
353
354 /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
355 /// the called function has target features the calling function hasn't.
356 fn check_target_features(&mut self, func_did: DefId) {
17df50a5
XL
357 // Unsafety isn't required on wasm targets. For more information see
358 // the corresponding check in typeck/src/collect.rs
359 if self.tcx.sess.target.options.is_like_wasm {
360 return;
361 }
362
f9f354fc 363 let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
04454e1e
FG
364 // The body might be a constant, so it doesn't have codegen attributes.
365 let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features;
f9f354fc
XL
366
367 // Is `callee_features` a subset of `calling_features`?
368 if !callee_features.iter().all(|feature| self_features.contains(feature)) {
369 self.require_unsafe(
cdc7bbd5 370 UnsafetyViolationKind::General,
3dfed10e 371 UnsafetyViolationDetails::CallToFunctionWith,
f9f354fc
XL
372 )
373 }
374 }
ea8adc8c
XL
375}
376
f035d41b 377pub(crate) fn provide(providers: &mut Providers) {
3dfed10e
XL
378 *providers = Providers {
379 unsafety_check_result: |tcx, def_id| {
380 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
381 tcx.unsafety_check_result_for_const_arg(def)
382 } else {
383 unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id))
384 }
385 },
386 unsafety_check_result_for_const_arg: |tcx, (did, param_did)| {
387 unsafety_check_result(
388 tcx,
389 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
390 )
391 },
3dfed10e
XL
392 ..*providers
393 };
ea8adc8c
XL
394}
395
5e7ed085
FG
396/// Context information for [`UnusedUnsafeVisitor`] traversal,
397/// saves (innermost) relevant context
398#[derive(Copy, Clone, Debug)]
399enum Context {
400 Safe,
401 /// in an `unsafe fn`
402 UnsafeFn(HirId),
403 /// in a *used* `unsafe` block
404 /// (i.e. a block without unused-unsafe warning)
405 UnsafeBlock(HirId),
406}
407
408struct UnusedUnsafeVisitor<'a, 'tcx> {
409 tcx: TyCtxt<'tcx>,
353b0b11 410 used_unsafe_blocks: &'a UnordSet<HirId>,
5e7ed085
FG
411 context: Context,
412 unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>,
ea8adc8c
XL
413}
414
5e7ed085 415impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
dfeec247 416 fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
dfeec247 417 if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
5e7ed085 418 let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) {
f2b60f7d
FG
419 (Level::Allow, _) => true,
420 _ => self.used_unsafe_blocks.contains(&block.hir_id),
5e7ed085
FG
421 };
422 let unused_unsafe = match (self.context, used) {
f2b60f7d
FG
423 (_, false) => UnusedUnsafe::Unused,
424 (Context::Safe, true) | (Context::UnsafeFn(_), true) => {
5e7ed085
FG
425 let previous_context = self.context;
426 self.context = Context::UnsafeBlock(block.hir_id);
427 intravisit::walk_block(self, block);
428 self.context = previous_context;
429 return;
430 }
f2b60f7d 431 (Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id),
5e7ed085
FG
432 };
433 self.unused_unsafes.push((block.hir_id, unused_unsafe));
434 }
435 intravisit::walk_block(self, block);
436 }
437
9c376795
FG
438 fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
439 if matches!(self.tcx.def_kind(c.def_id), DefKind::InlineConst) {
440 self.visit_body(self.tcx.hir().body(c.body))
441 }
442 }
443
5e7ed085
FG
444 fn visit_fn(
445 &mut self,
446 fk: intravisit::FnKind<'tcx>,
447 _fd: &'tcx hir::FnDecl<'tcx>,
448 b: hir::BodyId,
449 _s: rustc_span::Span,
9ffffee4 450 _id: LocalDefId,
5e7ed085
FG
451 ) {
452 if matches!(fk, intravisit::FnKind::Closure) {
453 self.visit_body(self.tcx.hir().body(b))
ea8adc8c
XL
454 }
455 }
456}
457
416331ca
XL
458fn check_unused_unsafe(
459 tcx: TyCtxt<'_>,
f9f354fc 460 def_id: LocalDefId,
353b0b11 461 used_unsafe_blocks: &UnordSet<HirId>,
5e7ed085 462) -> Vec<(HirId, UnusedUnsafe)> {
064997fb 463 let body_id = tcx.hir().maybe_body_owned_by(def_id);
5e7ed085
FG
464
465 let Some(body_id) = body_id else {
466 debug!("check_unused_unsafe({:?}) - no body found", def_id);
467 return vec![];
ea8adc8c 468 };
ea8adc8c 469
064997fb
FG
470 let body = tcx.hir().body(body_id);
471 let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
5e7ed085
FG
472 let context = match tcx.hir().fn_sig_by_hir_id(hir_id) {
473 Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn(hir_id),
474 _ => Context::Safe,
475 };
476
477 debug!(
478 "check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})",
479 def_id, body, context, used_unsafe_blocks
480 );
481
482 let mut unused_unsafes = vec![];
483
484 let mut visitor = UnusedUnsafeVisitor {
485 tcx,
486 used_unsafe_blocks,
487 context,
488 unused_unsafes: &mut unused_unsafes,
489 };
dfeec247 490 intravisit::Visitor::visit_body(&mut visitor, body);
5e7ed085
FG
491
492 unused_unsafes
ea8adc8c
XL
493}
494
9c376795
FG
495fn unsafety_check_result(
496 tcx: TyCtxt<'_>,
3dfed10e 497 def: ty::WithOptConstParam<LocalDefId>,
9c376795 498) -> &UnsafetyCheckResult {
3dfed10e 499 debug!("unsafety_violations({:?})", def);
ea8adc8c 500
0731742a 501 // N.B., this borrow is valid because all the consumers of
ea8adc8c 502 // `mir_built` force this.
3dfed10e 503 let body = &tcx.mir_built(def).borrow();
ea8adc8c 504
9c376795 505 if body.is_custom_mir() {
487cf647
FG
506 return tcx.arena.alloc(UnsafetyCheckResult {
507 violations: Vec::new(),
353b0b11 508 used_unsafe_blocks: Default::default(),
487cf647
FG
509 unused_unsafes: Some(Vec::new()),
510 });
511 }
512
3dfed10e 513 let param_env = tcx.param_env(def.did);
9fa01778 514
136023e0 515 let mut checker = UnsafetyChecker::new(body, def.did, tcx, param_env);
ba9703b0 516 checker.visit_body(&body);
ea8adc8c 517
9c376795 518 let unused_unsafes = (!tcx.is_typeck_child(def.did.to_def_id()))
5e7ed085 519 .then(|| check_unused_unsafe(tcx, def.did, &checker.used_unsafe_blocks));
3dfed10e
XL
520
521 tcx.arena.alloc(UnsafetyCheckResult {
5e7ed085
FG
522 violations: checker.violations,
523 used_unsafe_blocks: checker.used_unsafe_blocks,
524 unused_unsafes,
3dfed10e 525 })
ea8adc8c
XL
526}
527
5e7ed085 528fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
ba9703b0 529 let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
2b03887a
FG
530 let msg = "unnecessary `unsafe` block";
531 tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg, |lint| {
532 lint.span_label(span, msg);
5e7ed085
FG
533 match kind {
534 UnusedUnsafe::Unused => {}
535 UnusedUnsafe::InUnsafeBlock(id) => {
2b03887a 536 lint.span_label(
5e7ed085 537 tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
04454e1e 538 "because it's nested under this `unsafe` block",
5e7ed085
FG
539 );
540 }
74b04a01 541 }
5e7ed085 542
2b03887a 543 lint
74b04a01 544 });
ea8adc8c
XL
545}
546
f9f354fc 547pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
ea8adc8c 548 debug!("check_unsafety({:?})", def_id);
abe05a73 549
9c376795
FG
550 // closures and inline consts are handled by their parent fn.
551 if tcx.is_typeck_child(def_id.to_def_id()) {
abe05a73
XL
552 return;
553 }
ea8adc8c 554
5e7ed085 555 let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
ea8adc8c 556
3dfed10e
XL
557 for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
558 let (description, note) = details.description_and_note();
559
ff7c6d11 560 match kind {
cdc7bbd5 561 UnsafetyViolationKind::General => {
f9f354fc 562 // once
487cf647
FG
563 let unsafe_fn_msg = if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) {
564 " function or"
565 } else {
566 ""
567 };
568
569 let mut err = struct_span_err!(
dfeec247
XL
570 tcx.sess,
571 source_info.span,
572 E0133,
f9f354fc
XL
573 "{} is unsafe and requires unsafe{} block",
574 description,
575 unsafe_fn_msg,
487cf647
FG
576 );
577 err.span_label(source_info.span, description).note(note);
578 let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| {
579 if let Node::Expr(block) = node
580 && let ExprKind::Block(block, _) = block.kind
581 && let BlockCheckMode::UnsafeBlock(_) = block.rules
582 {
583 true
584 }
585 else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
586 && sig.header.is_unsafe()
587 {
588 true
589 } else {
590 false
591 }
592 });
593 if let Some((id, _)) = note_non_inherited {
594 let span = tcx.hir().span(id);
595 err.span_label(
596 tcx.sess.source_map().guess_head_span(span),
597 "items do not inherit unsafety from separate enclosing items",
598 );
599 }
600
601 err.emit();
ff7c6d11 602 }
f9f354fc
XL
603 UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
604 UNSAFE_OP_IN_UNSAFE_FN,
605 lint_root,
606 source_info.span,
2b03887a
FG
607 format!("{} is unsafe and requires unsafe block (error E0133)", description,),
608 |lint| lint.span_label(source_info.span, description).note(note),
f9f354fc 609 ),
ea8adc8c
XL
610 }
611 }
612
5e7ed085
FG
613 for &(block_id, kind) in unused_unsafes.as_ref().unwrap() {
614 report_unused_unsafe(tcx, kind, block_id);
ba9703b0 615 }
ea8adc8c 616}
f9f354fc
XL
617
618fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
619 tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
620}