]>
Commit | Line | Data |
---|---|---|
ea8adc8c | 1 | use rustc_data_structures::fx::FxHashSet; |
ea8adc8c | 2 | |
94b46f34 | 3 | use rustc::ty::query::Providers; |
ea8adc8c | 4 | use rustc::ty::{self, TyCtxt}; |
9fa01778 | 5 | use rustc::ty::cast::CastTy; |
ea8adc8c | 6 | use rustc::hir; |
b7449926 | 7 | use rustc::hir::Node; |
ea8adc8c | 8 | use rustc::hir::def_id::DefId; |
60c5eb7d | 9 | use rustc::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; |
ea8adc8c | 10 | use rustc::mir::*; |
13cf67c4 | 11 | use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext}; |
ea8adc8c | 12 | |
e74abb32 | 13 | use syntax::symbol::{Symbol, sym}; |
ea8adc8c | 14 | |
0731742a XL |
15 | use std::ops::Bound; |
16 | ||
9fa01778 | 17 | use crate::util; |
ea8adc8c | 18 | |
60c5eb7d XL |
19 | use rustc_error_codes::*; |
20 | ||
dc9dc135 XL |
21 | pub 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 | 34 | impl<'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 | ||
63 | impl<'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 | ||
330 | impl<'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 | 461 | pub(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 | ||
469 | struct UnusedUnsafeVisitor<'a> { | |
532ac7d7 XL |
470 | used_unsafe: &'a FxHashSet<hir::HirId>, |
471 | unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>, | |
ea8adc8c XL |
472 | } |
473 | ||
474 | impl<'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 |
490 | fn 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 | 516 | fn 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 | 545 | fn 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 |
565 | fn 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 |
590 | fn 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 | 602 | fn 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 | 618 | pub 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 | } |