]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/transform/check_unsafety.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.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 if context.is_borrow() {
185 if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
186 self.require_unsafe(
187 UnsafetyViolationKind::BorrowPacked,
188 UnsafetyViolationDetails::BorrowOfPackedField,
189 );
190 }
191 }
192
193 for (i, elem) in place.projection.iter().enumerate() {
194 let proj_base = &place.projection[..i];
195 if context.is_borrow() {
196 if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
197 self.require_unsafe(
198 UnsafetyViolationKind::BorrowPacked,
199 UnsafetyViolationDetails::BorrowOfPackedField,
200 );
201 }
202 }
203 let source_info = self.source_info;
204 if let [] = proj_base {
205 let decl = &self.body.local_decls[place.local];
206 if decl.internal {
207 if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
208 if self.tcx.is_mutable_static(def_id) {
209 self.require_unsafe(
210 UnsafetyViolationKind::General,
211 UnsafetyViolationDetails::UseOfMutableStatic,
212 );
213 return;
214 } else if self.tcx.is_foreign_item(def_id) {
215 self.require_unsafe(
216 UnsafetyViolationKind::General,
217 UnsafetyViolationDetails::UseOfExternStatic,
218 );
219 return;
220 }
221 } else {
222 // Internal locals are used in the `move_val_init` desugaring.
223 // We want to check unsafety against the source info of the
224 // desugaring, rather than the source info of the RHS.
225 self.source_info = self.body.local_decls[place.local].source_info;
226 }
227 }
228 }
229 let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
230 match base_ty.kind() {
231 ty::RawPtr(..) => self.require_unsafe(
232 UnsafetyViolationKind::GeneralAndConstFn,
233 UnsafetyViolationDetails::DerefOfRawPointer,
234 ),
235 ty::Adt(adt, _) => {
236 if adt.is_union() {
237 if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
238 || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)
239 || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
240 {
241 let elem_ty = match elem {
242 ProjectionElem::Field(_, ty) => ty,
243 _ => span_bug!(
244 self.source_info.span,
245 "non-field projection {:?} from union?",
246 place
247 ),
248 };
249 if !elem_ty.is_copy_modulo_regions(
250 self.tcx.at(self.source_info.span),
251 self.param_env,
252 ) {
253 self.require_unsafe(
254 UnsafetyViolationKind::GeneralAndConstFn,
255 UnsafetyViolationDetails::AssignToNonCopyUnionField,
256 )
257 } else {
258 // write to non-move union, safe
259 }
260 } else {
261 self.require_unsafe(
262 UnsafetyViolationKind::GeneralAndConstFn,
263 UnsafetyViolationDetails::AccessToUnionField,
264 )
265 }
266 }
267 }
268 _ => {}
269 }
270 self.source_info = source_info;
271 }
272 }
273 }
274
275 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
276 fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
277 let source_info = self.source_info;
278 let lint_root = self.body.source_scopes[self.source_info.scope]
279 .local_data
280 .as_ref()
281 .assert_crate_local()
282 .lint_root;
283 self.register_violations(
284 &[UnsafetyViolation { source_info, lint_root, kind, details }],
285 &[],
286 );
287 }
288
289 fn register_violations(
290 &mut self,
291 violations: &[UnsafetyViolation],
292 unsafe_blocks: &[(hir::HirId, bool)],
293 ) {
294 let safety = self.body.source_scopes[self.source_info.scope]
295 .local_data
296 .as_ref()
297 .assert_crate_local()
298 .safety;
299 let within_unsafe = match safety {
300 // `unsafe` blocks are required in safe code
301 Safety::Safe => {
302 for violation in violations {
303 let mut violation = *violation;
304 match violation.kind {
305 UnsafetyViolationKind::GeneralAndConstFn
306 | UnsafetyViolationKind::General => {}
307 UnsafetyViolationKind::BorrowPacked => {
308 if self.min_const_fn {
309 // const fns don't need to be backwards compatible and can
310 // emit these violations as a hard error instead of a backwards
311 // compat lint
312 violation.kind = UnsafetyViolationKind::General;
313 }
314 }
315 UnsafetyViolationKind::UnsafeFn
316 | UnsafetyViolationKind::UnsafeFnBorrowPacked => {
317 bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
318 }
319 }
320 if !self.violations.contains(&violation) {
321 self.violations.push(violation)
322 }
323 }
324 false
325 }
326 // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
327 Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => {
328 for violation in violations {
329 let mut violation = *violation;
330
331 if violation.kind == UnsafetyViolationKind::BorrowPacked {
332 violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked;
333 } else {
334 violation.kind = UnsafetyViolationKind::UnsafeFn;
335 }
336 if !self.violations.contains(&violation) {
337 self.violations.push(violation)
338 }
339 }
340 false
341 }
342 // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585)
343 Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
344 Safety::ExplicitUnsafe(hir_id) => {
345 // mark unsafe block as used if there are any unsafe operations inside
346 if !violations.is_empty() {
347 self.used_unsafe.insert(hir_id);
348 }
349 // only some unsafety is allowed in const fn
350 if self.min_const_fn {
351 for violation in violations {
352 match violation.kind {
353 // these unsafe things are stable in const fn
354 UnsafetyViolationKind::GeneralAndConstFn => {}
355 // these things are forbidden in const fns
356 UnsafetyViolationKind::General
357 | UnsafetyViolationKind::BorrowPacked => {
358 let mut violation = *violation;
359 // const fns don't need to be backwards compatible and can
360 // emit these violations as a hard error instead of a backwards
361 // compat lint
362 violation.kind = UnsafetyViolationKind::General;
363 if !self.violations.contains(&violation) {
364 self.violations.push(violation)
365 }
366 }
367 UnsafetyViolationKind::UnsafeFn
368 | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!(
369 "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
370 ),
371 }
372 }
373 }
374 true
375 }
376 };
377 self.inherited_blocks.extend(
378 unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)),
379 );
380 }
381 fn check_mut_borrowing_layout_constrained_field(
382 &mut self,
383 place: Place<'tcx>,
384 is_mut_use: bool,
385 ) {
386 let mut cursor = place.projection.as_ref();
387 while let &[ref proj_base @ .., elem] = cursor {
388 cursor = proj_base;
389
390 match elem {
391 // Modifications behind a dereference don't affect the value of
392 // the pointer.
393 ProjectionElem::Deref => return,
394 ProjectionElem::Field(..) => {
395 let ty =
396 Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty;
397 if let ty::Adt(def, _) = ty.kind() {
398 if self.tcx.layout_scalar_valid_range(def.did)
399 != (Bound::Unbounded, Bound::Unbounded)
400 {
401 let details = if is_mut_use {
402 UnsafetyViolationDetails::MutationOfLayoutConstrainedField
403
404 // Check `is_freeze` as late as possible to avoid cycle errors
405 // with opaque types.
406 } else if !place
407 .ty(self.body, self.tcx)
408 .ty
409 .is_freeze(self.tcx.at(self.source_info.span), self.param_env)
410 {
411 UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
412 } else {
413 continue;
414 };
415 self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
416 }
417 }
418 }
419 _ => {}
420 }
421 }
422 }
423
424 /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
425 /// the called function has target features the calling function hasn't.
426 fn check_target_features(&mut self, func_did: DefId) {
427 let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
428 let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
429
430 // Is `callee_features` a subset of `calling_features`?
431 if !callee_features.iter().all(|feature| self_features.contains(feature)) {
432 self.require_unsafe(
433 UnsafetyViolationKind::GeneralAndConstFn,
434 UnsafetyViolationDetails::CallToFunctionWith,
435 )
436 }
437 }
438 }
439
440 pub(crate) fn provide(providers: &mut Providers) {
441 *providers = Providers {
442 unsafety_check_result: |tcx, def_id| {
443 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
444 tcx.unsafety_check_result_for_const_arg(def)
445 } else {
446 unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id))
447 }
448 },
449 unsafety_check_result_for_const_arg: |tcx, (did, param_did)| {
450 unsafety_check_result(
451 tcx,
452 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
453 )
454 },
455 unsafe_derive_on_repr_packed,
456 ..*providers
457 };
458 }
459
460 struct UnusedUnsafeVisitor<'a> {
461 used_unsafe: &'a FxHashSet<hir::HirId>,
462 unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
463 }
464
465 impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
466 type Map = intravisit::ErasedMap<'tcx>;
467
468 fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
469 intravisit::NestedVisitorMap::None
470 }
471
472 fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
473 intravisit::walk_block(self, block);
474
475 if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
476 self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
477 }
478 }
479 }
480
481 fn check_unused_unsafe(
482 tcx: TyCtxt<'_>,
483 def_id: LocalDefId,
484 used_unsafe: &FxHashSet<hir::HirId>,
485 unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
486 ) {
487 let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().local_def_id_to_hir_id(def_id));
488
489 let body_id = match body_id {
490 Some(body) => body,
491 None => {
492 debug!("check_unused_unsafe({:?}) - no body found", def_id);
493 return;
494 }
495 };
496 let body = tcx.hir().body(body_id);
497 debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe);
498
499 let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
500 intravisit::Visitor::visit_body(&mut visitor, body);
501 }
502
503 fn unsafety_check_result<'tcx>(
504 tcx: TyCtxt<'tcx>,
505 def: ty::WithOptConstParam<LocalDefId>,
506 ) -> &'tcx UnsafetyCheckResult {
507 debug!("unsafety_violations({:?})", def);
508
509 // N.B., this borrow is valid because all the consumers of
510 // `mir_built` force this.
511 let body = &tcx.mir_built(def).borrow();
512
513 let param_env = tcx.param_env(def.did);
514
515 let id = tcx.hir().local_def_id_to_hir_id(def.did);
516 let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
517 hir::BodyOwnerKind::Closure => (false, false),
518 hir::BodyOwnerKind::Fn => {
519 (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
520 }
521 hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
522 };
523 let mut checker =
524 UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
525 checker.visit_body(&body);
526
527 check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
528
529 tcx.arena.alloc(UnsafetyCheckResult {
530 violations: checker.violations.into(),
531 unsafe_blocks: checker.inherited_blocks.into(),
532 })
533 }
534
535 fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
536 let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
537
538 tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
539 // FIXME: when we make this a hard error, this should have its
540 // own error code.
541 let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
542 "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
543 type or const parameters (error E0133)"
544 .to_string()
545 } else {
546 "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
547 does not derive Copy (error E0133)"
548 .to_string()
549 };
550 lint.build(&message).emit()
551 });
552 }
553
554 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
555 fn is_enclosed(
556 tcx: TyCtxt<'_>,
557 used_unsafe: &FxHashSet<hir::HirId>,
558 id: hir::HirId,
559 ) -> Option<(String, hir::HirId)> {
560 let parent_id = tcx.hir().get_parent_node(id);
561 if parent_id != id {
562 if used_unsafe.contains(&parent_id) {
563 Some(("block".to_string(), parent_id))
564 } else if let Some(Node::Item(&hir::Item {
565 kind: hir::ItemKind::Fn(ref sig, _, _), ..
566 })) = tcx.hir().find(parent_id)
567 {
568 if sig.header.unsafety == hir::Unsafety::Unsafe
569 && !tcx.features().unsafe_block_in_unsafe_fn
570 {
571 Some(("fn".to_string(), parent_id))
572 } else {
573 None
574 }
575 } else {
576 is_enclosed(tcx, used_unsafe, parent_id)
577 }
578 } else {
579 None
580 }
581 }
582
583 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
584 let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
585 tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
586 let msg = "unnecessary `unsafe` block";
587 let mut db = lint.build(msg);
588 db.span_label(span, msg);
589 if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
590 db.span_label(
591 tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
592 format!("because it's nested under this `unsafe` {}", kind),
593 );
594 }
595 db.emit();
596 });
597 }
598
599 fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
600 debug!("builtin_derive_def_id({:?})", def_id);
601 if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
602 if tcx.has_attr(impl_def_id, sym::automatically_derived) {
603 debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
604 Some(impl_def_id)
605 } else {
606 debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
607 None
608 }
609 } else {
610 debug!("builtin_derive_def_id({:?}) - not a method", def_id);
611 None
612 }
613 }
614
615 pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
616 debug!("check_unsafety({:?})", def_id);
617
618 // closures are handled by their parent fn.
619 if tcx.is_closure(def_id.to_def_id()) {
620 return;
621 }
622
623 let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id);
624
625 for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
626 let (description, note) = details.description_and_note();
627
628 // Report an error.
629 let unsafe_fn_msg =
630 if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
631
632 match kind {
633 UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
634 // once
635 struct_span_err!(
636 tcx.sess,
637 source_info.span,
638 E0133,
639 "{} is unsafe and requires unsafe{} block",
640 description,
641 unsafe_fn_msg,
642 )
643 .span_label(source_info.span, description)
644 .note(note)
645 .emit();
646 }
647 UnsafetyViolationKind::BorrowPacked => {
648 if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) {
649 // If a method is defined in the local crate,
650 // the impl containing that method should also be.
651 tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
652 } else {
653 tcx.struct_span_lint_hir(
654 SAFE_PACKED_BORROWS,
655 lint_root,
656 source_info.span,
657 |lint| {
658 lint.build(&format!(
659 "{} is unsafe and requires unsafe{} block (error E0133)",
660 description, unsafe_fn_msg,
661 ))
662 .note(note)
663 .emit()
664 },
665 )
666 }
667 }
668 UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
669 UNSAFE_OP_IN_UNSAFE_FN,
670 lint_root,
671 source_info.span,
672 |lint| {
673 lint.build(&format!(
674 "{} is unsafe and requires unsafe block (error E0133)",
675 description,
676 ))
677 .span_label(source_info.span, description)
678 .note(note)
679 .emit();
680 },
681 ),
682 UnsafetyViolationKind::UnsafeFnBorrowPacked => {
683 // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
684 // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
685 // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
686 // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
687 //
688 // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
689 // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
690 // should only issue a warning for the sake of backwards compatibility.
691 //
692 // The solution those 2 expectations is to always take the minimum of both lints.
693 // This prevent any new errors (unless both lints are explicitely set to `deny`).
694 let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0
695 <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0
696 {
697 SAFE_PACKED_BORROWS
698 } else {
699 UNSAFE_OP_IN_UNSAFE_FN
700 };
701 tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| {
702 lint.build(&format!(
703 "{} is unsafe and requires unsafe block (error E0133)",
704 description,
705 ))
706 .span_label(source_info.span, description)
707 .note(note)
708 .emit();
709 })
710 }
711 }
712 }
713
714 let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default();
715 for &(block_id, is_used) in unsafe_blocks.iter() {
716 if is_used {
717 unsafe_used.insert(block_id);
718 } else {
719 unsafe_unused.push(block_id);
720 }
721 }
722 // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe
723 // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass.
724 unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id));
725
726 for &block_id in &unsafe_unused {
727 report_unused_unsafe(tcx, &unsafe_used, block_id);
728 }
729 }
730
731 fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
732 tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
733 }