]>
Commit | Line | Data |
---|---|---|
add651ee | 1 | use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet}; |
dfeec247 | 2 | use rustc_hir as hir; |
9c376795 | 3 | use rustc_hir::def::DefKind; |
f9f354fc XL |
4 | use rustc_hir::def_id::{DefId, LocalDefId}; |
5 | use rustc_hir::hir_id::HirId; | |
dfeec247 | 6 | use rustc_hir::intravisit; |
487cf647 | 7 | use rustc_hir::{BlockCheckMode, ExprKind, Node}; |
ba9703b0 | 8 | use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; |
f2b60f7d | 9 | use rustc_middle::mir::*; |
49aad941 | 10 | use rustc_middle::query::Providers; |
ba9703b0 | 11 | use rustc_middle::ty::{self, TyCtxt}; |
cdc7bbd5 | 12 | use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; |
f9f354fc | 13 | use rustc_session::lint::Level; |
ea8adc8c | 14 | |
0731742a XL |
15 | use std::ops::Bound; |
16 | ||
49aad941 FG |
17 | use crate::errors; |
18 | ||
dc9dc135 XL |
19 | pub struct UnsafetyChecker<'a, 'tcx> { |
20 | body: &'a Body<'tcx>, | |
f9f354fc | 21 | body_did: LocalDefId, |
ea8adc8c XL |
22 | violations: Vec<UnsafetyViolation>, |
23 | source_info: SourceInfo, | |
dc9dc135 | 24 | tcx: TyCtxt<'tcx>, |
ea8adc8c | 25 | param_env: ty::ParamEnv<'tcx>, |
5e7ed085 FG |
26 | |
27 | /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint. | |
353b0b11 | 28 | used_unsafe_blocks: UnordSet<HirId>, |
ea8adc8c XL |
29 | } |
30 | ||
dc9dc135 | 31 | impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { |
b7449926 | 32 | fn new( |
dc9dc135 | 33 | body: &'a Body<'tcx>, |
f9f354fc | 34 | body_did: LocalDefId, |
dc9dc135 | 35 | tcx: TyCtxt<'tcx>, |
b7449926 XL |
36 | param_env: ty::ParamEnv<'tcx>, |
37 | ) -> Self { | |
ea8adc8c | 38 | Self { |
dc9dc135 | 39 | body, |
f9f354fc | 40 | body_did, |
ea8adc8c | 41 | violations: vec![], |
f9f354fc | 42 | source_info: SourceInfo::outermost(body.span), |
ea8adc8c XL |
43 | tcx, |
44 | param_env, | |
5e7ed085 | 45 | used_unsafe_blocks: Default::default(), |
ea8adc8c XL |
46 | } |
47 | } | |
48 | } | |
49 | ||
a2a8927a | 50 | impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { |
dfeec247 | 51 | fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { |
ea8adc8c XL |
52 | self.source_info = terminator.source_info; |
53 | match terminator.kind { | |
dfeec247 XL |
54 | TerminatorKind::Goto { .. } |
55 | | TerminatorKind::SwitchInt { .. } | |
56 | | TerminatorKind::Drop { .. } | |
57 | | TerminatorKind::Yield { .. } | |
58 | | TerminatorKind::Assert { .. } | |
dfeec247 XL |
59 | | TerminatorKind::GeneratorDrop |
60 | | TerminatorKind::Resume | |
353b0b11 | 61 | | TerminatorKind::Terminate |
dfeec247 XL |
62 | | TerminatorKind::Return |
63 | | TerminatorKind::Unreachable | |
f035d41b | 64 | | TerminatorKind::FalseEdge { .. } |
dfeec247 | 65 | | TerminatorKind::FalseUnwind { .. } => { |
ea8adc8c XL |
66 | // safe (at least as emitted during MIR construction) |
67 | } | |
68 | ||
69 | TerminatorKind::Call { ref func, .. } => { | |
dc9dc135 | 70 | let func_ty = func.ty(self.body, self.tcx); |
04454e1e FG |
71 | let func_id = |
72 | if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None }; | |
ea8adc8c XL |
73 | let sig = func_ty.fn_sig(self.tcx); |
74 | if let hir::Unsafety::Unsafe = sig.unsafety() { | |
dfeec247 | 75 | self.require_unsafe( |
cdc7bbd5 | 76 | UnsafetyViolationKind::General, |
3dfed10e | 77 | UnsafetyViolationDetails::CallToUnsafeFunction, |
dfeec247 | 78 | ) |
ea8adc8c | 79 | } |
f9f354fc | 80 | |
04454e1e | 81 | if let Some(func_id) = func_id { |
1b1a35ee | 82 | self.check_target_features(*func_id); |
f9f354fc | 83 | } |
ea8adc8c | 84 | } |
f9f354fc XL |
85 | |
86 | TerminatorKind::InlineAsm { .. } => self.require_unsafe( | |
f9f354fc | 87 | UnsafetyViolationKind::General, |
3dfed10e | 88 | UnsafetyViolationDetails::UseOfInlineAssembly, |
f9f354fc | 89 | ), |
ea8adc8c | 90 | } |
48663c56 | 91 | self.super_terminator(terminator, location); |
ea8adc8c XL |
92 | } |
93 | ||
dfeec247 | 94 | fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { |
ea8adc8c XL |
95 | self.source_info = statement.source_info; |
96 | match statement.kind { | |
dfeec247 XL |
97 | StatementKind::Assign(..) |
98 | | StatementKind::FakeRead(..) | |
99 | | StatementKind::SetDiscriminant { .. } | |
04454e1e | 100 | | StatementKind::Deinit(..) |
dfeec247 XL |
101 | | StatementKind::StorageLive(..) |
102 | | StatementKind::StorageDead(..) | |
103 | | StatementKind::Retag { .. } | |
353b0b11 | 104 | | StatementKind::PlaceMention(..) |
3dfed10e | 105 | | StatementKind::Coverage(..) |
487cf647 | 106 | | StatementKind::Intrinsic(..) |
9ffffee4 | 107 | | StatementKind::ConstEvalCounter |
dfeec247 | 108 | | StatementKind::Nop => { |
ea8adc8c XL |
109 | // safe (at least as emitted during MIR construction) |
110 | } | |
353b0b11 FG |
111 | // `AscribeUserType` just exists to help MIR borrowck. |
112 | // It has no semantics, and everything is already reported by `PlaceMention`. | |
113 | StatementKind::AscribeUserType(..) => return, | |
ea8adc8c | 114 | } |
48663c56 | 115 | self.super_statement(statement, location); |
ea8adc8c XL |
116 | } |
117 | ||
dfeec247 | 118 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { |
9fa01778 | 119 | match rvalue { |
dfeec247 XL |
120 | Rvalue::Aggregate(box ref aggregate, _) => match aggregate { |
121 | &AggregateKind::Array(..) | &AggregateKind::Tuple => {} | |
a2a8927a XL |
122 | &AggregateKind::Adt(adt_did, ..) => { |
123 | match self.tcx.layout_scalar_valid_range(adt_did) { | |
dfeec247 XL |
124 | (Bound::Unbounded, Bound::Unbounded) => {} |
125 | _ => self.require_unsafe( | |
cdc7bbd5 | 126 | UnsafetyViolationKind::General, |
3dfed10e | 127 | UnsafetyViolationDetails::InitializingTypeWith, |
dfeec247 | 128 | ), |
0731742a XL |
129 | } |
130 | } | |
dfeec247 | 131 | &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => { |
9ffffee4 | 132 | let def_id = def_id.expect_local(); |
5e7ed085 | 133 | let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = |
064997fb | 134 | self.tcx.unsafety_check_result(def_id); |
353b0b11 | 135 | self.register_violations(violations, used_unsafe_blocks.items().copied()); |
dfeec247 | 136 | } |
9fa01778 | 137 | }, |
dfeec247 | 138 | _ => {} |
ea8adc8c XL |
139 | } |
140 | self.super_rvalue(rvalue, location); | |
141 | } | |
142 | ||
9c376795 FG |
143 | fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { |
144 | if let Operand::Constant(constant) = op { | |
145 | let maybe_uneval = match constant.literal { | |
146 | ConstantKind::Val(..) | ConstantKind::Ty(_) => None, | |
147 | ConstantKind::Unevaluated(uv, _) => Some(uv), | |
148 | }; | |
149 | ||
150 | if let Some(uv) = maybe_uneval { | |
151 | if uv.promoted.is_none() { | |
49aad941 | 152 | let def_id = uv.def; |
9c376795 FG |
153 | if self.tcx.def_kind(def_id) == DefKind::InlineConst { |
154 | let local_def_id = def_id.expect_local(); | |
155 | let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = | |
156 | self.tcx.unsafety_check_result(local_def_id); | |
353b0b11 | 157 | self.register_violations(violations, used_unsafe_blocks.items().copied()); |
9c376795 FG |
158 | } |
159 | } | |
160 | } | |
161 | } | |
162 | self.super_operand(op, location); | |
163 | } | |
164 | ||
dfeec247 | 165 | fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { |
f9f354fc | 166 | // On types with `scalar_valid_range`, prevent |
74b04a01 XL |
167 | // * `&mut x.field` |
168 | // * `x.field = y;` | |
169 | // * `&x.field` if `field`'s type has interior mutability | |
170 | // because either of these would allow modifying the layout constrained field and | |
171 | // insert values that violate the layout constraints. | |
172 | if context.is_mutating_use() || context.is_borrow() { | |
ba9703b0 | 173 | self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); |
74b04a01 XL |
174 | } |
175 | ||
5e7ed085 | 176 | // Some checks below need the extra meta info of the local declaration. |
fc512014 XL |
177 | let decl = &self.body.local_decls[place.local]; |
178 | ||
179 | // Check the base local: it might be an unsafe-to-access static. We only check derefs of the | |
180 | // temporary holding the static pointer to avoid duplicate errors | |
181 | // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>. | |
182 | if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) { | |
5e7ed085 | 183 | // If the projection root is an artificial local that we introduced when |
fc512014 XL |
184 | // desugaring `static`, give a more specific error message |
185 | // (avoid the general "raw pointer" clause below, that would only be confusing). | |
353b0b11 | 186 | if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() { |
fc512014 XL |
187 | if self.tcx.is_mutable_static(def_id) { |
188 | self.require_unsafe( | |
189 | UnsafetyViolationKind::General, | |
190 | UnsafetyViolationDetails::UseOfMutableStatic, | |
191 | ); | |
192 | return; | |
193 | } else if self.tcx.is_foreign_item(def_id) { | |
74b04a01 | 194 | self.require_unsafe( |
fc512014 XL |
195 | UnsafetyViolationKind::General, |
196 | UnsafetyViolationDetails::UseOfExternStatic, | |
dfeec247 | 197 | ); |
fc512014 | 198 | return; |
0731742a | 199 | } |
e1599b0c | 200 | } |
fc512014 XL |
201 | } |
202 | ||
203 | // Check for raw pointer `Deref`. | |
204 | for (base, proj) in place.iter_projections() { | |
205 | if proj == ProjectionElem::Deref { | |
fc512014 XL |
206 | let base_ty = base.ty(self.body, self.tcx).ty; |
207 | if base_ty.is_unsafe_ptr() { | |
208 | self.require_unsafe( | |
cdc7bbd5 | 209 | UnsafetyViolationKind::General, |
fc512014 XL |
210 | UnsafetyViolationDetails::DerefOfRawPointer, |
211 | ) | |
0731742a | 212 | } |
fc512014 XL |
213 | } |
214 | } | |
215 | ||
216 | // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes | |
217 | // whether we *read* the union field or potentially *write* to it (if this place is being assigned to). | |
218 | let mut saw_deref = false; | |
219 | for (base, proj) in place.iter_projections().rev() { | |
220 | if proj == ProjectionElem::Deref { | |
221 | saw_deref = true; | |
222 | continue; | |
e1599b0c | 223 | } |
fc512014 XL |
224 | |
225 | let base_ty = base.ty(self.body, self.tcx).ty; | |
17df50a5 | 226 | if base_ty.is_union() { |
fc512014 XL |
227 | // If we did not hit a `Deref` yet and the overall place use is an assignment, the |
228 | // rules are different. | |
229 | let assign_to_field = !saw_deref | |
230 | && matches!( | |
231 | context, | |
232 | PlaceContext::MutatingUse( | |
233 | MutatingUseContext::Store | |
234 | | MutatingUseContext::Drop | |
235 | | MutatingUseContext::AsmOutput | |
236 | ) | |
237 | ); | |
238 | // If this is just an assignment, determine if the assigned type needs dropping. | |
239 | if assign_to_field { | |
240 | // We have to check the actual type of the assignment, as that determines if the | |
241 | // old value is being dropped. | |
242 | let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; | |
064997fb FG |
243 | if assigned_ty.needs_drop(self.tcx, self.param_env) { |
244 | // This would be unsafe, but should be outright impossible since we reject such unions. | |
245 | self.tcx.sess.delay_span_bug( | |
246 | self.source_info.span, | |
247 | format!("union fields that need dropping should be impossible: {assigned_ty}") | |
fc512014 | 248 | ); |
ea8adc8c | 249 | } |
fc512014 XL |
250 | } else { |
251 | self.require_unsafe( | |
cdc7bbd5 | 252 | UnsafetyViolationKind::General, |
fc512014 XL |
253 | UnsafetyViolationDetails::AccessToUnionField, |
254 | ) | |
ea8adc8c | 255 | } |
ea8adc8c | 256 | } |
e1599b0c | 257 | } |
ea8adc8c XL |
258 | } |
259 | } | |
260 | ||
a2a8927a | 261 | impl<'tcx> UnsafetyChecker<'_, 'tcx> { |
3dfed10e | 262 | fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) { |
cdc7bbd5 XL |
263 | // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such. |
264 | assert_ne!(kind, UnsafetyViolationKind::UnsafeFn); | |
265 | ||
ea8adc8c | 266 | let source_info = self.source_info; |
f9f354fc XL |
267 | let lint_root = self.body.source_scopes[self.source_info.scope] |
268 | .local_data | |
269 | .as_ref() | |
270 | .assert_crate_local() | |
271 | .lint_root; | |
dfeec247 | 272 | self.register_violations( |
5e7ed085 | 273 | [&UnsafetyViolation { source_info, lint_root, kind, details }], |
353b0b11 | 274 | UnordItems::empty(), |
dfeec247 | 275 | ); |
ea8adc8c XL |
276 | } |
277 | ||
5e7ed085 | 278 | fn register_violations<'a>( |
dfeec247 | 279 | &mut self, |
5e7ed085 | 280 | violations: impl IntoIterator<Item = &'a UnsafetyViolation>, |
353b0b11 | 281 | new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>, |
dfeec247 | 282 | ) { |
60c5eb7d XL |
283 | let safety = self.body.source_scopes[self.source_info.scope] |
284 | .local_data | |
285 | .as_ref() | |
286 | .assert_crate_local() | |
287 | .safety; | |
5e7ed085 | 288 | match safety { |
0731742a | 289 | // `unsafe` blocks are required in safe code |
5e7ed085 FG |
290 | Safety::Safe => violations.into_iter().for_each(|&violation| { |
291 | match violation.kind { | |
292 | UnsafetyViolationKind::General => {} | |
293 | UnsafetyViolationKind::UnsafeFn => { | |
294 | bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") | |
ea8adc8c XL |
295 | } |
296 | } | |
5e7ed085 FG |
297 | if !self.violations.contains(&violation) { |
298 | self.violations.push(violation) | |
f9f354fc | 299 | } |
5e7ed085 FG |
300 | }), |
301 | // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s | |
302 | Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| { | |
303 | violation.kind = UnsafetyViolationKind::UnsafeFn; | |
304 | if !self.violations.contains(&violation) { | |
305 | self.violations.push(violation) | |
ea8adc8c | 306 | } |
5e7ed085 FG |
307 | }), |
308 | Safety::BuiltinUnsafe => {} | |
f2b60f7d FG |
309 | Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| { |
310 | self.used_unsafe_blocks.insert(hir_id); | |
5e7ed085 | 311 | }), |
ea8adc8c | 312 | }; |
5e7ed085 | 313 | |
353b0b11 | 314 | self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks); |
ea8adc8c | 315 | } |
0731742a XL |
316 | fn check_mut_borrowing_layout_constrained_field( |
317 | &mut self, | |
ba9703b0 | 318 | place: Place<'tcx>, |
0731742a XL |
319 | is_mut_use: bool, |
320 | ) { | |
5869c6ff | 321 | for (place_base, elem) in place.iter_projections().rev() { |
e1599b0c | 322 | match elem { |
74b04a01 XL |
323 | // Modifications behind a dereference don't affect the value of |
324 | // the pointer. | |
325 | ProjectionElem::Deref => return, | |
0731742a | 326 | ProjectionElem::Field(..) => { |
5869c6ff | 327 | let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; |
1b1a35ee | 328 | if let ty::Adt(def, _) = ty.kind() { |
5e7ed085 | 329 | if self.tcx.layout_scalar_valid_range(def.did()) |
ba9703b0 XL |
330 | != (Bound::Unbounded, Bound::Unbounded) |
331 | { | |
3dfed10e XL |
332 | let details = if is_mut_use { |
333 | UnsafetyViolationDetails::MutationOfLayoutConstrainedField | |
74b04a01 | 334 | |
ba9703b0 XL |
335 | // Check `is_freeze` as late as possible to avoid cycle errors |
336 | // with opaque types. | |
f035d41b XL |
337 | } else if !place |
338 | .ty(self.body, self.tcx) | |
339 | .ty | |
2b03887a | 340 | .is_freeze(self.tcx, self.param_env) |
f035d41b | 341 | { |
3dfed10e | 342 | UnsafetyViolationDetails::BorrowOfLayoutConstrainedField |
ba9703b0 XL |
343 | } else { |
344 | continue; | |
345 | }; | |
cdc7bbd5 | 346 | self.require_unsafe(UnsafetyViolationKind::General, details); |
ba9703b0 | 347 | } |
0731742a XL |
348 | } |
349 | } | |
350 | _ => {} | |
351 | } | |
0731742a XL |
352 | } |
353 | } | |
f9f354fc XL |
354 | |
355 | /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether | |
356 | /// the called function has target features the calling function hasn't. | |
357 | fn check_target_features(&mut self, func_did: DefId) { | |
17df50a5 XL |
358 | // Unsafety isn't required on wasm targets. For more information see |
359 | // the corresponding check in typeck/src/collect.rs | |
360 | if self.tcx.sess.target.options.is_like_wasm { | |
361 | return; | |
362 | } | |
363 | ||
f9f354fc | 364 | let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; |
04454e1e FG |
365 | // The body might be a constant, so it doesn't have codegen attributes. |
366 | let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features; | |
f9f354fc XL |
367 | |
368 | // Is `callee_features` a subset of `calling_features`? | |
369 | if !callee_features.iter().all(|feature| self_features.contains(feature)) { | |
370 | self.require_unsafe( | |
cdc7bbd5 | 371 | UnsafetyViolationKind::General, |
3dfed10e | 372 | UnsafetyViolationDetails::CallToFunctionWith, |
f9f354fc XL |
373 | ) |
374 | } | |
375 | } | |
ea8adc8c XL |
376 | } |
377 | ||
f035d41b | 378 | pub(crate) fn provide(providers: &mut Providers) { |
49aad941 | 379 | *providers = Providers { unsafety_check_result, ..*providers }; |
ea8adc8c XL |
380 | } |
381 | ||
5e7ed085 FG |
382 | /// Context information for [`UnusedUnsafeVisitor`] traversal, |
383 | /// saves (innermost) relevant context | |
384 | #[derive(Copy, Clone, Debug)] | |
385 | enum Context { | |
386 | Safe, | |
387 | /// in an `unsafe fn` | |
388 | UnsafeFn(HirId), | |
389 | /// in a *used* `unsafe` block | |
390 | /// (i.e. a block without unused-unsafe warning) | |
391 | UnsafeBlock(HirId), | |
392 | } | |
393 | ||
394 | struct UnusedUnsafeVisitor<'a, 'tcx> { | |
395 | tcx: TyCtxt<'tcx>, | |
353b0b11 | 396 | used_unsafe_blocks: &'a UnordSet<HirId>, |
5e7ed085 FG |
397 | context: Context, |
398 | unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>, | |
ea8adc8c XL |
399 | } |
400 | ||
5e7ed085 | 401 | impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> { |
dfeec247 | 402 | fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { |
dfeec247 | 403 | if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { |
5e7ed085 | 404 | let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) { |
f2b60f7d FG |
405 | (Level::Allow, _) => true, |
406 | _ => self.used_unsafe_blocks.contains(&block.hir_id), | |
5e7ed085 FG |
407 | }; |
408 | let unused_unsafe = match (self.context, used) { | |
f2b60f7d FG |
409 | (_, false) => UnusedUnsafe::Unused, |
410 | (Context::Safe, true) | (Context::UnsafeFn(_), true) => { | |
5e7ed085 FG |
411 | let previous_context = self.context; |
412 | self.context = Context::UnsafeBlock(block.hir_id); | |
413 | intravisit::walk_block(self, block); | |
414 | self.context = previous_context; | |
415 | return; | |
416 | } | |
f2b60f7d | 417 | (Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id), |
5e7ed085 FG |
418 | }; |
419 | self.unused_unsafes.push((block.hir_id, unused_unsafe)); | |
420 | } | |
421 | intravisit::walk_block(self, block); | |
422 | } | |
423 | ||
fe692bf9 FG |
424 | fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { |
425 | self.visit_body(self.tcx.hir().body(c.body)) | |
9c376795 FG |
426 | } |
427 | ||
5e7ed085 FG |
428 | fn visit_fn( |
429 | &mut self, | |
430 | fk: intravisit::FnKind<'tcx>, | |
431 | _fd: &'tcx hir::FnDecl<'tcx>, | |
432 | b: hir::BodyId, | |
433 | _s: rustc_span::Span, | |
9ffffee4 | 434 | _id: LocalDefId, |
5e7ed085 FG |
435 | ) { |
436 | if matches!(fk, intravisit::FnKind::Closure) { | |
437 | self.visit_body(self.tcx.hir().body(b)) | |
ea8adc8c XL |
438 | } |
439 | } | |
440 | } | |
441 | ||
416331ca XL |
442 | fn check_unused_unsafe( |
443 | tcx: TyCtxt<'_>, | |
f9f354fc | 444 | def_id: LocalDefId, |
353b0b11 | 445 | used_unsafe_blocks: &UnordSet<HirId>, |
5e7ed085 | 446 | ) -> Vec<(HirId, UnusedUnsafe)> { |
064997fb | 447 | let body_id = tcx.hir().maybe_body_owned_by(def_id); |
5e7ed085 FG |
448 | |
449 | let Some(body_id) = body_id else { | |
450 | debug!("check_unused_unsafe({:?}) - no body found", def_id); | |
451 | return vec![]; | |
ea8adc8c | 452 | }; |
ea8adc8c | 453 | |
064997fb FG |
454 | let body = tcx.hir().body(body_id); |
455 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); | |
5e7ed085 FG |
456 | let context = match tcx.hir().fn_sig_by_hir_id(hir_id) { |
457 | Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn(hir_id), | |
458 | _ => Context::Safe, | |
459 | }; | |
460 | ||
461 | debug!( | |
462 | "check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})", | |
463 | def_id, body, context, used_unsafe_blocks | |
464 | ); | |
465 | ||
466 | let mut unused_unsafes = vec![]; | |
467 | ||
468 | let mut visitor = UnusedUnsafeVisitor { | |
469 | tcx, | |
470 | used_unsafe_blocks, | |
471 | context, | |
472 | unused_unsafes: &mut unused_unsafes, | |
473 | }; | |
dfeec247 | 474 | intravisit::Visitor::visit_body(&mut visitor, body); |
5e7ed085 FG |
475 | |
476 | unused_unsafes | |
ea8adc8c XL |
477 | } |
478 | ||
49aad941 | 479 | fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult { |
3dfed10e | 480 | debug!("unsafety_violations({:?})", def); |
ea8adc8c | 481 | |
0731742a | 482 | // N.B., this borrow is valid because all the consumers of |
ea8adc8c | 483 | // `mir_built` force this. |
3dfed10e | 484 | let body = &tcx.mir_built(def).borrow(); |
ea8adc8c | 485 | |
9c376795 | 486 | if body.is_custom_mir() { |
487cf647 FG |
487 | return tcx.arena.alloc(UnsafetyCheckResult { |
488 | violations: Vec::new(), | |
353b0b11 | 489 | used_unsafe_blocks: Default::default(), |
487cf647 FG |
490 | unused_unsafes: Some(Vec::new()), |
491 | }); | |
492 | } | |
493 | ||
49aad941 | 494 | let param_env = tcx.param_env(def); |
9fa01778 | 495 | |
49aad941 | 496 | let mut checker = UnsafetyChecker::new(body, def, tcx, param_env); |
ba9703b0 | 497 | checker.visit_body(&body); |
ea8adc8c | 498 | |
49aad941 FG |
499 | let unused_unsafes = (!tcx.is_typeck_child(def.to_def_id())) |
500 | .then(|| check_unused_unsafe(tcx, def, &checker.used_unsafe_blocks)); | |
3dfed10e XL |
501 | |
502 | tcx.arena.alloc(UnsafetyCheckResult { | |
5e7ed085 FG |
503 | violations: checker.violations, |
504 | used_unsafe_blocks: checker.used_unsafe_blocks, | |
505 | unused_unsafes, | |
3dfed10e | 506 | }) |
ea8adc8c XL |
507 | } |
508 | ||
5e7ed085 | 509 | fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) { |
ba9703b0 | 510 | let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); |
49aad941 FG |
511 | let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind { |
512 | Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) | |
513 | } else { | |
514 | None | |
515 | }; | |
516 | tcx.emit_spanned_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent }); | |
ea8adc8c XL |
517 | } |
518 | ||
f9f354fc | 519 | pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { |
ea8adc8c | 520 | debug!("check_unsafety({:?})", def_id); |
abe05a73 | 521 | |
9c376795 FG |
522 | // closures and inline consts are handled by their parent fn. |
523 | if tcx.is_typeck_child(def_id.to_def_id()) { | |
abe05a73 XL |
524 | return; |
525 | } | |
ea8adc8c | 526 | |
5e7ed085 | 527 | let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id); |
fe692bf9 FG |
528 | // Only suggest wrapping the entire function body in an unsafe block once |
529 | let mut suggest_unsafe_block = true; | |
ea8adc8c | 530 | |
3dfed10e | 531 | for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() { |
49aad941 | 532 | let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span }; |
3dfed10e | 533 | |
ff7c6d11 | 534 | match kind { |
cdc7bbd5 | 535 | UnsafetyViolationKind::General => { |
49aad941 | 536 | let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root); |
487cf647 FG |
537 | let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| { |
538 | if let Node::Expr(block) = node | |
539 | && let ExprKind::Block(block, _) = block.kind | |
540 | && let BlockCheckMode::UnsafeBlock(_) = block.rules | |
541 | { | |
542 | true | |
543 | } | |
544 | else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) | |
545 | && sig.header.is_unsafe() | |
546 | { | |
547 | true | |
548 | } else { | |
549 | false | |
550 | } | |
551 | }); | |
49aad941 FG |
552 | let enclosing = if let Some((id, _)) = note_non_inherited { |
553 | Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) | |
554 | } else { | |
555 | None | |
556 | }; | |
557 | tcx.sess.emit_err(errors::RequiresUnsafe { | |
558 | span: source_info.span, | |
559 | enclosing, | |
560 | details, | |
561 | op_in_unsafe_fn_allowed, | |
562 | }); | |
ff7c6d11 | 563 | } |
fe692bf9 FG |
564 | UnsafetyViolationKind::UnsafeFn => { |
565 | tcx.emit_spanned_lint( | |
566 | UNSAFE_OP_IN_UNSAFE_FN, | |
567 | lint_root, | |
568 | source_info.span, | |
569 | errors::UnsafeOpInUnsafeFn { | |
570 | details, | |
571 | suggest_unsafe_block: suggest_unsafe_block.then(|| { | |
572 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); | |
573 | let fn_sig = tcx | |
574 | .hir() | |
575 | .fn_sig_by_hir_id(hir_id) | |
576 | .expect("this violation only occurs in fn"); | |
577 | let body = tcx.hir().body_owned_by(def_id); | |
578 | let body_span = tcx.hir().body(body).value.span; | |
579 | let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi(); | |
580 | let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo(); | |
581 | (start, end, fn_sig.span) | |
582 | }), | |
583 | }, | |
584 | ); | |
585 | suggest_unsafe_block = false; | |
586 | } | |
ea8adc8c XL |
587 | } |
588 | } | |
589 | ||
5e7ed085 FG |
590 | for &(block_id, kind) in unused_unsafes.as_ref().unwrap() { |
591 | report_unused_unsafe(tcx, kind, block_id); | |
ba9703b0 | 592 | } |
ea8adc8c | 593 | } |
f9f354fc XL |
594 | |
595 | fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { | |
596 | tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow | |
597 | } |