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