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