]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_unsafety.rs
improve naming
[rust.git] / src / librustc_mir / 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, Symbol};
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                         "call to unsafe function",
90                         "consult the function's documentation for information on how to avoid \
91                          undefined behavior",
92                         UnsafetyViolationKind::GeneralAndConstFn,
93                     )
94                 }
95
96                 if let ty::FnDef(func_id, _) = func_ty.kind {
97                     self.check_target_features(func_id);
98                 }
99             }
100
101             TerminatorKind::InlineAsm { .. } => self.require_unsafe(
102                 "use of inline assembly",
103                 "inline assembly is entirely unchecked and can cause undefined behavior",
104                 UnsafetyViolationKind::General,
105             ),
106         }
107         self.super_terminator(terminator, location);
108     }
109
110     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
111         self.source_info = statement.source_info;
112         match statement.kind {
113             StatementKind::Assign(..)
114             | StatementKind::FakeRead(..)
115             | StatementKind::SetDiscriminant { .. }
116             | StatementKind::StorageLive(..)
117             | StatementKind::StorageDead(..)
118             | StatementKind::Retag { .. }
119             | StatementKind::AscribeUserType(..)
120             | StatementKind::Nop => {
121                 // safe (at least as emitted during MIR construction)
122             }
123
124             StatementKind::LlvmInlineAsm { .. } => self.require_unsafe(
125                 "use of inline assembly",
126                 "inline assembly is entirely unchecked and can cause undefined behavior",
127                 UnsafetyViolationKind::General,
128             ),
129         }
130         self.super_statement(statement, location);
131     }
132
133     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
134         match rvalue {
135             Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
136                 &AggregateKind::Array(..) | &AggregateKind::Tuple => {}
137                 &AggregateKind::Adt(ref def, ..) => {
138                     match self.tcx.layout_scalar_valid_range(def.did) {
139                         (Bound::Unbounded, Bound::Unbounded) => {}
140                         _ => self.require_unsafe(
141                             "initializing type with `rustc_layout_scalar_valid_range` attr",
142                             "initializing a layout restricted type's field with a value \
143                                 outside the valid range is undefined behavior",
144                             UnsafetyViolationKind::GeneralAndConstFn,
145                         ),
146                     }
147                 }
148                 &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
149                     let UnsafetyCheckResult { violations, unsafe_blocks } =
150                         self.tcx.unsafety_check_result(def_id.expect_local());
151                     self.register_violations(&violations, &unsafe_blocks);
152                 }
153             },
154             // casting pointers to ints is unsafe in const fn because the const evaluator cannot
155             // possibly know what the result of various operations like `address / 2` would be
156             // pointers during const evaluation have no integral address, only an abstract one
157             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty)
158                 if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast =>
159             {
160                 let operand_ty = operand.ty(self.body, self.tcx);
161                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
162                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
163                 match (cast_in, cast_out) {
164                     (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
165                         self.require_unsafe(
166                             "cast of pointer to int",
167                             "casting pointers to integers in constants",
168                             UnsafetyViolationKind::General,
169                         );
170                     }
171                     _ => {}
172                 }
173             }
174             _ => {}
175         }
176         self.super_rvalue(rvalue, location);
177     }
178
179     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
180         // On types with `scalar_valid_range`, prevent
181         // * `&mut x.field`
182         // * `x.field = y;`
183         // * `&x.field` if `field`'s type has interior mutability
184         // because either of these would allow modifying the layout constrained field and
185         // insert values that violate the layout constraints.
186         if context.is_mutating_use() || context.is_borrow() {
187             self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
188         }
189
190         if context.is_borrow() {
191             if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
192                 self.require_unsafe(
193                     "borrow of packed field",
194                     "fields of packed structs might be misaligned: dereferencing a \
195                     misaligned pointer or even just creating a misaligned reference \
196                     is undefined behavior",
197                     UnsafetyViolationKind::BorrowPacked,
198                 );
199             }
200         }
201
202         for (i, elem) in place.projection.iter().enumerate() {
203             let proj_base = &place.projection[..i];
204             if context.is_borrow() {
205                 if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
206                     self.require_unsafe(
207                         "borrow of packed field",
208                         "fields of packed structs might be misaligned: dereferencing a \
209                         misaligned pointer or even just creating a misaligned reference \
210                         is undefined behavior",
211                         UnsafetyViolationKind::BorrowPacked,
212                     );
213                 }
214             }
215             let source_info = self.source_info;
216             if let [] = proj_base {
217                 let decl = &self.body.local_decls[place.local];
218                 if decl.internal {
219                     if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
220                         if self.tcx.is_mutable_static(def_id) {
221                             self.require_unsafe(
222                                 "use of mutable static",
223                                 "mutable statics can be mutated by multiple threads: aliasing \
224                             violations or data races will cause undefined behavior",
225                                 UnsafetyViolationKind::General,
226                             );
227                             return;
228                         } else if self.tcx.is_foreign_item(def_id) {
229                             self.require_unsafe(
230                                 "use of extern static",
231                                 "extern statics are not controlled by the Rust type system: \
232                             invalid data, aliasing violations or data races will cause \
233                             undefined behavior",
234                                 UnsafetyViolationKind::General,
235                             );
236                             return;
237                         }
238                     } else {
239                         // Internal locals are used in the `move_val_init` desugaring.
240                         // We want to check unsafety against the source info of the
241                         // desugaring, rather than the source info of the RHS.
242                         self.source_info = self.body.local_decls[place.local].source_info;
243                     }
244                 }
245             }
246             let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
247             match base_ty.kind {
248                 ty::RawPtr(..) => self.require_unsafe(
249                     "dereference of raw pointer",
250                     "raw pointers may be NULL, dangling or unaligned; they can violate \
251                          aliasing rules and cause data races: all of these are undefined \
252                          behavior",
253                     UnsafetyViolationKind::General,
254                 ),
255                 ty::Adt(adt, _) => {
256                     if adt.is_union() {
257                         if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
258                             || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)
259                             || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
260                         {
261                             let elem_ty = match elem {
262                                 ProjectionElem::Field(_, ty) => ty,
263                                 _ => span_bug!(
264                                     self.source_info.span,
265                                     "non-field projection {:?} from union?",
266                                     place
267                                 ),
268                             };
269                             if !elem_ty.is_copy_modulo_regions(
270                                 self.tcx.at(self.source_info.span),
271                                 self.param_env,
272                             ) {
273                                 self.require_unsafe(
274                                     "assignment to non-`Copy` union field",
275                                     "the previous content of the field will be dropped, which \
276                                      causes undefined behavior if the field was not properly \
277                                      initialized",
278                                     UnsafetyViolationKind::GeneralAndConstFn,
279                                 )
280                             } else {
281                                 // write to non-move union, safe
282                             }
283                         } else {
284                             self.require_unsafe(
285                                 "access to union field",
286                                 "the field may not be properly initialized: using \
287                                  uninitialized data will cause undefined behavior",
288                                 UnsafetyViolationKind::GeneralAndConstFn,
289                             )
290                         }
291                     }
292                 }
293                 _ => {}
294             }
295             self.source_info = source_info;
296         }
297     }
298 }
299
300 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
301     fn require_unsafe(
302         &mut self,
303         description: &'static str,
304         details: &'static str,
305         kind: UnsafetyViolationKind,
306     ) {
307         let source_info = self.source_info;
308         let lint_root = self.body.source_scopes[self.source_info.scope]
309             .local_data
310             .as_ref()
311             .assert_crate_local()
312             .lint_root;
313         self.register_violations(
314             &[UnsafetyViolation {
315                 source_info,
316                 lint_root,
317                 description: Symbol::intern(description),
318                 details: Symbol::intern(details),
319                 kind,
320             }],
321             &[],
322         );
323     }
324
325     fn register_violations(
326         &mut self,
327         violations: &[UnsafetyViolation],
328         unsafe_blocks: &[(hir::HirId, bool)],
329     ) {
330         let safety = self.body.source_scopes[self.source_info.scope]
331             .local_data
332             .as_ref()
333             .assert_crate_local()
334             .safety;
335         let within_unsafe = match safety {
336             // `unsafe` blocks are required in safe code
337             Safety::Safe => {
338                 for violation in violations {
339                     let mut violation = *violation;
340                     match violation.kind {
341                         UnsafetyViolationKind::GeneralAndConstFn
342                         | UnsafetyViolationKind::General => {}
343                         UnsafetyViolationKind::BorrowPacked => {
344                             if self.min_const_fn {
345                                 // const fns don't need to be backwards compatible and can
346                                 // emit these violations as a hard error instead of a backwards
347                                 // compat lint
348                                 violation.kind = UnsafetyViolationKind::General;
349                             }
350                         }
351                         UnsafetyViolationKind::UnsafeFn
352                         | UnsafetyViolationKind::UnsafeFnBorrowPacked => {
353                             bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
354                         }
355                     }
356                     if !self.violations.contains(&violation) {
357                         self.violations.push(violation)
358                     }
359                 }
360                 false
361             }
362             // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
363             Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => {
364                 for violation in violations {
365                     let mut violation = *violation;
366
367                     if violation.kind == UnsafetyViolationKind::BorrowPacked {
368                         violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked;
369                     } else {
370                         violation.kind = UnsafetyViolationKind::UnsafeFn;
371                     }
372                     if !self.violations.contains(&violation) {
373                         self.violations.push(violation)
374                     }
375                 }
376                 false
377             }
378             // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
379             Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
380             Safety::ExplicitUnsafe(hir_id) => {
381                 // mark unsafe block as used if there are any unsafe operations inside
382                 if !violations.is_empty() {
383                     self.used_unsafe.insert(hir_id);
384                 }
385                 // only some unsafety is allowed in const fn
386                 if self.min_const_fn {
387                     for violation in violations {
388                         match violation.kind {
389                             // these unsafe things are stable in const fn
390                             UnsafetyViolationKind::GeneralAndConstFn => {}
391                             // these things are forbidden in const fns
392                             UnsafetyViolationKind::General
393                             | UnsafetyViolationKind::BorrowPacked => {
394                                 let mut violation = *violation;
395                                 // const fns don't need to be backwards compatible and can
396                                 // emit these violations as a hard error instead of a backwards
397                                 // compat lint
398                                 violation.kind = UnsafetyViolationKind::General;
399                                 if !self.violations.contains(&violation) {
400                                     self.violations.push(violation)
401                                 }
402                             }
403                             UnsafetyViolationKind::UnsafeFn
404                             | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!(
405                                 "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
406                             ),
407                         }
408                     }
409                 }
410                 true
411             }
412         };
413         self.inherited_blocks.extend(
414             unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
415         );
416     }
417     fn check_mut_borrowing_layout_constrained_field(
418         &mut self,
419         place: Place<'tcx>,
420         is_mut_use: bool,
421     ) {
422         let mut cursor = place.projection.as_ref();
423         while let &[ref proj_base @ .., elem] = cursor {
424             cursor = proj_base;
425
426             match elem {
427                 // Modifications behind a dereference don't affect the value of
428                 // the pointer.
429                 ProjectionElem::Deref => return,
430                 ProjectionElem::Field(..) => {
431                     let ty =
432                         Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty;
433                     if let ty::Adt(def, _) = ty.kind {
434                         if self.tcx.layout_scalar_valid_range(def.did)
435                             != (Bound::Unbounded, Bound::Unbounded)
436                         {
437                             let (description, details) = if is_mut_use {
438                                 (
439                                     "mutation of layout constrained field",
440                                     "mutating layout constrained fields cannot statically be \
441                                         checked for valid values",
442                                 )
443
444                             // Check `is_freeze` as late as possible to avoid cycle errors
445                             // with opaque types.
446                             } else if !place
447                                 .ty(self.body, self.tcx)
448                                 .ty
449                                 .is_freeze(self.tcx.at(self.source_info.span), self.param_env)
450                             {
451                                 (
452                                     "borrow of layout constrained field with interior \
453                                         mutability",
454                                     "references to fields of layout constrained fields \
455                                         lose the constraints. Coupled with interior mutability, \
456                                         the field can be changed to invalid values",
457                                 )
458                             } else {
459                                 continue;
460                             };
461                             self.require_unsafe(
462                                 description,
463                                 details,
464                                 UnsafetyViolationKind::GeneralAndConstFn,
465                             );
466                         }
467                     }
468                 }
469                 _ => {}
470             }
471         }
472     }
473
474     /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
475     /// the called function has target features the calling function hasn't.
476     fn check_target_features(&mut self, func_did: DefId) {
477         let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
478         let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
479
480         // Is `callee_features` a subset of `calling_features`?
481         if !callee_features.iter().all(|feature| self_features.contains(feature)) {
482             self.require_unsafe(
483                 "call to function with `#[target_feature]`",
484                 "can only be called if the required target features are available",
485                 UnsafetyViolationKind::GeneralAndConstFn,
486             )
487         }
488     }
489 }
490
491 pub(crate) fn provide(providers: &mut Providers) {
492     *providers = Providers {
493         unsafety_check_result: |tcx, def_id| {
494             unsafety_check_result(tcx, ty::WithOptConstParam::dummy(def_id))
495         },
496         unsafety_check_result_const_arg: |tcx, (did, param_did)| {
497             unsafety_check_result(
498                 tcx,
499                 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
500             )
501         },
502         unsafe_derive_on_repr_packed,
503         ..*providers
504     };
505 }
506
507 struct UnusedUnsafeVisitor<'a> {
508     used_unsafe: &'a FxHashSet<hir::HirId>,
509     unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
510 }
511
512 impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
513     type Map = intravisit::ErasedMap<'tcx>;
514
515     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
516         intravisit::NestedVisitorMap::None
517     }
518
519     fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
520         intravisit::walk_block(self, block);
521
522         if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
523             self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
524         }
525     }
526 }
527
528 fn check_unused_unsafe(
529     tcx: TyCtxt<'_>,
530     def_id: LocalDefId,
531     used_unsafe: &FxHashSet<hir::HirId>,
532     unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
533 ) {
534     let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().as_local_hir_id(def_id));
535
536     let body_id = match body_id {
537         Some(body) => body,
538         None => {
539             debug!("check_unused_unsafe({:?}) - no body found", def_id);
540             return;
541         }
542     };
543     let body = tcx.hir().body(body_id);
544     debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
545
546     let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
547     intravisit::Visitor::visit_body(&mut visitor, body);
548 }
549
550 fn unsafety_check_result<'tcx>(
551     tcx: TyCtxt<'tcx>,
552     def: ty::WithOptConstParam<LocalDefId>,
553 ) -> &'tcx UnsafetyCheckResult {
554     if def.const_param_did.is_none() {
555         if let Some(param_did) = tcx.opt_const_param_of(def.did) {
556             return tcx.unsafety_check_result_const_arg((def.did, param_did));
557         }
558     }
559
560     debug!("unsafety_violations({:?})", def);
561
562     // N.B., this borrow is valid because all the consumers of
563     // `mir_built` force this.
564     let body = &tcx.mir_built(def).borrow();
565
566     let param_env = tcx.param_env(def.did);
567
568     let id = tcx.hir().as_local_hir_id(def.did);
569     let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
570         hir::BodyOwnerKind::Closure => (false, false),
571         hir::BodyOwnerKind::Fn => {
572             (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
573         }
574         hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
575     };
576     let mut checker =
577         UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
578     checker.visit_body(&body);
579
580     check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
581
582     tcx.arena.alloc(UnsafetyCheckResult {
583         violations: checker.violations.into(),
584         unsafe_blocks: checker.inherited_blocks.into(),
585     })
586 }
587
588 fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
589     let lint_hir_id = tcx.hir().as_local_hir_id(def_id);
590
591     tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
592         // FIXME: when we make this a hard error, this should have its
593         // own error code.
594         let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
595             "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
596              type or const parameters (error E0133)"
597                 .to_string()
598         } else {
599             "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
600              does not derive Copy (error E0133)"
601                 .to_string()
602         };
603         lint.build(&message).emit()
604     });
605 }
606
607 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
608 fn is_enclosed(
609     tcx: TyCtxt<'_>,
610     used_unsafe: &FxHashSet<hir::HirId>,
611     id: hir::HirId,
612 ) -> Option<(String, hir::HirId)> {
613     let parent_id = tcx.hir().get_parent_node(id);
614     if parent_id != id {
615         if used_unsafe.contains(&parent_id) {
616             Some(("block".to_string(), parent_id))
617         } else if let Some(Node::Item(&hir::Item {
618             kind: hir::ItemKind::Fn(ref sig, _, _), ..
619         })) = tcx.hir().find(parent_id)
620         {
621             if sig.header.unsafety == hir::Unsafety::Unsafe
622                 && !tcx.features().unsafe_block_in_unsafe_fn
623             {
624                 Some(("fn".to_string(), parent_id))
625             } else {
626                 None
627             }
628         } else {
629             is_enclosed(tcx, used_unsafe, parent_id)
630         }
631     } else {
632         None
633     }
634 }
635
636 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
637     let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
638     tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
639         let msg = "unnecessary `unsafe` block";
640         let mut db = lint.build(msg);
641         db.span_label(span, msg);
642         if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
643             db.span_label(
644                 tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
645                 format!("because it's nested under this `unsafe` {}", kind),
646             );
647         }
648         db.emit();
649     });
650 }
651
652 fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
653     debug!("builtin_derive_def_id({:?})", def_id);
654     if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
655         if tcx.has_attr(impl_def_id, sym::automatically_derived) {
656             debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
657             Some(impl_def_id)
658         } else {
659             debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
660             None
661         }
662     } else {
663         debug!("builtin_derive_def_id({:?}) - not a method", def_id);
664         None
665     }
666 }
667
668 pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
669     debug!("check_unsafety({:?})", def_id);
670
671     // closures are handled by their parent fn.
672     if tcx.is_closure(def_id.to_def_id()) {
673         return;
674     }
675
676     let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
677
678     for &UnsafetyViolation { source_info, lint_root, description, details, kind } in
679         violations.iter()
680     {
681         // Report an error.
682         let unsafe_fn_msg =
683             if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
684
685         match kind {
686             UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
687                 // once
688                 struct_span_err!(
689                     tcx.sess,
690                     source_info.span,
691                     E0133,
692                     "{} is unsafe and requires unsafe{} block",
693                     description,
694                     unsafe_fn_msg,
695                 )
696                 .span_label(source_info.span, &*description.as_str())
697                 .note(&details.as_str())
698                 .emit();
699             }
700             UnsafetyViolationKind::BorrowPacked => {
701                 if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) {
702                     // If a method is defined in the local crate,
703                     // the impl containing that method should also be.
704                     tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
705                 } else {
706                     tcx.struct_span_lint_hir(
707                         SAFE_PACKED_BORROWS,
708                         lint_root,
709                         source_info.span,
710                         |lint| {
711                             lint.build(&format!(
712                                 "{} is unsafe and requires unsafe{} block (error E0133)",
713                                 description, unsafe_fn_msg,
714                             ))
715                             .note(&details.as_str())
716                             .emit()
717                         },
718                     )
719                 }
720             }
721             UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
722                 UNSAFE_OP_IN_UNSAFE_FN,
723                 lint_root,
724                 source_info.span,
725                 |lint| {
726                     lint.build(&format!(
727                         "{} is unsafe and requires unsafe block (error E0133)",
728                         description,
729                     ))
730                     .span_label(source_info.span, &*description.as_str())
731                     .note(&details.as_str())
732                     .emit();
733                 },
734             ),
735             UnsafetyViolationKind::UnsafeFnBorrowPacked => {
736                 // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
737                 // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
738                 // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
739                 // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
740                 //
741                 // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
742                 // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
743                 // should only issue a warning for the sake of backwards compatibility.
744                 //
745                 // The solution those 2 expectations is to always take the minimum of both lints.
746                 // This prevent any new errors (unless both lints are explicitely set to `deny`).
747                 let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0
748                     <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0
749                 {
750                     SAFE_PACKED_BORROWS
751                 } else {
752                     UNSAFE_OP_IN_UNSAFE_FN
753                 };
754                 tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| {
755                     lint.build(&format!(
756                         "{} is unsafe and requires unsafe block (error E0133)",
757                         description,
758                     ))
759                     .span_label(source_info.span, &*description.as_str())
760                     .note(&details.as_str())
761                     .emit();
762                 })
763             }
764         }
765     }
766
767     let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default();
768     for &(block_id, is_used) in unsafe_blocks.iter() {
769         if is_used {
770             unsafe_used.insert(block_id);
771         } else {
772             unsafe_unused.push(block_id);
773         }
774     }
775     // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe
776     // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass.
777     unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id));
778
779     for &block_id in &unsafe_unused {
780         report_unused_unsafe(tcx, &unsafe_used, block_id);
781     }
782 }
783
784 fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
785     tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
786 }