]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/check_unsafety.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_mir / transform / check_unsafety.rs
CommitLineData
ea8adc8c 1use rustc_data_structures::fx::FxHashSet;
ea8adc8c 2
94b46f34 3use rustc::ty::query::Providers;
ea8adc8c 4use rustc::ty::{self, TyCtxt};
9fa01778 5use rustc::ty::cast::CastTy;
ea8adc8c 6use rustc::hir;
b7449926 7use rustc::hir::Node;
ea8adc8c 8use rustc::hir::def_id::DefId;
60c5eb7d 9use rustc::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE};
ea8adc8c 10use rustc::mir::*;
13cf67c4 11use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext};
ea8adc8c 12
e74abb32 13use syntax::symbol::{Symbol, sym};
ea8adc8c 14
0731742a
XL
15use std::ops::Bound;
16
9fa01778 17use crate::util;
ea8adc8c 18
60c5eb7d
XL
19use rustc_error_codes::*;
20
dc9dc135
XL
21pub struct UnsafetyChecker<'a, 'tcx> {
22 body: &'a Body<'tcx>,
9fa01778 23 const_context: bool,
b7449926 24 min_const_fn: bool,
ea8adc8c
XL
25 violations: Vec<UnsafetyViolation>,
26 source_info: SourceInfo,
dc9dc135 27 tcx: TyCtxt<'tcx>,
ea8adc8c 28 param_env: ty::ParamEnv<'tcx>,
9fa01778 29 /// Mark an `unsafe` block as used, so we don't lint it.
532ac7d7
XL
30 used_unsafe: FxHashSet<hir::HirId>,
31 inherited_blocks: Vec<(hir::HirId, bool)>,
ea8adc8c
XL
32}
33
dc9dc135 34impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
b7449926 35 fn new(
9fa01778 36 const_context: bool,
b7449926 37 min_const_fn: bool,
dc9dc135 38 body: &'a Body<'tcx>,
dc9dc135 39 tcx: TyCtxt<'tcx>,
b7449926
XL
40 param_env: ty::ParamEnv<'tcx>,
41 ) -> Self {
9fa01778
XL
42 // sanity check
43 if min_const_fn {
44 assert!(const_context);
45 }
ea8adc8c 46 Self {
dc9dc135 47 body,
9fa01778 48 const_context,
b7449926 49 min_const_fn,
ea8adc8c
XL
50 violations: vec![],
51 source_info: SourceInfo {
dc9dc135 52 span: body.span,
94b46f34 53 scope: OUTERMOST_SOURCE_SCOPE
ea8adc8c
XL
54 },
55 tcx,
56 param_env,
0bf4aa26 57 used_unsafe: Default::default(),
ea8adc8c
XL
58 inherited_blocks: vec![],
59 }
60 }
61}
62
63impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
64 fn visit_terminator(&mut self,
ea8adc8c
XL
65 terminator: &Terminator<'tcx>,
66 location: Location)
67 {
68 self.source_info = terminator.source_info;
69 match terminator.kind {
70 TerminatorKind::Goto { .. } |
71 TerminatorKind::SwitchInt { .. } |
72 TerminatorKind::Drop { .. } |
73 TerminatorKind::Yield { .. } |
74 TerminatorKind::Assert { .. } |
75 TerminatorKind::DropAndReplace { .. } |
76 TerminatorKind::GeneratorDrop |
77 TerminatorKind::Resume |
ff7c6d11 78 TerminatorKind::Abort |
ea8adc8c 79 TerminatorKind::Return |
abe05a73 80 TerminatorKind::Unreachable |
2c00a5a8
XL
81 TerminatorKind::FalseEdges { .. } |
82 TerminatorKind::FalseUnwind { .. } => {
ea8adc8c
XL
83 // safe (at least as emitted during MIR construction)
84 }
85
86 TerminatorKind::Call { ref func, .. } => {
dc9dc135 87 let func_ty = func.ty(self.body, self.tcx);
ea8adc8c
XL
88 let sig = func_ty.fn_sig(self.tcx);
89 if let hir::Unsafety::Unsafe = sig.unsafety() {
8faf50e0
XL
90 self.require_unsafe("call to unsafe function",
91 "consult the function's documentation for information on how to avoid \
0731742a 92 undefined behavior", UnsafetyViolationKind::GeneralAndConstFn)
ea8adc8c
XL
93 }
94 }
95 }
48663c56 96 self.super_terminator(terminator, location);
ea8adc8c
XL
97 }
98
99 fn visit_statement(&mut self,
ea8adc8c
XL
100 statement: &Statement<'tcx>,
101 location: Location)
102 {
103 self.source_info = statement.source_info;
104 match statement.kind {
105 StatementKind::Assign(..) |
0bf4aa26 106 StatementKind::FakeRead(..) |
ea8adc8c
XL
107 StatementKind::SetDiscriminant { .. } |
108 StatementKind::StorageLive(..) |
109 StatementKind::StorageDead(..) |
a1dfa0c6 110 StatementKind::Retag { .. } |
b7449926 111 StatementKind::AscribeUserType(..) |
ea8adc8c
XL
112 StatementKind::Nop => {
113 // safe (at least as emitted during MIR construction)
114 }
115
116 StatementKind::InlineAsm { .. } => {
8faf50e0 117 self.require_unsafe("use of inline assembly",
0731742a
XL
118 "inline assembly is entirely unchecked and can cause undefined behavior",
119 UnsafetyViolationKind::General)
ea8adc8c
XL
120 },
121 }
48663c56 122 self.super_statement(statement, location);
ea8adc8c
XL
123 }
124
125 fn visit_rvalue(&mut self,
126 rvalue: &Rvalue<'tcx>,
127 location: Location)
128 {
9fa01778
XL
129 match rvalue {
130 Rvalue::Aggregate(box ref aggregate, _) => {
131 match aggregate {
132 &AggregateKind::Array(..) |
133 &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 "initializing type with `rustc_layout_scalar_valid_range` attr",
139 "initializing a layout restricted type's field with a value \
140 outside the valid range is undefined behavior",
141 UnsafetyViolationKind::GeneralAndConstFn,
142 ),
143 }
144 }
145 &AggregateKind::Closure(def_id, _) |
146 &AggregateKind::Generator(def_id, _, _) => {
147 let UnsafetyCheckResult {
148 violations, unsafe_blocks
149 } = self.tcx.unsafety_check_result(def_id);
150 self.register_violations(&violations, &unsafe_blocks);
0731742a
XL
151 }
152 }
9fa01778
XL
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 => {
dc9dc135 159 let operand_ty = operand.ty(self.body, self.tcx);
9fa01778
XL
160 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
161 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
162 match (cast_in, cast_out) {
163 (CastTy::Ptr(_), CastTy::Int(_)) |
164 (CastTy::FnPtr, CastTy::Int(_)) => {
165 self.register_violations(&[UnsafetyViolation {
166 source_info: self.source_info,
e74abb32
XL
167 description: Symbol::intern("cast of pointer to int"),
168 details: Symbol::intern("casting pointers to integers in constants"),
9fa01778
XL
169 kind: UnsafetyViolationKind::General,
170 }], &[]);
171 },
172 _ => {},
ea8adc8c
XL
173 }
174 }
9fa01778
XL
175 // raw pointer and fn pointer operations are unsafe as it is not clear whether one
176 // pointer would be "less" or "equal" to another, because we cannot know where llvm
177 // or the linker will place various statics in memory. Without this information the
178 // result of a comparison of addresses would differ between runtime and compile-time.
179 Rvalue::BinaryOp(_, ref lhs, _)
180 if self.const_context && self.tcx.features().const_compare_raw_pointers => {
e74abb32 181 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
9fa01778
XL
182 self.register_violations(&[UnsafetyViolation {
183 source_info: self.source_info,
e74abb32
XL
184 description: Symbol::intern("pointer operation"),
185 details: Symbol::intern("operations on pointers in constants"),
9fa01778
XL
186 kind: UnsafetyViolationKind::General,
187 }], &[]);
188 }
189 }
190 _ => {},
ea8adc8c
XL
191 }
192 self.super_rvalue(rvalue, location);
193 }
194
ff7c6d11
XL
195 fn visit_place(&mut self,
196 place: &Place<'tcx>,
48663c56 197 context: PlaceContext,
dc9dc135 198 _location: Location) {
e1599b0c
XL
199 match place.base {
200 PlaceBase::Local(..) => {
201 // Locals are safe.
202 }
203 PlaceBase::Static(box Static { kind: StaticKind::Promoted(_, _), .. }) => {
60c5eb7d 204 bug!("unsafety checking should happen before promotion");
e1599b0c 205 }
60c5eb7d
XL
206 PlaceBase::Static(box Static { kind: StaticKind::Static, .. }) => {
207 bug!("StaticKind::Static should not exist");
dc9dc135 208 }
e1599b0c 209 }
dc9dc135 210
e1599b0c
XL
211 for (i, elem) in place.projection.iter().enumerate() {
212 let proj_base = &place.projection[..i];
213
214 if context.is_borrow() {
215 if util::is_disaligned(self.tcx, self.body, self.param_env, place) {
216 let source_info = self.source_info;
60c5eb7d
XL
217 let lint_root = self.body.source_scopes[source_info.scope]
218 .local_data
219 .as_ref()
220 .assert_crate_local()
221 .lint_root;
e1599b0c
XL
222 self.register_violations(&[UnsafetyViolation {
223 source_info,
e74abb32
XL
224 description: Symbol::intern("borrow of packed field"),
225 details: Symbol::intern(
e1599b0c
XL
226 "fields of packed structs might be misaligned: dereferencing a \
227 misaligned pointer or even just creating a misaligned reference \
228 is undefined behavior"),
229 kind: UnsafetyViolationKind::BorrowPacked(lint_root)
230 }], &[]);
0731742a 231 }
e1599b0c
XL
232 }
233 let is_borrow_of_interior_mut = context.is_borrow() &&
234 !Place::ty_from(&place.base, proj_base, self.body, self.tcx)
235 .ty
236 .is_freeze(self.tcx, self.param_env, self.source_info.span);
237 // prevent
238 // * `&mut x.field`
239 // * `x.field = y;`
240 // * `&x.field` if `field`'s type has interior mutability
241 // because either of these would allow modifying the layout constrained field and
242 // insert values that violate the layout constraints.
243 if context.is_mutating_use() || is_borrow_of_interior_mut {
244 self.check_mut_borrowing_layout_constrained_field(
245 place, context.is_mutating_use(),
246 );
247 }
248 let old_source_info = self.source_info;
249 if let (PlaceBase::Local(local), []) = (&place.base, proj_base) {
60c5eb7d
XL
250 let decl = &self.body.local_decls[*local];
251 if decl.internal {
252 if let LocalInfo::StaticRef { def_id, .. } = decl.local_info {
253 if self.tcx.is_mutable_static(def_id) {
254 self.require_unsafe(
255 "use of mutable static",
256 "mutable statics can be mutated by multiple threads: aliasing \
257 violations or data races will cause undefined behavior",
258 UnsafetyViolationKind::General,
259 );
260 return;
261 } else if self.tcx.is_foreign_item(def_id) {
262 self.require_unsafe(
263 "use of extern static",
264 "extern statics are not controlled by the Rust type system: \
265 invalid data, aliasing violations or data races will cause \
266 undefined behavior",
267 UnsafetyViolationKind::General,
268 );
269 return;
270 }
271 } else {
272 // Internal locals are used in the `move_val_init` desugaring.
273 // We want to check unsafety against the source info of the
274 // desugaring, rather than the source info of the RHS.
275 self.source_info = self.body.local_decls[*local].source_info;
276 }
0731742a 277 }
e1599b0c
XL
278 }
279 let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
e74abb32 280 match base_ty.kind {
e1599b0c
XL
281 ty::RawPtr(..) => {
282 self.require_unsafe("dereference of raw pointer",
283 "raw pointers may be NULL, dangling or unaligned; they can violate \
284 aliasing rules and cause data races: all of these are undefined \
285 behavior", UnsafetyViolationKind::General)
ea8adc8c 286 }
e1599b0c
XL
287 ty::Adt(adt, _) => {
288 if adt.is_union() {
289 if context == PlaceContext::MutatingUse(MutatingUseContext::Store) ||
290 context == PlaceContext::MutatingUse(MutatingUseContext::Drop) ||
291 context == PlaceContext::MutatingUse(
292 MutatingUseContext::AsmOutput
293 )
294 {
295 let elem_ty = match elem {
296 ProjectionElem::Field(_, ty) => ty,
297 _ => span_bug!(
0731742a 298 self.source_info.span,
e1599b0c
XL
299 "non-field projection {:?} from union?",
300 place)
301 };
302 if !elem_ty.is_copy_modulo_regions(
303 self.tcx,
304 self.param_env,
305 self.source_info.span,
306 ) {
307 self.require_unsafe(
308 "assignment to non-`Copy` union field",
309 "the previous content of the field will be dropped, which \
310 causes undefined behavior if the field was not properly \
311 initialized", UnsafetyViolationKind::GeneralAndConstFn)
ea8adc8c 312 } else {
e1599b0c 313 // write to non-move union, safe
ea8adc8c 314 }
e1599b0c
XL
315 } else {
316 self.require_unsafe("access to union field",
317 "the field may not be properly initialized: using \
318 uninitialized data will cause undefined behavior",
319 UnsafetyViolationKind::GeneralAndConstFn)
ea8adc8c
XL
320 }
321 }
ea8adc8c 322 }
e1599b0c 323 _ => {}
ea8adc8c 324 }
e1599b0c
XL
325 self.source_info = old_source_info;
326 }
ea8adc8c
XL
327 }
328}
329
330impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
0731742a
XL
331 fn require_unsafe(
332 &mut self,
333 description: &'static str,
334 details: &'static str,
335 kind: UnsafetyViolationKind,
336 ) {
ea8adc8c
XL
337 let source_info = self.source_info;
338 self.register_violations(&[UnsafetyViolation {
ff7c6d11 339 source_info,
e74abb32
XL
340 description: Symbol::intern(description),
341 details: Symbol::intern(details),
0731742a 342 kind,
ea8adc8c
XL
343 }], &[]);
344 }
345
346 fn register_violations(&mut self,
347 violations: &[UnsafetyViolation],
532ac7d7 348 unsafe_blocks: &[(hir::HirId, bool)]) {
60c5eb7d
XL
349 let safety = self.body.source_scopes[self.source_info.scope]
350 .local_data
351 .as_ref()
352 .assert_crate_local()
353 .safety;
0731742a
XL
354 let within_unsafe = match safety {
355 // `unsafe` blocks are required in safe code
ea8adc8c
XL
356 Safety::Safe => {
357 for violation in violations {
0731742a
XL
358 let mut violation = violation.clone();
359 match violation.kind {
360 UnsafetyViolationKind::GeneralAndConstFn |
361 UnsafetyViolationKind::General => {},
60c5eb7d 362 UnsafetyViolationKind::BorrowPacked(_) => if self.min_const_fn {
0731742a
XL
363 // const fns don't need to be backwards compatible and can
364 // emit these violations as a hard error instead of a backwards
365 // compat lint
366 violation.kind = UnsafetyViolationKind::General;
367 },
368 }
369 if !self.violations.contains(&violation) {
370 self.violations.push(violation)
ea8adc8c
XL
371 }
372 }
ea8adc8c
XL
373 false
374 }
0731742a 375 // `unsafe` function bodies allow unsafe without additional unsafe blocks
ea8adc8c 376 Safety::BuiltinUnsafe | Safety::FnUnsafe => true,
532ac7d7 377 Safety::ExplicitUnsafe(hir_id) => {
0731742a 378 // mark unsafe block as used if there are any unsafe operations inside
ea8adc8c 379 if !violations.is_empty() {
532ac7d7 380 self.used_unsafe.insert(hir_id);
ea8adc8c 381 }
0731742a
XL
382 // only some unsafety is allowed in const fn
383 if self.min_const_fn {
384 for violation in violations {
385 match violation.kind {
386 // these unsafe things are stable in const fn
387 UnsafetyViolationKind::GeneralAndConstFn => {},
388 // these things are forbidden in const fns
389 UnsafetyViolationKind::General |
60c5eb7d 390 UnsafetyViolationKind::BorrowPacked(_) => {
0731742a
XL
391 let mut violation = violation.clone();
392 // const fns don't need to be backwards compatible and can
393 // emit these violations as a hard error instead of a backwards
394 // compat lint
395 violation.kind = UnsafetyViolationKind::General;
396 if !self.violations.contains(&violation) {
397 self.violations.push(violation)
398 }
399 },
400 }
401 }
402 }
ea8adc8c
XL
403 true
404 }
405 };
532ac7d7
XL
406 self.inherited_blocks.extend(unsafe_blocks.iter().map(|&(hir_id, is_used)| {
407 (hir_id, is_used && !within_unsafe)
ea8adc8c
XL
408 }));
409 }
0731742a
XL
410 fn check_mut_borrowing_layout_constrained_field(
411 &mut self,
416331ca 412 place: &Place<'tcx>,
0731742a
XL
413 is_mut_use: bool,
414 ) {
e74abb32
XL
415 let mut cursor = place.projection.as_ref();
416 while let &[ref proj_base @ .., elem] = cursor {
e1599b0c
XL
417 cursor = proj_base;
418
419 match elem {
0731742a 420 ProjectionElem::Field(..) => {
416331ca 421 let ty =
e1599b0c 422 Place::ty_from(&place.base, proj_base, &self.body.local_decls, self.tcx)
416331ca 423 .ty;
e74abb32 424 match ty.kind {
0731742a
XL
425 ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) {
426 (Bound::Unbounded, Bound::Unbounded) => {},
427 _ => {
428 let (description, details) = if is_mut_use {
429 (
430 "mutation of layout constrained field",
431 "mutating layout constrained fields cannot statically be \
432 checked for valid values",
433 )
434 } else {
435 (
436 "borrow of layout constrained field with interior \
437 mutability",
438 "references to fields of layout constrained fields \
439 lose the constraints. Coupled with interior mutability, \
440 the field can be changed to invalid values",
441 )
442 };
443 let source_info = self.source_info;
444 self.register_violations(&[UnsafetyViolation {
445 source_info,
e74abb32
XL
446 description: Symbol::intern(description),
447 details: Symbol::intern(details),
0731742a
XL
448 kind: UnsafetyViolationKind::GeneralAndConstFn,
449 }], &[]);
450 }
451 },
452 _ => {}
453 }
454 }
455 _ => {}
456 }
0731742a
XL
457 }
458 }
ea8adc8c
XL
459}
460
9fa01778 461pub(crate) fn provide(providers: &mut Providers<'_>) {
ea8adc8c
XL
462 *providers = Providers {
463 unsafety_check_result,
ff7c6d11 464 unsafe_derive_on_repr_packed,
ea8adc8c
XL
465 ..*providers
466 };
467}
468
469struct UnusedUnsafeVisitor<'a> {
532ac7d7
XL
470 used_unsafe: &'a FxHashSet<hir::HirId>,
471 unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
ea8adc8c
XL
472}
473
474impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
475 fn nested_visit_map<'this>(&'this mut self) ->
476 hir::intravisit::NestedVisitorMap<'this, 'tcx>
477 {
478 hir::intravisit::NestedVisitorMap::None
479 }
480
481 fn visit_block(&mut self, block: &'tcx hir::Block) {
482 hir::intravisit::walk_block(self, block);
483
484 if let hir::UnsafeBlock(hir::UserProvided) = block.rules {
532ac7d7 485 self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id)));
ea8adc8c
XL
486 }
487 }
488}
489
416331ca
XL
490fn check_unused_unsafe(
491 tcx: TyCtxt<'_>,
dc9dc135
XL
492 def_id: DefId,
493 used_unsafe: &FxHashSet<hir::HirId>,
416331ca 494 unsafe_blocks: &mut Vec<(hir::HirId, bool)>,
dc9dc135 495) {
ea8adc8c 496 let body_id =
532ac7d7 497 tcx.hir().as_local_hir_id(def_id).and_then(|hir_id| {
dc9dc135 498 tcx.hir().maybe_body_owned_by(hir_id)
ea8adc8c
XL
499 });
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 };
0731742a 508 let body = tcx.hir().body(body_id);
ea8adc8c
XL
509 debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})",
510 def_id, body, used_unsafe);
511
512 let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks };
513 hir::intravisit::Visitor::visit_body(&mut visitor, body);
514}
515
416331ca 516fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult {
ea8adc8c
XL
517 debug!("unsafety_violations({:?})", def_id);
518
0731742a 519 // N.B., this borrow is valid because all the consumers of
ea8adc8c 520 // `mir_built` force this.
dc9dc135 521 let body = &tcx.mir_built(def_id).borrow();
ea8adc8c 522
ea8adc8c 523 let param_env = tcx.param_env(def_id);
9fa01778 524
532ac7d7 525 let id = tcx.hir().as_local_hir_id(def_id).unwrap();
dc9dc135 526 let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
9fa01778
XL
527 hir::BodyOwnerKind::Closure => (false, false),
528 hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)),
529 hir::BodyOwnerKind::Const |
530 hir::BodyOwnerKind::Static(_) => (true, false),
531 };
60c5eb7d
XL
532 let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
533 // mir_built ensures that body has a computed cache, so we don't (and can't) attempt to
534 // recompute it here.
535 let body = body.unwrap_read_only();
dc9dc135 536 checker.visit_body(body);
ea8adc8c
XL
537
538 check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
539 UnsafetyCheckResult {
540 violations: checker.violations.into(),
541 unsafe_blocks: checker.inherited_blocks.into()
542 }
543}
544
416331ca 545fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
532ac7d7
XL
546 let lint_hir_id = tcx.hir().as_local_hir_id(def_id).unwrap_or_else(||
547 bug!("checking unsafety for non-local def id {:?}", def_id));
ff7c6d11
XL
548
549 // FIXME: when we make this a hard error, this should have its
550 // own error code.
48663c56 551 let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
416331ca 552 "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
532ac7d7 553 type or const parameters (error E0133)".to_string()
ff7c6d11 554 } else {
416331ca 555 "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
8faf50e0 556 does not derive Copy (error E0133)".to_string()
ff7c6d11 557 };
532ac7d7
XL
558 tcx.lint_hir(SAFE_PACKED_BORROWS,
559 lint_hir_id,
560 tcx.def_span(def_id),
561 &message);
ff7c6d11
XL
562}
563
532ac7d7 564/// Returns the `HirId` for an enclosing scope that is also `unsafe`.
dc9dc135
XL
565fn is_enclosed(
566 tcx: TyCtxt<'_>,
567 used_unsafe: &FxHashSet<hir::HirId>,
568 id: hir::HirId,
569) -> Option<(String, hir::HirId)> {
570 let parent_id = tcx.hir().get_parent_node(id);
ea8adc8c
XL
571 if parent_id != id {
572 if used_unsafe.contains(&parent_id) {
573 Some(("block".to_string(), parent_id))
b7449926 574 } else if let Some(Node::Item(&hir::Item {
60c5eb7d 575 kind: hir::ItemKind::Fn(ref sig, _, _),
ea8adc8c 576 ..
dc9dc135 577 })) = tcx.hir().find(parent_id) {
60c5eb7d 578 match sig.header.unsafety {
0531ce1d
XL
579 hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)),
580 hir::Unsafety::Normal => None,
581 }
ea8adc8c
XL
582 } else {
583 is_enclosed(tcx, used_unsafe, parent_id)
584 }
585 } else {
586 None
587 }
588}
589
dc9dc135
XL
590fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
591 let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
ff7c6d11 592 let msg = "unnecessary `unsafe` block";
532ac7d7 593 let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg);
ff7c6d11 594 db.span_label(span, msg);
ea8adc8c 595 if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
dc9dc135 596 db.span_label(tcx.sess.source_map().def_span(tcx.hir().span(id)),
ff7c6d11 597 format!("because it's nested under this `unsafe` {}", kind));
ea8adc8c
XL
598 }
599 db.emit();
600}
601
416331ca 602fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
ff7c6d11
XL
603 debug!("builtin_derive_def_id({:?})", def_id);
604 if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
48663c56 605 if tcx.has_attr(impl_def_id, sym::automatically_derived) {
ff7c6d11
XL
606 debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
607 Some(impl_def_id)
608 } else {
609 debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
610 None
611 }
612 } else {
613 debug!("builtin_derive_def_id({:?}) - not a method", def_id);
614 None
615 }
616}
617
416331ca 618pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
ea8adc8c 619 debug!("check_unsafety({:?})", def_id);
abe05a73
XL
620
621 // closures are handled by their parent fn.
622 if tcx.is_closure(def_id) {
623 return;
624 }
ea8adc8c
XL
625
626 let UnsafetyCheckResult {
627 violations,
628 unsafe_blocks
629 } = tcx.unsafety_check_result(def_id);
630
631 for &UnsafetyViolation {
8faf50e0 632 source_info, description, details, kind
ea8adc8c
XL
633 } in violations.iter() {
634 // Report an error.
ff7c6d11 635 match kind {
0731742a 636 UnsafetyViolationKind::GeneralAndConstFn |
ff7c6d11
XL
637 UnsafetyViolationKind::General => {
638 struct_span_err!(
639 tcx.sess, source_info.span, E0133,
8faf50e0 640 "{} is unsafe and requires unsafe function or block", description)
60c5eb7d
XL
641 .span_label(source_info.span, &*description.as_str())
642 .note(&details.as_str())
ff7c6d11
XL
643 .emit();
644 }
532ac7d7 645 UnsafetyViolationKind::BorrowPacked(lint_hir_id) => {
ff7c6d11
XL
646 if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) {
647 tcx.unsafe_derive_on_repr_packed(impl_def_id);
648 } else {
8faf50e0 649 tcx.lint_node_note(SAFE_PACKED_BORROWS,
532ac7d7 650 lint_hir_id,
ff7c6d11 651 source_info.span,
8faf50e0 652 &format!("{} is unsafe and requires unsafe function or block \
60c5eb7d
XL
653 (error E0133)", description),
654 &details.as_str());
ff7c6d11
XL
655 }
656 }
ea8adc8c
XL
657 }
658 }
659
660 let mut unsafe_blocks: Vec<_> = unsafe_blocks.into_iter().collect();
532ac7d7 661 unsafe_blocks.sort_by_cached_key(|(hir_id, _)| tcx.hir().hir_to_node_id(*hir_id));
ea8adc8c 662 let used_unsafe: FxHashSet<_> = unsafe_blocks.iter()
60c5eb7d 663 .flat_map(|&&(id, used)| used.then_some(id))
ea8adc8c
XL
664 .collect();
665 for &(block_id, is_used) in unsafe_blocks {
666 if !is_used {
667 report_unused_unsafe(tcx, &used_unsafe, block_id);
668 }
669 }
670}