]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/transform/check_unsafety.rs
Rollup merge of #81904 - jhpratt:const_int_fn-stabilization, r=jyn514
[rust.git] / compiler / rustc_mir / src / transform / check_unsafety.rs
1 use rustc_data_structures::fx::FxHashSet;
2 use rustc_errors::struct_span_err;
3 use rustc_hir as hir;
4 use rustc_hir::def_id::{DefId, LocalDefId};
5 use rustc_hir::hir_id::HirId;
6 use rustc_hir::intravisit;
7 use rustc_hir::Node;
8 use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
9 use rustc_middle::mir::*;
10 use rustc_middle::ty::cast::CastTy;
11 use rustc_middle::ty::query::Providers;
12 use rustc_middle::ty::{self, TyCtxt};
13 use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
14 use rustc_session::lint::Level;
15 use rustc_span::symbol::sym;
16
17 use std::ops::Bound;
18
19 use crate::const_eval::is_min_const_fn;
20 use crate::util;
21
22 pub struct UnsafetyChecker<'a, 'tcx> {
23     body: &'a Body<'tcx>,
24     body_did: LocalDefId,
25     const_context: bool,
26     min_const_fn: bool,
27     violations: Vec<UnsafetyViolation>,
28     source_info: SourceInfo,
29     tcx: TyCtxt<'tcx>,
30     param_env: ty::ParamEnv<'tcx>,
31     /// Mark an `unsafe` block as used, so we don't lint it.
32     used_unsafe: FxHashSet<hir::HirId>,
33     inherited_blocks: Vec<(hir::HirId, bool)>,
34 }
35
36 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
37     fn new(
38         const_context: bool,
39         min_const_fn: bool,
40         body: &'a Body<'tcx>,
41         body_did: LocalDefId,
42         tcx: TyCtxt<'tcx>,
43         param_env: ty::ParamEnv<'tcx>,
44     ) -> Self {
45         // sanity check
46         if min_const_fn {
47             assert!(const_context);
48         }
49         Self {
50             body,
51             body_did,
52             const_context,
53             min_const_fn,
54             violations: vec![],
55             source_info: SourceInfo::outermost(body.span),
56             tcx,
57             param_env,
58             used_unsafe: Default::default(),
59             inherited_blocks: vec![],
60         }
61     }
62 }
63
64 impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
65     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
66         self.source_info = terminator.source_info;
67         match terminator.kind {
68             TerminatorKind::Goto { .. }
69             | TerminatorKind::SwitchInt { .. }
70             | TerminatorKind::Drop { .. }
71             | TerminatorKind::Yield { .. }
72             | TerminatorKind::Assert { .. }
73             | TerminatorKind::DropAndReplace { .. }
74             | TerminatorKind::GeneratorDrop
75             | TerminatorKind::Resume
76             | TerminatorKind::Abort
77             | TerminatorKind::Return
78             | TerminatorKind::Unreachable
79             | TerminatorKind::FalseEdge { .. }
80             | TerminatorKind::FalseUnwind { .. } => {
81                 // safe (at least as emitted during MIR construction)
82             }
83
84             TerminatorKind::Call { ref func, .. } => {
85                 let func_ty = func.ty(self.body, self.tcx);
86                 let sig = func_ty.fn_sig(self.tcx);
87                 if let hir::Unsafety::Unsafe = sig.unsafety() {
88                     self.require_unsafe(
89                         UnsafetyViolationKind::GeneralAndConstFn,
90                         UnsafetyViolationDetails::CallToUnsafeFunction,
91                     )
92                 }
93
94                 if let ty::FnDef(func_id, _) = func_ty.kind() {
95                     self.check_target_features(*func_id);
96                 }
97             }
98
99             TerminatorKind::InlineAsm { .. } => self.require_unsafe(
100                 UnsafetyViolationKind::General,
101                 UnsafetyViolationDetails::UseOfInlineAssembly,
102             ),
103         }
104         self.super_terminator(terminator, location);
105     }
106
107     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
108         self.source_info = statement.source_info;
109         match statement.kind {
110             StatementKind::Assign(..)
111             | StatementKind::FakeRead(..)
112             | StatementKind::SetDiscriminant { .. }
113             | StatementKind::StorageLive(..)
114             | StatementKind::StorageDead(..)
115             | StatementKind::Retag { .. }
116             | StatementKind::AscribeUserType(..)
117             | StatementKind::Coverage(..)
118             | StatementKind::Nop => {
119                 // safe (at least as emitted during MIR construction)
120             }
121
122             StatementKind::LlvmInlineAsm { .. } => self.require_unsafe(
123                 UnsafetyViolationKind::General,
124                 UnsafetyViolationDetails::UseOfInlineAssembly,
125             ),
126         }
127         self.super_statement(statement, location);
128     }
129
130     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
131         match rvalue {
132             Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
133                 &AggregateKind::Array(..) | &AggregateKind::Tuple => {}
134                 &AggregateKind::Adt(ref def, ..) => {
135                     match self.tcx.layout_scalar_valid_range(def.did) {
136                         (Bound::Unbounded, Bound::Unbounded) => {}
137                         _ => self.require_unsafe(
138                             UnsafetyViolationKind::GeneralAndConstFn,
139                             UnsafetyViolationDetails::InitializingTypeWith,
140                         ),
141                     }
142                 }
143                 &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
144                     let UnsafetyCheckResult { violations, unsafe_blocks } =
145                         self.tcx.unsafety_check_result(def_id.expect_local());
146                     self.register_violations(&violations, &unsafe_blocks);
147                 }
148             },
149             // casting pointers to ints is unsafe in const fn because the const evaluator cannot
150             // possibly know what the result of various operations like `address / 2` would be
151             // pointers during const evaluation have no integral address, only an abstract one
152             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty)
153                 if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast =>
154             {
155                 let operand_ty = operand.ty(self.body, self.tcx);
156                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
157                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
158                 match (cast_in, cast_out) {
159                     (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
160                         self.require_unsafe(
161                             UnsafetyViolationKind::General,
162                             UnsafetyViolationDetails::CastOfPointerToInt,
163                         );
164                     }
165                     _ => {}
166                 }
167             }
168             _ => {}
169         }
170         self.super_rvalue(rvalue, location);
171     }
172
173     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
174         // On types with `scalar_valid_range`, prevent
175         // * `&mut x.field`
176         // * `x.field = y;`
177         // * `&x.field` if `field`'s type has interior mutability
178         // because either of these would allow modifying the layout constrained field and
179         // insert values that violate the layout constraints.
180         if context.is_mutating_use() || context.is_borrow() {
181             self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
182         }
183
184         // Check for borrows to packed fields.
185         // `is_disaligned` already traverses the place to consider all projections after the last
186         // `Deref`, so this only needs to be called once at the top level.
187         if context.is_borrow() {
188             if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
189                 self.require_unsafe(
190                     UnsafetyViolationKind::BorrowPacked,
191                     UnsafetyViolationDetails::BorrowOfPackedField,
192                 );
193             }
194         }
195
196         // Some checks below need the extra metainfo of the local declaration.
197         let decl = &self.body.local_decls[place.local];
198
199         // Check the base local: it might be an unsafe-to-access static. We only check derefs of the
200         // temporary holding the static pointer to avoid duplicate errors
201         // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
202         if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
203             // If the projection root is an artifical local that we introduced when
204             // desugaring `static`, give a more specific error message
205             // (avoid the general "raw pointer" clause below, that would only be confusing).
206             if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
207                 if self.tcx.is_mutable_static(def_id) {
208                     self.require_unsafe(
209                         UnsafetyViolationKind::General,
210                         UnsafetyViolationDetails::UseOfMutableStatic,
211                     );
212                     return;
213                 } else if self.tcx.is_foreign_item(def_id) {
214                     self.require_unsafe(
215                         UnsafetyViolationKind::General,
216                         UnsafetyViolationDetails::UseOfExternStatic,
217                     );
218                     return;
219                 }
220             }
221         }
222
223         // Check for raw pointer `Deref`.
224         for (base, proj) in place.iter_projections() {
225             if proj == ProjectionElem::Deref {
226                 let base_ty = base.ty(self.body, self.tcx).ty;
227                 if base_ty.is_unsafe_ptr() {
228                     self.require_unsafe(
229                         UnsafetyViolationKind::GeneralAndConstFn,
230                         UnsafetyViolationDetails::DerefOfRawPointer,
231                     )
232                 }
233             }
234         }
235
236         // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
237         // whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
238         let mut saw_deref = false;
239         for (base, proj) in place.iter_projections().rev() {
240             if proj == ProjectionElem::Deref {
241                 saw_deref = true;
242                 continue;
243             }
244
245             let base_ty = base.ty(self.body, self.tcx).ty;
246             if base_ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
247                 // If we did not hit a `Deref` yet and the overall place use is an assignment, the
248                 // rules are different.
249                 let assign_to_field = !saw_deref
250                     && matches!(
251                         context,
252                         PlaceContext::MutatingUse(
253                             MutatingUseContext::Store
254                                 | MutatingUseContext::Drop
255                                 | MutatingUseContext::AsmOutput
256                         )
257                     );
258                 // If this is just an assignment, determine if the assigned type needs dropping.
259                 if assign_to_field {
260                     // We have to check the actual type of the assignment, as that determines if the
261                     // old value is being dropped.
262                     let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
263                     // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
264                     let manually_drop = assigned_ty
265                         .ty_adt_def()
266                         .map_or(false, |adt_def| adt_def.is_manually_drop());
267                     let nodrop = manually_drop
268                         || assigned_ty.is_copy_modulo_regions(
269                             self.tcx.at(self.source_info.span),
270                             self.param_env,
271                         );
272                     if !nodrop {
273                         self.require_unsafe(
274                             UnsafetyViolationKind::GeneralAndConstFn,
275                             UnsafetyViolationDetails::AssignToDroppingUnionField,
276                         );
277                     } else {
278                         // write to non-drop union field, safe
279                     }
280                 } else {
281                     self.require_unsafe(
282                         UnsafetyViolationKind::GeneralAndConstFn,
283                         UnsafetyViolationDetails::AccessToUnionField,
284                     )
285                 }
286             }
287         }
288     }
289 }
290
291 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
292     fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
293         let source_info = self.source_info;
294         let lint_root = self.body.source_scopes[self.source_info.scope]
295             .local_data
296             .as_ref()
297             .assert_crate_local()
298             .lint_root;
299         self.register_violations(
300             &[UnsafetyViolation { source_info, lint_root, kind, details }],
301             &[],
302         );
303     }
304
305     fn register_violations(
306         &mut self,
307         violations: &[UnsafetyViolation],
308         unsafe_blocks: &[(hir::HirId, bool)],
309     ) {
310         let safety = self.body.source_scopes[self.source_info.scope]
311             .local_data
312             .as_ref()
313             .assert_crate_local()
314             .safety;
315         let within_unsafe = match safety {
316             // `unsafe` blocks are required in safe code
317             Safety::Safe => {
318                 for violation in violations {
319                     let mut violation = *violation;
320                     match violation.kind {
321                         UnsafetyViolationKind::GeneralAndConstFn
322                         | UnsafetyViolationKind::General => {}
323                         UnsafetyViolationKind::BorrowPacked => {
324                             if self.min_const_fn {
325                                 // const fns don't need to be backwards compatible and can
326                                 // emit these violations as a hard error instead of a backwards
327                                 // compat lint
328                                 violation.kind = UnsafetyViolationKind::General;
329                             }
330                         }
331                         UnsafetyViolationKind::UnsafeFn
332                         | UnsafetyViolationKind::UnsafeFnBorrowPacked => {
333                             bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
334                         }
335                     }
336                     if !self.violations.contains(&violation) {
337                         self.violations.push(violation)
338                     }
339                 }
340                 false
341             }
342             // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
343             Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => {
344                 for violation in violations {
345                     let mut violation = *violation;
346
347                     if violation.kind == UnsafetyViolationKind::BorrowPacked {
348                         violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked;
349                     } else {
350                         violation.kind = UnsafetyViolationKind::UnsafeFn;
351                     }
352                     if !self.violations.contains(&violation) {
353                         self.violations.push(violation)
354                     }
355                 }
356                 false
357             }
358             // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
359             Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
360             Safety::ExplicitUnsafe(hir_id) => {
361                 // mark unsafe block as used if there are any unsafe operations inside
362                 if !violations.is_empty() {
363                     self.used_unsafe.insert(hir_id);
364                 }
365                 // only some unsafety is allowed in const fn
366                 if self.min_const_fn {
367                     for violation in violations {
368                         match violation.kind {
369                             // these unsafe things are stable in const fn
370                             UnsafetyViolationKind::GeneralAndConstFn => {}
371                             // these things are forbidden in const fns
372                             UnsafetyViolationKind::General
373                             | UnsafetyViolationKind::BorrowPacked => {
374                                 let mut violation = *violation;
375                                 // const fns don't need to be backwards compatible and can
376                                 // emit these violations as a hard error instead of a backwards
377                                 // compat lint
378                                 violation.kind = UnsafetyViolationKind::General;
379                                 if !self.violations.contains(&violation) {
380                                     self.violations.push(violation)
381                                 }
382                             }
383                             UnsafetyViolationKind::UnsafeFn
384                             | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!(
385                                 "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
386                             ),
387                         }
388                     }
389                 }
390                 true
391             }
392         };
393         self.inherited_blocks.extend(
394             unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
395         );
396     }
397     fn check_mut_borrowing_layout_constrained_field(
398         &mut self,
399         place: Place<'tcx>,
400         is_mut_use: bool,
401     ) {
402         for (place_base, elem) in place.iter_projections().rev() {
403             match elem {
404                 // Modifications behind a dereference don't affect the value of
405                 // the pointer.
406                 ProjectionElem::Deref => return,
407                 ProjectionElem::Field(..) => {
408                     let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
409                     if let ty::Adt(def, _) = ty.kind() {
410                         if self.tcx.layout_scalar_valid_range(def.did)
411                             != (Bound::Unbounded, Bound::Unbounded)
412                         {
413                             let details = if is_mut_use {
414                                 UnsafetyViolationDetails::MutationOfLayoutConstrainedField
415
416                             // Check `is_freeze` as late as possible to avoid cycle errors
417                             // with opaque types.
418                             } else if !place
419                                 .ty(self.body, self.tcx)
420                                 .ty
421                                 .is_freeze(self.tcx.at(self.source_info.span), self.param_env)
422                             {
423                                 UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
424                             } else {
425                                 continue;
426                             };
427                             self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
428                         }
429                     }
430                 }
431                 _ => {}
432             }
433         }
434     }
435
436     /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
437     /// the called function has target features the calling function hasn't.
438     fn check_target_features(&mut self, func_did: DefId) {
439         let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
440         let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
441
442         // Is `callee_features` a subset of `calling_features`?
443         if !callee_features.iter().all(|feature| self_features.contains(feature)) {
444             self.require_unsafe(
445                 UnsafetyViolationKind::GeneralAndConstFn,
446                 UnsafetyViolationDetails::CallToFunctionWith,
447             )
448         }
449     }
450 }
451
452 pub(crate) fn provide(providers: &mut Providers) {
453     *providers = Providers {
454         unsafety_check_result: |tcx, def_id| {
455             if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
456                 tcx.unsafety_check_result_for_const_arg(def)
457             } else {
458                 unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id))
459             }
460         },
461         unsafety_check_result_for_const_arg: |tcx, (did, param_did)| {
462             unsafety_check_result(
463                 tcx,
464                 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
465             )
466         },
467         unsafe_derive_on_repr_packed,
468         ..*providers
469     };
470 }
471
472 struct UnusedUnsafeVisitor<'a> {
473     used_unsafe: &'a FxHashSet<hir::HirId>,
474     unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
475 }
476
477 impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
478     type Map = intravisit::ErasedMap<'tcx>;
479
480     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
481         intravisit::NestedVisitorMap::None
482     }
483
484     fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
485         intravisit::walk_block(self, block);
486
487         if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
488             self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
489         }
490     }
491 }
492
493 fn check_unused_unsafe(
494     tcx: TyCtxt<'_>,
495     def_id: LocalDefId,
496     used_unsafe: &FxHashSet<hir::HirId>,
497     unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
498 ) {
499     let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().local_def_id_to_hir_id(def_id));
500
501     let body_id = match body_id {
502         Some(body) => body,
503         None => {
504             debug!("check_unused_unsafe({:?}) - no body found", def_id);
505             return;
506         }
507     };
508     let body = tcx.hir().body(body_id);
509     debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
510
511     let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
512     intravisit::Visitor::visit_body(&mut visitor, body);
513 }
514
515 fn unsafety_check_result<'tcx>(
516     tcx: TyCtxt<'tcx>,
517     def: ty::WithOptConstParam<LocalDefId>,
518 ) -> &'tcx UnsafetyCheckResult {
519     debug!("unsafety_violations({:?})", def);
520
521     // N.B., this borrow is valid because all the consumers of
522     // `mir_built` force this.
523     let body = &tcx.mir_built(def).borrow();
524
525     let param_env = tcx.param_env(def.did);
526
527     let id = tcx.hir().local_def_id_to_hir_id(def.did);
528     let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
529         hir::BodyOwnerKind::Closure => (false, false),
530         hir::BodyOwnerKind::Fn => {
531             (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
532         }
533         hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
534     };
535     let mut checker =
536         UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
537     checker.visit_body(&body);
538
539     check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
540
541     tcx.arena.alloc(UnsafetyCheckResult {
542         violations: checker.violations.into(),
543         unsafe_blocks: checker.inherited_blocks.into(),
544     })
545 }
546
547 fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
548     let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
549
550     tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
551         // FIXME: when we make this a hard error, this should have its
552         // own error code.
553         let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
554             "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
555              type or const parameters (error E0133)"
556                 .to_string()
557         } else {
558             "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
559              does not derive Copy (error E0133)"
560                 .to_string()
561         };
562         lint.build(&message).emit()
563     });
564 }
565
566 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
567 fn is_enclosed(
568     tcx: TyCtxt<'_>,
569     used_unsafe: &FxHashSet<hir::HirId>,
570     id: hir::HirId,
571     unsafe_op_in_unsafe_fn_allowed: bool,
572 ) -> Option<(&'static str, hir::HirId)> {
573     let parent_id = tcx.hir().get_parent_node(id);
574     if parent_id != id {
575         if used_unsafe.contains(&parent_id) {
576             Some(("block", parent_id))
577         } else if let Some(Node::Item(&hir::Item {
578             kind: hir::ItemKind::Fn(ref sig, _, _), ..
579         })) = tcx.hir().find(parent_id)
580         {
581             if sig.header.unsafety == hir::Unsafety::Unsafe && unsafe_op_in_unsafe_fn_allowed {
582                 Some(("fn", parent_id))
583             } else {
584                 None
585             }
586         } else {
587             is_enclosed(tcx, used_unsafe, parent_id, unsafe_op_in_unsafe_fn_allowed)
588         }
589     } else {
590         None
591     }
592 }
593
594 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
595     let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
596     tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
597         let msg = "unnecessary `unsafe` block";
598         let mut db = lint.build(msg);
599         db.span_label(span, msg);
600         if let Some((kind, id)) =
601             is_enclosed(tcx, used_unsafe, id, unsafe_op_in_unsafe_fn_allowed(tcx, id))
602         {
603             db.span_label(
604                 tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
605                 format!("because it's nested under this `unsafe` {}", kind),
606             );
607         }
608         db.emit();
609     });
610 }
611
612 fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
613     debug!("builtin_derive_def_id({:?})", def_id);
614     if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
615         if tcx.has_attr(impl_def_id, sym::automatically_derived) {
616             debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
617             Some(impl_def_id)
618         } else {
619             debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
620             None
621         }
622     } else {
623         debug!("builtin_derive_def_id({:?}) - not a method", def_id);
624         None
625     }
626 }
627
628 pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
629     debug!("check_unsafety({:?})", def_id);
630
631     // closures are handled by their parent fn.
632     if tcx.is_closure(def_id.to_def_id()) {
633         return;
634     }
635
636     let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
637
638     for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
639         let (description, note) = details.description_and_note();
640
641         // Report an error.
642         let unsafe_fn_msg =
643             if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
644
645         match kind {
646             UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
647                 // once
648                 struct_span_err!(
649                     tcx.sess,
650                     source_info.span,
651                     E0133,
652                     "{} is unsafe and requires unsafe{} block",
653                     description,
654                     unsafe_fn_msg,
655                 )
656                 .span_label(source_info.span, description)
657                 .note(note)
658                 .emit();
659             }
660             UnsafetyViolationKind::BorrowPacked => {
661                 if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) {
662                     // If a method is defined in the local crate,
663                     // the impl containing that method should also be.
664                     tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
665                 } else {
666                     tcx.struct_span_lint_hir(
667                         SAFE_PACKED_BORROWS,
668                         lint_root,
669                         source_info.span,
670                         |lint| {
671                             lint.build(&format!(
672                                 "{} is unsafe and requires unsafe{} block (error E0133)",
673                                 description, unsafe_fn_msg,
674                             ))
675                             .note(note)
676                             .emit()
677                         },
678                     )
679                 }
680             }
681             UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
682                 UNSAFE_OP_IN_UNSAFE_FN,
683                 lint_root,
684                 source_info.span,
685                 |lint| {
686                     lint.build(&format!(
687                         "{} is unsafe and requires unsafe block (error E0133)",
688                         description,
689                     ))
690                     .span_label(source_info.span, description)
691                     .note(note)
692                     .emit();
693                 },
694             ),
695             UnsafetyViolationKind::UnsafeFnBorrowPacked => {
696                 // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
697                 // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
698                 // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
699                 // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
700                 //
701                 // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
702                 // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
703                 // should only issue a warning for the sake of backwards compatibility.
704                 //
705                 // The solution those 2 expectations is to always take the minimum of both lints.
706                 // This prevent any new errors (unless both lints are explicitly set to `deny`).
707                 let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0
708                     <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0
709                 {
710                     SAFE_PACKED_BORROWS
711                 } else {
712                     UNSAFE_OP_IN_UNSAFE_FN
713                 };
714                 tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| {
715                     lint.build(&format!(
716                         "{} is unsafe and requires unsafe block (error E0133)",
717                         description,
718                     ))
719                     .span_label(source_info.span, description)
720                     .note(note)
721                     .emit();
722                 })
723             }
724         }
725     }
726
727     let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default();
728     for &(block_id, is_used) in unsafe_blocks.iter() {
729         if is_used {
730             unsafe_used.insert(block_id);
731         } else {
732             unsafe_unused.push(block_id);
733         }
734     }
735     // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe
736     // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass.
737     unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id));
738
739     for &block_id in &unsafe_unused {
740         report_unused_unsafe(tcx, &unsafe_used, block_id);
741     }
742 }
743
744 fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
745     tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
746 }