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