]>
Commit | Line | Data |
---|---|---|
f9f354fc | 1 | use either::Either; |
5e7ed085 | 2 | use rustc_const_eval::util::CallKind; |
923072b8 | 3 | use rustc_data_structures::captures::Captures; |
dc9dc135 | 4 | use rustc_data_structures::fx::FxHashSet; |
064997fb FG |
5 | use rustc_errors::{ |
6 | struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, | |
7 | }; | |
dfeec247 | 8 | use rustc_hir as hir; |
9ffffee4 | 9 | use rustc_hir::def::{DefKind, Res}; |
064997fb | 10 | use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; |
487cf647 | 11 | use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; |
5e7ed085 FG |
12 | use rustc_infer::infer::TyCtxtInferExt; |
13 | use rustc_infer::traits::ObligationCause; | |
04454e1e | 14 | use rustc_middle::mir::tcx::PlaceTy; |
ba9703b0 XL |
15 | use rustc_middle::mir::{ |
16 | self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, | |
17df50a5 | 17 | FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, |
6a06907d | 18 | ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, |
ba9703b0 | 19 | }; |
2b03887a | 20 | use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty}; |
c295e0f8 | 21 | use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; |
064997fb FG |
22 | use rustc_span::def_id::LocalDefId; |
23 | use rustc_span::hygiene::DesugaringKind; | |
9c376795 | 24 | use rustc_span::symbol::{kw, sym}; |
064997fb | 25 | use rustc_span::{BytePos, Span, Symbol}; |
136023e0 | 26 | use rustc_trait_selection::infer::InferCtxtExt; |
dc9dc135 | 27 | |
a2a8927a | 28 | use crate::borrow_set::TwoPhaseActivation; |
c295e0f8 | 29 | use crate::borrowck_errors; |
dc9dc135 | 30 | |
04454e1e | 31 | use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; |
a2a8927a | 32 | use crate::diagnostics::find_all_local_uses; |
9c376795 | 33 | use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; |
c295e0f8 | 34 | use crate::{ |
6a06907d XL |
35 | borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, |
36 | InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, | |
60c5eb7d XL |
37 | }; |
38 | ||
39 | use super::{ | |
a2a8927a | 40 | explain_borrow::{BorrowExplanation, LaterUseKind}, |
064997fb | 41 | DescribePlaceOpt, RegionName, RegionNameSource, UseSpans, |
60c5eb7d XL |
42 | }; |
43 | ||
dc9dc135 XL |
44 | #[derive(Debug)] |
45 | struct MoveSite { | |
46 | /// Index of the "move out" that we found. The `MoveData` can | |
47 | /// then tell us where the move occurred. | |
48 | moi: MoveOutIndex, | |
49 | ||
50 | /// `true` if we traversed a back edge while walking from the point | |
51 | /// of error to the move site. | |
dfeec247 | 52 | traversed_back_edge: bool, |
dc9dc135 XL |
53 | } |
54 | ||
55 | /// Which case a StorageDeadOrDrop is for. | |
56 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
57 | enum StorageDeadOrDrop<'tcx> { | |
58 | LocalStorageDead, | |
59 | BoxedStorageDead, | |
60 | Destructor(Ty<'tcx>), | |
61 | } | |
62 | ||
63 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | |
c295e0f8 | 64 | pub(crate) fn report_use_of_moved_or_uninitialized( |
dc9dc135 XL |
65 | &mut self, |
66 | location: Location, | |
67 | desired_action: InitializationRequiringAction, | |
74b04a01 | 68 | (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span), |
dc9dc135 XL |
69 | mpi: MovePathIndex, |
70 | ) { | |
71 | debug!( | |
72 | "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \ | |
73 | moved_place={:?} used_place={:?} span={:?} mpi={:?}", | |
74 | location, desired_action, moved_place, used_place, span, mpi | |
75 | ); | |
76 | ||
dfeec247 XL |
77 | let use_spans = |
78 | self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location)); | |
dc9dc135 XL |
79 | let span = use_spans.args_or_use(); |
80 | ||
136023e0 | 81 | let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi); |
1b1a35ee XL |
82 | debug!( |
83 | "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}", | |
84 | move_site_vec, use_spans | |
85 | ); | |
dfeec247 XL |
86 | let move_out_indices: Vec<_> = |
87 | move_site_vec.iter().map(|move_site| move_site.moi).collect(); | |
dc9dc135 XL |
88 | |
89 | if move_out_indices.is_empty() { | |
60c5eb7d | 90 | let root_place = PlaceRef { projection: &[], ..used_place }; |
dc9dc135 | 91 | |
e74abb32 | 92 | if !self.uninitialized_error_reported.insert(root_place) { |
dc9dc135 XL |
93 | debug!( |
94 | "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", | |
95 | root_place | |
96 | ); | |
97 | return; | |
98 | } | |
99 | ||
064997fb FG |
100 | let err = self.report_use_of_uninitialized( |
101 | mpi, | |
102 | used_place, | |
103 | moved_place, | |
104 | desired_action, | |
dc9dc135 | 105 | span, |
064997fb | 106 | use_spans, |
dc9dc135 | 107 | ); |
5099ac24 | 108 | self.buffer_error(err); |
dc9dc135 | 109 | } else { |
5099ac24 | 110 | if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) { |
dfeec247 | 111 | if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) { |
dc9dc135 | 112 | debug!( |
064997fb | 113 | "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}", |
dc9dc135 XL |
114 | move_out_indices |
115 | ); | |
116 | return; | |
117 | } | |
118 | } | |
119 | ||
1b1a35ee XL |
120 | let is_partial_move = move_site_vec.iter().any(|move_site| { |
121 | let move_out = self.move_data.moves[(*move_site).moi]; | |
122 | let moved_place = &self.move_data.move_paths[move_out.path].place; | |
123 | // `*(_1)` where `_1` is a `Box` is actually a move out. | |
29967ef6 | 124 | let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref] |
1b1a35ee XL |
125 | && self.body.local_decls[moved_place.local].ty.is_box(); |
126 | ||
127 | !is_box_move | |
128 | && used_place != moved_place.as_ref() | |
129 | && used_place.is_prefix_of(moved_place.as_ref()) | |
130 | }); | |
131 | ||
132 | let partial_str = if is_partial_move { "partial " } else { "" }; | |
133 | let partially_str = if is_partial_move { "partially " } else { "" }; | |
dc9dc135 | 134 | |
416331ca | 135 | let mut err = self.cannot_act_on_moved_value( |
dc9dc135 XL |
136 | span, |
137 | desired_action.as_noun(), | |
1b1a35ee | 138 | partially_str, |
064997fb FG |
139 | self.describe_place_with_options( |
140 | moved_place, | |
141 | DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, | |
142 | ), | |
dc9dc135 XL |
143 | ); |
144 | ||
136023e0 XL |
145 | let reinit_spans = maybe_reinitialized_locations |
146 | .iter() | |
147 | .take(3) | |
148 | .map(|loc| { | |
149 | self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc) | |
150 | .args_or_use() | |
151 | }) | |
152 | .collect::<Vec<Span>>(); | |
5e7ed085 | 153 | |
136023e0 XL |
154 | let reinits = maybe_reinitialized_locations.len(); |
155 | if reinits == 1 { | |
156 | err.span_label(reinit_spans[0], "this reinitialization might get skipped"); | |
157 | } else if reinits > 1 { | |
158 | err.span_note( | |
159 | MultiSpan::from_spans(reinit_spans), | |
160 | &if reinits <= 3 { | |
9c376795 | 161 | format!("these {reinits} reinitializations might get skipped") |
136023e0 XL |
162 | } else { |
163 | format!( | |
164 | "these 3 reinitializations and {} other{} might get skipped", | |
165 | reinits - 3, | |
166 | if reinits == 4 { "" } else { "s" } | |
167 | ) | |
168 | }, | |
169 | ); | |
170 | } | |
171 | ||
487cf647 | 172 | let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err); |
dc9dc135 XL |
173 | |
174 | let mut is_loop_move = false; | |
5869c6ff | 175 | let mut in_pattern = false; |
487cf647 | 176 | let mut seen_spans = FxHashSet::default(); |
1b1a35ee | 177 | |
dc9dc135 XL |
178 | for move_site in &move_site_vec { |
179 | let move_out = self.move_data.moves[(*move_site).moi]; | |
180 | let moved_place = &self.move_data.move_paths[move_out.path].place; | |
181 | ||
416331ca | 182 | let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); |
dc9dc135 XL |
183 | let move_span = move_spans.args_or_use(); |
184 | ||
dfeec247 | 185 | let move_msg = if move_spans.for_closure() { " into closure" } else { "" }; |
dc9dc135 | 186 | |
5869c6ff XL |
187 | let loop_message = if location == move_out.source || move_site.traversed_back_edge { |
188 | ", in previous iteration of loop" | |
189 | } else { | |
190 | "" | |
191 | }; | |
192 | ||
f035d41b | 193 | if location == move_out.source { |
dc9dc135 | 194 | is_loop_move = true; |
5869c6ff XL |
195 | } |
196 | ||
487cf647 FG |
197 | if !seen_spans.contains(&move_span) { |
198 | if !closure { | |
9c376795 FG |
199 | self.suggest_ref_or_clone( |
200 | mpi, | |
201 | move_span, | |
202 | &mut err, | |
203 | &mut in_pattern, | |
204 | move_spans, | |
205 | ); | |
136023e0 | 206 | } |
487cf647 FG |
207 | |
208 | self.explain_captures( | |
209 | &mut err, | |
210 | span, | |
211 | move_span, | |
212 | move_spans, | |
213 | *moved_place, | |
214 | partially_str, | |
215 | loop_message, | |
216 | move_msg, | |
217 | is_loop_move, | |
218 | maybe_reinitialized_locations.is_empty(), | |
219 | ); | |
dc9dc135 | 220 | } |
487cf647 | 221 | seen_spans.insert(move_span); |
dc9dc135 XL |
222 | } |
223 | ||
487cf647 | 224 | use_spans.var_path_only_subdiag(&mut err, desired_action); |
dc9dc135 XL |
225 | |
226 | if !is_loop_move { | |
227 | err.span_label( | |
228 | span, | |
229 | format!( | |
9c376795 | 230 | "value {} here after {partial_str}move", |
dc9dc135 | 231 | desired_action.as_verb_in_past_tense(), |
dc9dc135 XL |
232 | ), |
233 | ); | |
234 | } | |
235 | ||
5869c6ff | 236 | let ty = used_place.ty(self.body, self.infcx.tcx).ty; |
1b1a35ee | 237 | let needs_note = match ty.kind() { |
dc9dc135 | 238 | ty::Closure(id, _) => { |
9ffffee4 | 239 | self.infcx.tcx.closure_kind_origin(id.expect_local()).is_none() |
dc9dc135 XL |
240 | } |
241 | _ => true, | |
242 | }; | |
243 | ||
f035d41b XL |
244 | let mpi = self.move_data.moves[move_out_indices[0]].path; |
245 | let place = &self.move_data.move_paths[mpi].place; | |
246 | let ty = place.ty(self.body, self.infcx.tcx).ty; | |
247 | ||
5869c6ff | 248 | // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). |
f2b60f7d FG |
249 | // Same for if we're in a loop, see #101119. |
250 | if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { | |
1b1a35ee | 251 | if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { |
f035d41b XL |
252 | // We have a `&mut` ref, we need to reborrow on each iteration (#62112). |
253 | err.span_suggestion_verbose( | |
254 | span.shrink_to_lo(), | |
255 | &format!( | |
256 | "consider creating a fresh reborrow of {} here", | |
257 | self.describe_place(moved_place) | |
9c376795 | 258 | .map(|n| format!("`{n}`")) |
f035d41b XL |
259 | .unwrap_or_else(|| "the mutable reference".to_string()), |
260 | ), | |
923072b8 | 261 | "&mut *", |
f035d41b XL |
262 | Applicability::MachineApplicable, |
263 | ); | |
264 | } | |
265 | } | |
dc9dc135 | 266 | |
064997fb FG |
267 | let opt_name = self.describe_place_with_options( |
268 | place.as_ref(), | |
269 | DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, | |
270 | ); | |
5e7ed085 | 271 | let note_msg = match opt_name { |
9c376795 | 272 | Some(name) => format!("`{name}`"), |
5e7ed085 FG |
273 | None => "value".to_owned(), |
274 | }; | |
275 | if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { | |
276 | // Suppress the next suggestion since we don't want to put more bounds onto | |
277 | // something that already has `Fn`-like bounds (or is a closure), so we can't | |
278 | // restrict anyways. | |
279 | } else { | |
280 | self.suggest_adding_copy_bounds(&mut err, ty, span); | |
281 | } | |
282 | ||
f035d41b | 283 | if needs_note { |
e74abb32 | 284 | let span = if let Some(local) = place.as_local() { |
5e7ed085 | 285 | Some(self.body.local_decls[local].source_info.span) |
dc9dc135 XL |
286 | } else { |
287 | None | |
288 | }; | |
1b1a35ee XL |
289 | self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str); |
290 | } | |
291 | ||
292 | if let UseSpans::FnSelfUse { | |
5099ac24 | 293 | kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, |
1b1a35ee XL |
294 | .. |
295 | } = use_spans | |
296 | { | |
297 | err.note(&format!( | |
9c376795 | 298 | "{} occurs due to deref coercion to `{deref_target_ty}`", |
1b1a35ee | 299 | desired_action.as_noun(), |
1b1a35ee XL |
300 | )); |
301 | ||
c295e0f8 | 302 | // Check first whether the source is accessible (issue #87060) |
064997fb | 303 | if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) { |
c295e0f8 XL |
304 | err.span_note(deref_target, "deref defined here"); |
305 | } | |
dc9dc135 XL |
306 | } |
307 | ||
5099ac24 | 308 | self.buffer_move_error(move_out_indices, (used_place, err)); |
dc9dc135 XL |
309 | } |
310 | } | |
311 | ||
487cf647 FG |
312 | fn suggest_ref_or_clone( |
313 | &mut self, | |
314 | mpi: MovePathIndex, | |
315 | move_span: Span, | |
316 | err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, | |
317 | in_pattern: &mut bool, | |
9c376795 | 318 | move_spans: UseSpans<'_>, |
487cf647 FG |
319 | ) { |
320 | struct ExpressionFinder<'hir> { | |
321 | expr_span: Span, | |
322 | expr: Option<&'hir hir::Expr<'hir>>, | |
323 | pat: Option<&'hir hir::Pat<'hir>>, | |
324 | parent_pat: Option<&'hir hir::Pat<'hir>>, | |
325 | } | |
326 | impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { | |
327 | fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { | |
328 | if e.span == self.expr_span { | |
329 | self.expr = Some(e); | |
330 | } | |
331 | hir::intravisit::walk_expr(self, e); | |
332 | } | |
333 | fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) { | |
334 | if p.span == self.expr_span { | |
335 | self.pat = Some(p); | |
336 | } | |
337 | if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind { | |
338 | if i.span == self.expr_span || p.span == self.expr_span { | |
339 | self.pat = Some(p); | |
340 | } | |
341 | // Check if we are in a situation of `ident @ ident` where we want to suggest | |
342 | // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`. | |
343 | if let Some(subpat) = sub && self.pat.is_none() { | |
344 | self.visit_pat(subpat); | |
345 | if self.pat.is_some() { | |
346 | self.parent_pat = Some(p); | |
347 | } | |
348 | return; | |
349 | } | |
350 | } | |
351 | hir::intravisit::walk_pat(self, p); | |
352 | } | |
353 | } | |
354 | let hir = self.infcx.tcx.hir(); | |
355 | if let Some(hir::Node::Item(hir::Item { | |
356 | kind: hir::ItemKind::Fn(_, _, body_id), | |
357 | .. | |
9c376795 | 358 | })) = hir.find(self.mir_hir_id()) |
487cf647 FG |
359 | && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) |
360 | { | |
361 | let place = &self.move_data.move_paths[mpi].place; | |
362 | let span = place.as_local() | |
363 | .map(|local| self.body.local_decls[local].source_info.span); | |
364 | let mut finder = ExpressionFinder { | |
365 | expr_span: move_span, | |
366 | expr: None, | |
367 | pat: None, | |
368 | parent_pat: None, | |
369 | }; | |
370 | finder.visit_expr(expr); | |
371 | if let Some(span) = span && let Some(expr) = finder.expr { | |
372 | for (_, expr) in hir.parent_iter(expr.hir_id) { | |
373 | if let hir::Node::Expr(expr) = expr { | |
374 | if expr.span.contains(span) { | |
375 | // If the let binding occurs within the same loop, then that | |
376 | // loop isn't relevant, like in the following, the outermost `loop` | |
377 | // doesn't play into `x` being moved. | |
378 | // ``` | |
379 | // loop { | |
380 | // let x = String::new(); | |
381 | // loop { | |
382 | // foo(x); | |
383 | // } | |
384 | // } | |
385 | // ``` | |
386 | break; | |
387 | } | |
388 | if let hir::ExprKind::Loop(.., loop_span) = expr.kind { | |
389 | err.span_label(loop_span, "inside of this loop"); | |
390 | } | |
391 | } | |
392 | } | |
393 | let typeck = self.infcx.tcx.typeck(self.mir_def_id()); | |
9c376795 | 394 | let hir_id = hir.parent_id(expr.hir_id); |
487cf647 FG |
395 | if let Some(parent) = hir.find(hir_id) { |
396 | let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent | |
397 | && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind | |
398 | && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id) | |
399 | { | |
400 | (def_id.as_local(), args, 1) | |
401 | } else if let hir::Node::Expr(parent_expr) = parent | |
402 | && let hir::ExprKind::Call(call, args) = parent_expr.kind | |
403 | && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() | |
404 | { | |
405 | (def_id.as_local(), args, 0) | |
406 | } else { | |
407 | (None, &[][..], 0) | |
408 | }; | |
409 | if let Some(def_id) = def_id | |
410 | && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id)) | |
411 | && let Some(fn_sig) = node.fn_sig() | |
412 | && let Some(ident) = node.ident() | |
413 | && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) | |
414 | && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) | |
415 | { | |
416 | let mut span: MultiSpan = arg.span.into(); | |
417 | span.push_span_label( | |
418 | arg.span, | |
419 | "this parameter takes ownership of the value".to_string(), | |
420 | ); | |
421 | let descr = match node.fn_kind() { | |
422 | Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function", | |
423 | Some(hir::intravisit::FnKind::Method(..)) => "method", | |
424 | Some(hir::intravisit::FnKind::Closure) => "closure", | |
425 | }; | |
426 | span.push_span_label( | |
427 | ident.span, | |
428 | format!("in this {descr}"), | |
429 | ); | |
430 | err.span_note( | |
431 | span, | |
432 | format!( | |
433 | "consider changing this parameter type in {descr} `{ident}` to \ | |
434 | borrow instead if owning the value isn't necessary", | |
435 | ), | |
436 | ); | |
437 | } | |
438 | let place = &self.move_data.move_paths[mpi].place; | |
439 | let ty = place.ty(self.body, self.infcx.tcx).ty; | |
440 | if let hir::Node::Expr(parent_expr) = parent | |
441 | && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind | |
442 | && let hir::ExprKind::Path( | |
443 | hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _) | |
444 | ) = call_expr.kind | |
445 | { | |
446 | // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing. | |
9c376795 FG |
447 | } else if let UseSpans::FnSelfUse { |
448 | kind: CallKind::Normal { .. }, | |
449 | .. | |
450 | } = move_spans { | |
451 | // We already suggest cloning for these cases in `explain_captures`. | |
487cf647 FG |
452 | } else { |
453 | self.suggest_cloning(err, ty, move_span); | |
454 | } | |
455 | } | |
456 | } | |
457 | if let Some(pat) = finder.pat { | |
458 | *in_pattern = true; | |
459 | let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; | |
460 | if let Some(pat) = finder.parent_pat { | |
461 | sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string())); | |
462 | } | |
463 | err.multipart_suggestion_verbose( | |
464 | "borrow this binding in the pattern to avoid moving the value", | |
465 | sugg, | |
466 | Applicability::MachineApplicable, | |
467 | ); | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
064997fb FG |
472 | fn report_use_of_uninitialized( |
473 | &self, | |
474 | mpi: MovePathIndex, | |
475 | used_place: PlaceRef<'tcx>, | |
476 | moved_place: PlaceRef<'tcx>, | |
477 | desired_action: InitializationRequiringAction, | |
478 | span: Span, | |
479 | use_spans: UseSpans<'tcx>, | |
480 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { | |
481 | // We need all statements in the body where the binding was assigned to to later find all | |
482 | // the branching code paths where the binding *wasn't* assigned to. | |
483 | let inits = &self.move_data.init_path_map[mpi]; | |
484 | let move_path = &self.move_data.move_paths[mpi]; | |
485 | let decl_span = self.body.local_decls[move_path.place.local].source_info.span; | |
486 | let mut spans = vec![]; | |
487 | for init_idx in inits { | |
488 | let init = &self.move_data.inits[*init_idx]; | |
489 | let span = init.span(&self.body); | |
490 | if !span.is_dummy() { | |
491 | spans.push(span); | |
492 | } | |
493 | } | |
494 | ||
495 | let (name, desc) = match self.describe_place_with_options( | |
496 | moved_place, | |
497 | DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, | |
498 | ) { | |
499 | Some(name) => (format!("`{name}`"), format!("`{name}` ")), | |
500 | None => ("the variable".to_string(), String::new()), | |
501 | }; | |
502 | let path = match self.describe_place_with_options( | |
503 | used_place, | |
504 | DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, | |
505 | ) { | |
506 | Some(name) => format!("`{name}`"), | |
507 | None => "value".to_string(), | |
508 | }; | |
509 | ||
510 | // We use the statements were the binding was initialized, and inspect the HIR to look | |
511 | // for the branching codepaths that aren't covered, to point at them. | |
512 | let map = self.infcx.tcx.hir(); | |
513 | let body_id = map.body_owned_by(self.mir_def_id()); | |
514 | let body = map.body(body_id); | |
515 | ||
516 | let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; | |
517 | visitor.visit_body(&body); | |
518 | ||
2b03887a | 519 | let mut show_assign_sugg = false; |
064997fb FG |
520 | let isnt_initialized = if let InitializationRequiringAction::PartialAssignment |
521 | | InitializationRequiringAction::Assignment = desired_action | |
522 | { | |
523 | // The same error is emitted for bindings that are *sometimes* initialized and the ones | |
524 | // that are *partially* initialized by assigning to a field of an uninitialized | |
525 | // binding. We differentiate between them for more accurate wording here. | |
526 | "isn't fully initialized" | |
9c376795 FG |
527 | } else if !spans.iter().any(|i| { |
528 | // We filter these to avoid misleading wording in cases like the following, | |
529 | // where `x` has an `init`, but it is in the same place we're looking at: | |
530 | // ``` | |
531 | // let x; | |
532 | // x += 1; | |
533 | // ``` | |
534 | !i.contains(span) | |
535 | // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs` | |
536 | && !visitor | |
537 | .errors | |
538 | .iter() | |
539 | .map(|(sp, _)| *sp) | |
540 | .any(|sp| span < sp && !sp.contains(span)) | |
541 | }) { | |
2b03887a | 542 | show_assign_sugg = true; |
064997fb FG |
543 | "isn't initialized" |
544 | } else { | |
545 | "is possibly-uninitialized" | |
546 | }; | |
547 | ||
548 | let used = desired_action.as_general_verb_in_past_tense(); | |
549 | let mut err = | |
550 | struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}"); | |
487cf647 | 551 | use_spans.var_path_only_subdiag(&mut err, desired_action); |
064997fb FG |
552 | |
553 | if let InitializationRequiringAction::PartialAssignment | |
554 | | InitializationRequiringAction::Assignment = desired_action | |
555 | { | |
556 | err.help( | |
557 | "partial initialization isn't supported, fully initialize the binding with a \ | |
558 | default value and mutate it, or use `std::mem::MaybeUninit`", | |
559 | ); | |
560 | } | |
561 | err.span_label(span, format!("{path} {used} here but it {isnt_initialized}")); | |
562 | ||
563 | let mut shown = false; | |
564 | for (sp, label) in visitor.errors { | |
565 | if sp < span && !sp.overlaps(span) { | |
566 | // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention | |
567 | // match arms coming after the primary span because they aren't relevant: | |
568 | // ``` | |
569 | // let x; | |
570 | // match y { | |
571 | // _ if { x = 2; true } => {} | |
572 | // _ if { | |
573 | // x; //~ ERROR | |
574 | // false | |
575 | // } => {} | |
576 | // _ => {} // We don't want to point to this. | |
577 | // }; | |
578 | // ``` | |
579 | err.span_label(sp, &label); | |
580 | shown = true; | |
581 | } | |
582 | } | |
583 | if !shown { | |
584 | for sp in &spans { | |
585 | if *sp < span && !sp.overlaps(span) { | |
586 | err.span_label(*sp, "binding initialized here in some conditions"); | |
587 | } | |
588 | } | |
589 | } | |
2b03887a | 590 | |
064997fb | 591 | err.span_label(decl_span, "binding declared here but left uninitialized"); |
2b03887a FG |
592 | if show_assign_sugg { |
593 | struct LetVisitor { | |
594 | decl_span: Span, | |
595 | sugg_span: Option<Span>, | |
596 | } | |
597 | ||
598 | impl<'v> Visitor<'v> for LetVisitor { | |
599 | fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { | |
600 | if self.sugg_span.is_some() { | |
601 | return; | |
602 | } | |
603 | if let hir::StmtKind::Local(hir::Local { | |
604 | span, ty, init: None, .. | |
605 | }) = &ex.kind && span.contains(self.decl_span) { | |
606 | self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span)); | |
607 | } | |
608 | hir::intravisit::walk_stmt(self, ex); | |
609 | } | |
610 | } | |
611 | ||
612 | let mut visitor = LetVisitor { decl_span, sugg_span: None }; | |
613 | visitor.visit_body(&body); | |
614 | if let Some(span) = visitor.sugg_span { | |
615 | self.suggest_assign_value(&mut err, moved_place, span); | |
616 | } | |
617 | } | |
064997fb FG |
618 | err |
619 | } | |
620 | ||
2b03887a FG |
621 | fn suggest_assign_value( |
622 | &self, | |
623 | err: &mut Diagnostic, | |
624 | moved_place: PlaceRef<'tcx>, | |
625 | sugg_span: Span, | |
626 | ) { | |
627 | let ty = moved_place.ty(self.body, self.infcx.tcx).ty; | |
628 | debug!("ty: {:?}, kind: {:?}", ty, ty.kind()); | |
629 | ||
630 | let tcx = self.infcx.tcx; | |
631 | let implements_default = |ty, param_env| { | |
632 | let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { | |
633 | return false; | |
634 | }; | |
635 | // Regions are already solved, so we must use a fresh InferCtxt, | |
636 | // but the type has region variables, so erase those. | |
637 | tcx.infer_ctxt() | |
638 | .build() | |
487cf647 | 639 | .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env) |
2b03887a FG |
640 | .must_apply_modulo_regions() |
641 | }; | |
642 | ||
643 | let assign_value = match ty.kind() { | |
644 | ty::Bool => "false", | |
645 | ty::Float(_) => "0.0", | |
646 | ty::Int(_) | ty::Uint(_) => "0", | |
647 | ty::Never | ty::Error(_) => "", | |
648 | ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]", | |
649 | ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()", | |
650 | _ => "todo!()", | |
651 | }; | |
652 | ||
653 | if !assign_value.is_empty() { | |
654 | err.span_suggestion_verbose( | |
655 | sugg_span.shrink_to_hi(), | |
9c376795 | 656 | "consider assigning a value", |
2b03887a FG |
657 | format!(" = {}", assign_value), |
658 | Applicability::MaybeIncorrect, | |
659 | ); | |
660 | } | |
661 | } | |
662 | ||
5e7ed085 FG |
663 | fn suggest_borrow_fn_like( |
664 | &self, | |
f2b60f7d | 665 | err: &mut Diagnostic, |
5e7ed085 FG |
666 | ty: Ty<'tcx>, |
667 | move_sites: &[MoveSite], | |
668 | value_name: &str, | |
669 | ) -> bool { | |
670 | let tcx = self.infcx.tcx; | |
671 | ||
672 | // Find out if the predicates show that the type is a Fn or FnMut | |
9c376795 FG |
673 | let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| { |
674 | if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder() | |
675 | && pred.self_ty() == ty | |
676 | { | |
677 | if Some(pred.def_id()) == tcx.lang_items().fn_trait() { | |
678 | return Some(hir::Mutability::Not); | |
679 | } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() { | |
680 | return Some(hir::Mutability::Mut); | |
5e7ed085 | 681 | } |
9c376795 FG |
682 | } |
683 | None | |
487cf647 | 684 | }; |
5e7ed085 FG |
685 | |
686 | // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably) | |
687 | // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`. | |
688 | // These types seem reasonably opaque enough that they could be substituted with their | |
689 | // borrowed variants in a function body when we see a move error. | |
9c376795 FG |
690 | let borrow_level = match *ty.kind() { |
691 | ty::Param(_) => tcx | |
692 | .explicit_predicates_of(self.mir_def_id().to_def_id()) | |
693 | .predicates | |
694 | .iter() | |
695 | .copied() | |
696 | .find_map(find_fn_kind_from_did), | |
697 | ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx | |
698 | .bound_explicit_item_bounds(def_id) | |
699 | .subst_iter_copied(tcx, substs) | |
700 | .find_map(find_fn_kind_from_did), | |
5e7ed085 FG |
701 | ty::Closure(_, substs) => match substs.as_closure().kind() { |
702 | ty::ClosureKind::Fn => Some(hir::Mutability::Not), | |
703 | ty::ClosureKind::FnMut => Some(hir::Mutability::Mut), | |
704 | _ => None, | |
705 | }, | |
706 | _ => None, | |
707 | }; | |
708 | ||
709 | let Some(borrow_level) = borrow_level else { return false; }; | |
710 | let sugg = move_sites | |
711 | .iter() | |
712 | .map(|move_site| { | |
713 | let move_out = self.move_data.moves[(*move_site).moi]; | |
714 | let moved_place = &self.move_data.move_paths[move_out.path].place; | |
715 | let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); | |
716 | let move_span = move_spans.args_or_use(); | |
487cf647 | 717 | let suggestion = borrow_level.ref_prefix_str().to_owned(); |
5e7ed085 FG |
718 | (move_span.shrink_to_lo(), suggestion) |
719 | }) | |
720 | .collect(); | |
721 | err.multipart_suggestion_verbose( | |
487cf647 | 722 | format!("consider {}borrowing {value_name}", borrow_level.mutably_str()), |
5e7ed085 FG |
723 | sugg, |
724 | Applicability::MaybeIncorrect, | |
725 | ); | |
726 | true | |
727 | } | |
728 | ||
487cf647 FG |
729 | fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { |
730 | let tcx = self.infcx.tcx; | |
731 | // Try to find predicates on *generic params* that would allow copying `ty` | |
732 | let infcx = tcx.infer_ctxt().build(); | |
733 | ||
734 | if let Some(clone_trait_def) = tcx.lang_items().clone_trait() | |
735 | && infcx | |
736 | .type_implements_trait( | |
737 | clone_trait_def, | |
738 | [tcx.erase_regions(ty)], | |
739 | self.param_env, | |
740 | ) | |
741 | .must_apply_modulo_regions() | |
742 | { | |
743 | err.span_suggestion_verbose( | |
744 | span.shrink_to_hi(), | |
745 | "consider cloning the value if the performance cost is acceptable", | |
9c376795 | 746 | ".clone()", |
487cf647 FG |
747 | Applicability::MachineApplicable, |
748 | ); | |
749 | } | |
750 | } | |
751 | ||
f2b60f7d | 752 | fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { |
5e7ed085 FG |
753 | let tcx = self.infcx.tcx; |
754 | let generics = tcx.generics_of(self.mir_def_id()); | |
755 | ||
756 | let Some(hir_generics) = tcx | |
757 | .typeck_root_def_id(self.mir_def_id().to_def_id()) | |
758 | .as_local() | |
759 | .and_then(|def_id| tcx.hir().get_generics(def_id)) | |
760 | else { return; }; | |
761 | // Try to find predicates on *generic params* that would allow copying `ty` | |
2b03887a | 762 | let infcx = tcx.infer_ctxt().build(); |
487cf647 | 763 | let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span)); |
2b03887a FG |
764 | let cause = ObligationCause::new( |
765 | span, | |
9ffffee4 | 766 | self.mir_def_id(), |
2b03887a FG |
767 | rustc_infer::traits::ObligationCauseCode::MiscObligation, |
768 | ); | |
487cf647 | 769 | let errors = rustc_trait_selection::traits::fully_solve_bound( |
2b03887a | 770 | &infcx, |
487cf647 | 771 | cause, |
2b03887a FG |
772 | self.param_env, |
773 | // Erase any region vids from the type, which may not be resolved | |
774 | infcx.tcx.erase_regions(ty), | |
775 | copy_did, | |
2b03887a | 776 | ); |
2b03887a FG |
777 | |
778 | // Only emit suggestion if all required predicates are on generic | |
779 | let predicates: Result<Vec<_>, _> = errors | |
780 | .into_iter() | |
781 | .map(|err| match err.obligation.predicate.kind().skip_binder() { | |
487cf647 FG |
782 | PredicateKind::Clause(ty::Clause::Trait(predicate)) => { |
783 | match predicate.self_ty().kind() { | |
784 | ty::Param(param_ty) => Ok(( | |
785 | generics.type_param(param_ty, tcx), | |
786 | predicate.trait_ref.print_only_trait_path().to_string(), | |
787 | )), | |
788 | _ => Err(()), | |
789 | } | |
790 | } | |
2b03887a FG |
791 | _ => Err(()), |
792 | }) | |
793 | .collect(); | |
5e7ed085 FG |
794 | |
795 | if let Ok(predicates) = predicates { | |
796 | suggest_constraining_type_params( | |
797 | tcx, | |
798 | hir_generics, | |
799 | err, | |
800 | predicates | |
801 | .iter() | |
802 | .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), | |
9ffffee4 | 803 | None, |
5e7ed085 FG |
804 | ); |
805 | } | |
806 | } | |
807 | ||
c295e0f8 | 808 | pub(crate) fn report_move_out_while_borrowed( |
dc9dc135 XL |
809 | &mut self, |
810 | location: Location, | |
ba9703b0 | 811 | (place, span): (Place<'tcx>, Span), |
dc9dc135 XL |
812 | borrow: &BorrowData<'tcx>, |
813 | ) { | |
814 | debug!( | |
815 | "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}", | |
816 | location, place, span, borrow | |
817 | ); | |
ba9703b0 XL |
818 | let value_msg = self.describe_any_place(place.as_ref()); |
819 | let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref()); | |
dc9dc135 XL |
820 | |
821 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
822 | let borrow_span = borrow_spans.args_or_use(); | |
823 | ||
416331ca | 824 | let move_spans = self.move_spans(place.as_ref(), location); |
dc9dc135 XL |
825 | let span = move_spans.args_or_use(); |
826 | ||
487cf647 FG |
827 | let mut err = self.cannot_move_when_borrowed( |
828 | span, | |
829 | borrow_span, | |
830 | &self.describe_any_place(place.as_ref()), | |
831 | &borrow_msg, | |
832 | &value_msg, | |
dc9dc135 XL |
833 | ); |
834 | ||
487cf647 FG |
835 | borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow); |
836 | ||
17df50a5 XL |
837 | move_spans.var_span_label( |
838 | &mut err, | |
839 | format!("move occurs due to use{}", move_spans.describe()), | |
840 | "moved", | |
841 | ); | |
dc9dc135 | 842 | |
dfeec247 XL |
843 | self.explain_why_borrow_contains_point(location, borrow, None) |
844 | .add_explanation_to_diagnostic( | |
845 | self.infcx.tcx, | |
846 | &self.body, | |
847 | &self.local_names, | |
848 | &mut err, | |
849 | "", | |
850 | Some(borrow_span), | |
136023e0 | 851 | None, |
dfeec247 | 852 | ); |
5099ac24 | 853 | self.buffer_error(err); |
dc9dc135 XL |
854 | } |
855 | ||
c295e0f8 | 856 | pub(crate) fn report_use_while_mutably_borrowed( |
dc9dc135 XL |
857 | &mut self, |
858 | location: Location, | |
ba9703b0 | 859 | (place, _span): (Place<'tcx>, Span), |
dc9dc135 | 860 | borrow: &BorrowData<'tcx>, |
5e7ed085 | 861 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 XL |
862 | let borrow_spans = self.retrieve_borrow_spans(borrow); |
863 | let borrow_span = borrow_spans.args_or_use(); | |
864 | ||
865 | // Conflicting borrows are reported separately, so only check for move | |
866 | // captures. | |
416331ca | 867 | let use_spans = self.move_spans(place.as_ref(), location); |
dc9dc135 XL |
868 | let span = use_spans.var_or_use(); |
869 | ||
17df50a5 XL |
870 | // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use |
871 | // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure | |
416331ca | 872 | let mut err = self.cannot_use_when_mutably_borrowed( |
dc9dc135 | 873 | span, |
ba9703b0 | 874 | &self.describe_any_place(place.as_ref()), |
dc9dc135 | 875 | borrow_span, |
ba9703b0 | 876 | &self.describe_any_place(borrow.borrowed_place.as_ref()), |
dc9dc135 | 877 | ); |
487cf647 FG |
878 | borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| { |
879 | use crate::session_diagnostics::CaptureVarCause::*; | |
880 | let place = &borrow.borrowed_place; | |
881 | let desc_place = self.describe_any_place(place.as_ref()); | |
882 | match kind { | |
883 | Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span }, | |
884 | None => BorrowUsePlaceClosure { place: desc_place, var_span }, | |
885 | } | |
886 | }); | |
dc9dc135 XL |
887 | |
888 | self.explain_why_borrow_contains_point(location, borrow, None) | |
60c5eb7d XL |
889 | .add_explanation_to_diagnostic( |
890 | self.infcx.tcx, | |
891 | &self.body, | |
892 | &self.local_names, | |
893 | &mut err, | |
894 | "", | |
895 | None, | |
136023e0 | 896 | None, |
60c5eb7d | 897 | ); |
dc9dc135 XL |
898 | err |
899 | } | |
900 | ||
c295e0f8 | 901 | pub(crate) fn report_conflicting_borrow( |
dc9dc135 XL |
902 | &mut self, |
903 | location: Location, | |
ba9703b0 | 904 | (place, span): (Place<'tcx>, Span), |
dc9dc135 XL |
905 | gen_borrow_kind: BorrowKind, |
906 | issued_borrow: &BorrowData<'tcx>, | |
5e7ed085 | 907 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 XL |
908 | let issued_spans = self.retrieve_borrow_spans(issued_borrow); |
909 | let issued_span = issued_spans.args_or_use(); | |
910 | ||
911 | let borrow_spans = self.borrow_spans(span, location); | |
912 | let span = borrow_spans.args_or_use(); | |
913 | ||
914 | let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { | |
915 | "generator" | |
916 | } else { | |
917 | "closure" | |
918 | }; | |
919 | ||
920 | let (desc_place, msg_place, msg_borrow, union_type_name) = | |
ba9703b0 | 921 | self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place); |
dc9dc135 XL |
922 | |
923 | let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None); | |
dfeec247 | 924 | let second_borrow_desc = if explanation.is_explained() { "second " } else { "" }; |
dc9dc135 XL |
925 | |
926 | // FIXME: supply non-"" `opt_via` when appropriate | |
dc9dc135 | 927 | let first_borrow_desc; |
dfeec247 | 928 | let mut err = match (gen_borrow_kind, issued_borrow.kind) { |
60c5eb7d | 929 | (BorrowKind::Shared, BorrowKind::Mut { .. }) => { |
dc9dc135 | 930 | first_borrow_desc = "mutable "; |
416331ca | 931 | self.cannot_reborrow_already_borrowed( |
dc9dc135 XL |
932 | span, |
933 | &desc_place, | |
934 | &msg_place, | |
60c5eb7d | 935 | "immutable", |
dc9dc135 XL |
936 | issued_span, |
937 | "it", | |
60c5eb7d | 938 | "mutable", |
dc9dc135 XL |
939 | &msg_borrow, |
940 | None, | |
dc9dc135 XL |
941 | ) |
942 | } | |
60c5eb7d | 943 | (BorrowKind::Mut { .. }, BorrowKind::Shared) => { |
dc9dc135 | 944 | first_borrow_desc = "immutable "; |
9c376795 | 945 | let mut err = self.cannot_reborrow_already_borrowed( |
dc9dc135 XL |
946 | span, |
947 | &desc_place, | |
948 | &msg_place, | |
60c5eb7d | 949 | "mutable", |
dc9dc135 XL |
950 | issued_span, |
951 | "it", | |
60c5eb7d | 952 | "immutable", |
dc9dc135 XL |
953 | &msg_borrow, |
954 | None, | |
9c376795 FG |
955 | ); |
956 | self.suggest_binding_for_closure_capture_self( | |
957 | &mut err, | |
958 | issued_borrow.borrowed_place, | |
959 | &issued_spans, | |
960 | ); | |
961 | err | |
dc9dc135 XL |
962 | } |
963 | ||
60c5eb7d | 964 | (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => { |
dc9dc135 | 965 | first_borrow_desc = "first "; |
74b04a01 | 966 | let mut err = self.cannot_mutably_borrow_multiply( |
dc9dc135 XL |
967 | span, |
968 | &desc_place, | |
969 | &msg_place, | |
970 | issued_span, | |
971 | &msg_borrow, | |
972 | None, | |
74b04a01 XL |
973 | ); |
974 | self.suggest_split_at_mut_if_applicable( | |
975 | &mut err, | |
ba9703b0 XL |
976 | place, |
977 | issued_borrow.borrowed_place, | |
74b04a01 XL |
978 | ); |
979 | err | |
dc9dc135 XL |
980 | } |
981 | ||
60c5eb7d | 982 | (BorrowKind::Unique, BorrowKind::Unique) => { |
dc9dc135 | 983 | first_borrow_desc = "first "; |
dfeec247 | 984 | self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) |
dc9dc135 XL |
985 | } |
986 | ||
ba9703b0 | 987 | (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => { |
dfeec247 | 988 | if let Some(immutable_section_description) = |
ba9703b0 | 989 | self.classify_immutable_section(issued_borrow.assigned_place) |
dfeec247 | 990 | { |
60c5eb7d XL |
991 | let mut err = self.cannot_mutate_in_immutable_section( |
992 | span, | |
993 | issued_span, | |
994 | &desc_place, | |
995 | immutable_section_description, | |
996 | "mutably borrow", | |
997 | ); | |
998 | borrow_spans.var_span_label( | |
999 | &mut err, | |
1000 | format!( | |
ba9703b0 | 1001 | "borrow occurs due to use of {}{}", |
60c5eb7d XL |
1002 | desc_place, |
1003 | borrow_spans.describe(), | |
1004 | ), | |
17df50a5 | 1005 | "immutable", |
60c5eb7d | 1006 | ); |
dc9dc135 | 1007 | |
60c5eb7d XL |
1008 | return err; |
1009 | } else { | |
1010 | first_borrow_desc = "immutable "; | |
1011 | self.cannot_reborrow_already_borrowed( | |
1012 | span, | |
1013 | &desc_place, | |
1014 | &msg_place, | |
1015 | "mutable", | |
1016 | issued_span, | |
1017 | "it", | |
1018 | "immutable", | |
1019 | &msg_borrow, | |
1020 | None, | |
1021 | ) | |
1022 | } | |
dc9dc135 XL |
1023 | } |
1024 | ||
60c5eb7d | 1025 | (BorrowKind::Unique, _) => { |
dc9dc135 | 1026 | first_borrow_desc = "first "; |
416331ca | 1027 | self.cannot_uniquely_borrow_by_one_closure( |
dc9dc135 XL |
1028 | span, |
1029 | container_name, | |
1030 | &desc_place, | |
1031 | "", | |
1032 | issued_span, | |
1033 | "it", | |
1034 | "", | |
1035 | None, | |
dc9dc135 | 1036 | ) |
dfeec247 | 1037 | } |
dc9dc135 | 1038 | |
60c5eb7d | 1039 | (BorrowKind::Shared, BorrowKind::Unique) => { |
dc9dc135 | 1040 | first_borrow_desc = "first "; |
416331ca | 1041 | self.cannot_reborrow_already_uniquely_borrowed( |
dc9dc135 XL |
1042 | span, |
1043 | container_name, | |
1044 | &desc_place, | |
1045 | "", | |
60c5eb7d | 1046 | "immutable", |
dc9dc135 XL |
1047 | issued_span, |
1048 | "", | |
1049 | None, | |
1050 | second_borrow_desc, | |
dc9dc135 XL |
1051 | ) |
1052 | } | |
1053 | ||
60c5eb7d | 1054 | (BorrowKind::Mut { .. }, BorrowKind::Unique) => { |
dc9dc135 | 1055 | first_borrow_desc = "first "; |
416331ca | 1056 | self.cannot_reborrow_already_uniquely_borrowed( |
dc9dc135 XL |
1057 | span, |
1058 | container_name, | |
1059 | &desc_place, | |
1060 | "", | |
60c5eb7d | 1061 | "mutable", |
dc9dc135 XL |
1062 | issued_span, |
1063 | "", | |
1064 | None, | |
1065 | second_borrow_desc, | |
dc9dc135 XL |
1066 | ) |
1067 | } | |
1068 | ||
ba9703b0 XL |
1069 | (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow) |
1070 | | ( | |
1071 | BorrowKind::Shallow, | |
1072 | BorrowKind::Mut { .. } | |
1073 | | BorrowKind::Unique | |
1074 | | BorrowKind::Shared | |
1075 | | BorrowKind::Shallow, | |
1076 | ) => unreachable!(), | |
dc9dc135 XL |
1077 | }; |
1078 | ||
1079 | if issued_spans == borrow_spans { | |
1080 | borrow_spans.var_span_label( | |
1081 | &mut err, | |
17df50a5 XL |
1082 | format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),), |
1083 | gen_borrow_kind.describe_mutability(), | |
dc9dc135 XL |
1084 | ); |
1085 | } else { | |
1086 | let borrow_place = &issued_borrow.borrowed_place; | |
ba9703b0 | 1087 | let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); |
dc9dc135 XL |
1088 | issued_spans.var_span_label( |
1089 | &mut err, | |
1090 | format!( | |
ba9703b0 | 1091 | "first borrow occurs due to use of {}{}", |
dc9dc135 XL |
1092 | borrow_place_desc, |
1093 | issued_spans.describe(), | |
1094 | ), | |
17df50a5 | 1095 | issued_borrow.kind.describe_mutability(), |
dc9dc135 XL |
1096 | ); |
1097 | ||
1098 | borrow_spans.var_span_label( | |
1099 | &mut err, | |
1100 | format!( | |
ba9703b0 | 1101 | "second borrow occurs due to use of {}{}", |
dc9dc135 XL |
1102 | desc_place, |
1103 | borrow_spans.describe(), | |
1104 | ), | |
17df50a5 | 1105 | gen_borrow_kind.describe_mutability(), |
dc9dc135 XL |
1106 | ); |
1107 | } | |
1108 | ||
1109 | if union_type_name != "" { | |
1110 | err.note(&format!( | |
ba9703b0 | 1111 | "{} is a field of the union `{}`, so it overlaps the field {}", |
dc9dc135 XL |
1112 | msg_place, union_type_name, msg_borrow, |
1113 | )); | |
1114 | } | |
1115 | ||
1116 | explanation.add_explanation_to_diagnostic( | |
1117 | self.infcx.tcx, | |
60c5eb7d XL |
1118 | &self.body, |
1119 | &self.local_names, | |
dc9dc135 XL |
1120 | &mut err, |
1121 | first_borrow_desc, | |
1122 | None, | |
136023e0 | 1123 | Some((issued_span, span)), |
dc9dc135 XL |
1124 | ); |
1125 | ||
04454e1e | 1126 | self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation); |
a2a8927a | 1127 | |
dc9dc135 XL |
1128 | err |
1129 | } | |
1130 | ||
a2a8927a XL |
1131 | #[instrument(level = "debug", skip(self, err))] |
1132 | fn suggest_using_local_if_applicable( | |
1133 | &self, | |
5e7ed085 | 1134 | err: &mut Diagnostic, |
a2a8927a | 1135 | location: Location, |
a2a8927a | 1136 | issued_borrow: &BorrowData<'tcx>, |
923072b8 | 1137 | explanation: BorrowExplanation<'tcx>, |
a2a8927a | 1138 | ) { |
04454e1e FG |
1139 | let used_in_call = matches!( |
1140 | explanation, | |
1141 | BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _) | |
1142 | ); | |
a2a8927a XL |
1143 | if !used_in_call { |
1144 | debug!("not later used in call"); | |
1145 | return; | |
1146 | } | |
1147 | ||
04454e1e FG |
1148 | let use_span = |
1149 | if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation { | |
1150 | Some(use_span) | |
1151 | } else { | |
1152 | None | |
1153 | }; | |
1154 | ||
a2a8927a XL |
1155 | let outer_call_loc = |
1156 | if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location { | |
1157 | loc | |
1158 | } else { | |
1159 | issued_borrow.reserve_location | |
1160 | }; | |
1161 | let outer_call_stmt = self.body.stmt_at(outer_call_loc); | |
1162 | ||
1163 | let inner_param_location = location; | |
1164 | let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else { | |
1165 | debug!("`inner_param_location` {:?} is not for a statement", inner_param_location); | |
1166 | return; | |
1167 | }; | |
1168 | let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else { | |
1169 | debug!( | |
1170 | "`inner_param_location` {:?} is not for an assignment: {:?}", | |
1171 | inner_param_location, inner_param_stmt | |
1172 | ); | |
1173 | return; | |
1174 | }; | |
1175 | let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local); | |
04454e1e | 1176 | let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| { |
a2a8927a XL |
1177 | let Either::Right(term) = self.body.stmt_at(loc) else { |
1178 | debug!("{:?} is a statement, so it can't be a call", loc); | |
1179 | return None; | |
1180 | }; | |
1181 | let TerminatorKind::Call { args, .. } = &term.kind else { | |
1182 | debug!("not a call: {:?}", term); | |
1183 | return None; | |
1184 | }; | |
1185 | debug!("checking call args for uses of inner_param: {:?}", args); | |
9ffffee4 | 1186 | args.contains(&Operand::Move(inner_param)).then_some((loc, term)) |
a2a8927a XL |
1187 | }) else { |
1188 | debug!("no uses of inner_param found as a by-move call arg"); | |
1189 | return; | |
1190 | }; | |
1191 | debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc); | |
1192 | ||
1193 | let inner_call_span = inner_call_term.source_info.span; | |
04454e1e FG |
1194 | let outer_call_span = match use_span { |
1195 | Some(span) => span, | |
1196 | None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span, | |
1197 | }; | |
a2a8927a XL |
1198 | if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) { |
1199 | // FIXME: This stops the suggestion in some cases where it should be emitted. | |
1200 | // Fix the spans for those cases so it's emitted correctly. | |
1201 | debug!( | |
1202 | "outer span {:?} does not strictly contain inner span {:?}", | |
1203 | outer_call_span, inner_call_span | |
1204 | ); | |
1205 | return; | |
1206 | } | |
04454e1e FG |
1207 | err.span_help( |
1208 | inner_call_span, | |
1209 | &format!( | |
1210 | "try adding a local storing this{}...", | |
1211 | if use_span.is_some() { "" } else { " argument" } | |
1212 | ), | |
1213 | ); | |
1214 | err.span_help( | |
1215 | outer_call_span, | |
1216 | &format!( | |
1217 | "...and then using that local {}", | |
1218 | if use_span.is_some() { "here" } else { "as the argument to this call" } | |
1219 | ), | |
1220 | ); | |
a2a8927a XL |
1221 | } |
1222 | ||
74b04a01 XL |
1223 | fn suggest_split_at_mut_if_applicable( |
1224 | &self, | |
5e7ed085 | 1225 | err: &mut Diagnostic, |
ba9703b0 XL |
1226 | place: Place<'tcx>, |
1227 | borrowed_place: Place<'tcx>, | |
74b04a01 | 1228 | ) { |
ba9703b0 XL |
1229 | if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) = |
1230 | (&place.projection[..], &borrowed_place.projection[..]) | |
1231 | { | |
1232 | err.help( | |
1233 | "consider using `.split_at_mut(position)` or similar method to obtain \ | |
74b04a01 | 1234 | two mutable non-overlapping sub-slices", |
ba9703b0 | 1235 | ); |
74b04a01 XL |
1236 | } |
1237 | } | |
1238 | ||
9c376795 FG |
1239 | fn suggest_binding_for_closure_capture_self( |
1240 | &self, | |
1241 | err: &mut Diagnostic, | |
1242 | borrowed_place: Place<'tcx>, | |
1243 | issued_spans: &UseSpans<'tcx>, | |
1244 | ) { | |
1245 | let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; | |
1246 | let hir = self.infcx.tcx.hir(); | |
1247 | ||
1248 | // check whether the borrowed place is capturing `self` by mut reference | |
1249 | let local = borrowed_place.local; | |
1250 | let Some(_) = self | |
1251 | .body | |
1252 | .local_decls | |
1253 | .get(local) | |
1254 | .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return }; | |
1255 | ||
1256 | struct ExpressionFinder<'hir> { | |
1257 | capture_span: Span, | |
1258 | closure_change_spans: Vec<Span>, | |
1259 | closure_arg_span: Option<Span>, | |
1260 | in_closure: bool, | |
1261 | suggest_arg: String, | |
1262 | hir: rustc_middle::hir::map::Map<'hir>, | |
1263 | closure_local_id: Option<hir::HirId>, | |
1264 | closure_call_changes: Vec<(Span, String)>, | |
1265 | } | |
1266 | impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { | |
1267 | fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { | |
1268 | if e.span.contains(self.capture_span) { | |
1269 | if let hir::ExprKind::Closure(&hir::Closure { | |
1270 | movability: None, | |
1271 | body, | |
1272 | fn_arg_span, | |
1273 | fn_decl: hir::FnDecl{ inputs, .. }, | |
1274 | .. | |
1275 | }) = e.kind && | |
1276 | let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) { | |
1277 | self.suggest_arg = "this: &Self".to_string(); | |
1278 | if inputs.len() > 0 { | |
1279 | self.suggest_arg.push_str(", "); | |
1280 | } | |
1281 | self.in_closure = true; | |
1282 | self.closure_arg_span = fn_arg_span; | |
1283 | self.visit_expr(body); | |
1284 | self.in_closure = false; | |
1285 | } | |
1286 | } | |
1287 | if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e { | |
1288 | if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && | |
1289 | seg.ident.name == kw::SelfLower && self.in_closure { | |
1290 | self.closure_change_spans.push(e.span); | |
1291 | } | |
1292 | } | |
1293 | hir::intravisit::walk_expr(self, e); | |
1294 | } | |
1295 | ||
1296 | fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { | |
1297 | if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && | |
1298 | let Some(init) = local.init | |
1299 | { | |
1300 | if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { | |
1301 | movability: None, | |
1302 | .. | |
1303 | }), .. } = init && | |
1304 | init.span.contains(self.capture_span) { | |
1305 | self.closure_local_id = Some(*hir_id); | |
1306 | } | |
1307 | } | |
1308 | hir::intravisit::walk_local(self, local); | |
1309 | } | |
1310 | ||
1311 | fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { | |
1312 | if let hir::StmtKind::Semi(e) = s.kind && | |
1313 | let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind && | |
1314 | let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && | |
1315 | let Res::Local(hir_id) = seg.res && | |
1316 | Some(hir_id) == self.closure_local_id { | |
1317 | let (span, arg_str) = if args.len() > 0 { | |
1318 | (args[0].span.shrink_to_lo(), "self, ".to_string()) | |
1319 | } else { | |
1320 | let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); | |
1321 | (span, "(self)".to_string()) | |
1322 | }; | |
1323 | self.closure_call_changes.push((span, arg_str)); | |
1324 | } | |
1325 | hir::intravisit::walk_stmt(self, s); | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | if let Some(hir::Node::ImplItem( | |
1330 | hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. } | |
1331 | )) = hir.find(self.mir_hir_id()) && | |
1332 | let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { | |
1333 | let mut finder = ExpressionFinder { | |
1334 | capture_span: *capture_kind_span, | |
1335 | closure_change_spans: vec![], | |
1336 | closure_arg_span: None, | |
1337 | in_closure: false, | |
1338 | suggest_arg: String::new(), | |
1339 | closure_local_id: None, | |
1340 | closure_call_changes: vec![], | |
1341 | hir, | |
1342 | }; | |
1343 | finder.visit_expr(expr); | |
1344 | ||
1345 | if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { | |
1346 | return; | |
1347 | } | |
1348 | ||
1349 | let mut sugg = vec![]; | |
1350 | let sm = self.infcx.tcx.sess.source_map(); | |
1351 | ||
1352 | if let Some(span) = finder.closure_arg_span { | |
1353 | sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); | |
1354 | } | |
1355 | for span in finder.closure_change_spans { | |
1356 | sugg.push((span, "this".to_string())); | |
1357 | } | |
1358 | ||
1359 | for (span, suggest) in finder.closure_call_changes { | |
1360 | sugg.push((span, suggest)); | |
1361 | } | |
1362 | ||
1363 | err.multipart_suggestion_verbose( | |
1364 | "try explicitly pass `&Self` into the Closure as an argument", | |
1365 | sugg, | |
1366 | Applicability::MachineApplicable, | |
1367 | ); | |
1368 | } | |
1369 | } | |
1370 | ||
dc9dc135 XL |
1371 | /// Returns the description of the root place for a conflicting borrow and the full |
1372 | /// descriptions of the places that caused the conflict. | |
1373 | /// | |
1374 | /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is | |
1375 | /// attempted while a shared borrow is live, then this function will return: | |
04454e1e FG |
1376 | /// ``` |
1377 | /// ("x", "", "") | |
1378 | /// # ; | |
1379 | /// ``` | |
dc9dc135 XL |
1380 | /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while |
1381 | /// a shared borrow of another field `x.y`, then this function will return: | |
04454e1e FG |
1382 | /// ``` |
1383 | /// ("x", "x.z", "x.y") | |
1384 | /// # ; | |
1385 | /// ``` | |
dc9dc135 XL |
1386 | /// In the more complex union case, where the union is a field of a struct, then if a mutable |
1387 | /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of | |
1388 | /// another field `x.u.y`, then this function will return: | |
04454e1e FG |
1389 | /// ``` |
1390 | /// ("x.u", "x.u.z", "x.u.y") | |
1391 | /// # ; | |
1392 | /// ``` | |
dc9dc135 XL |
1393 | /// This is used when creating error messages like below: |
1394 | /// | |
f9f354fc XL |
1395 | /// ```text |
1396 | /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as | |
1397 | /// mutable (via `a.u.s.b`) [E0502] | |
1398 | /// ``` | |
c295e0f8 | 1399 | pub(crate) fn describe_place_for_conflicting_borrow( |
dc9dc135 | 1400 | &self, |
ba9703b0 XL |
1401 | first_borrowed_place: Place<'tcx>, |
1402 | second_borrowed_place: Place<'tcx>, | |
dc9dc135 XL |
1403 | ) -> (String, String, String, String) { |
1404 | // Define a small closure that we can use to check if the type of a place | |
1405 | // is a union. | |
5869c6ff XL |
1406 | let union_ty = |place_base| { |
1407 | // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`; | |
1408 | // using a type annotation in the closure argument instead leads to a lifetime error. | |
1409 | let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty; | |
dc9dc135 XL |
1410 | ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) |
1411 | }; | |
dc9dc135 XL |
1412 | |
1413 | // Start with an empty tuple, so we can use the functions on `Option` to reduce some | |
1414 | // code duplication (particularly around returning an empty description in the failure | |
1415 | // case). | |
1416 | Some(()) | |
1417 | .filter(|_| { | |
1418 | // If we have a conflicting borrow of the same place, then we don't want to add | |
1419 | // an extraneous "via x.y" to our diagnostics, so filter out this case. | |
1420 | first_borrowed_place != second_borrowed_place | |
1421 | }) | |
1422 | .and_then(|_| { | |
1423 | // We're going to want to traverse the first borrowed place to see if we can find | |
1424 | // field access to a union. If we find that, then we will keep the place of the | |
1425 | // union being accessed and the field that was being accessed so we can check the | |
94222f64 | 1426 | // second borrowed place for the same union and an access to a different field. |
5869c6ff | 1427 | for (place_base, elem) in first_borrowed_place.iter_projections().rev() { |
dc9dc135 | 1428 | match elem { |
5869c6ff XL |
1429 | ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => { |
1430 | return Some((place_base, field)); | |
dfeec247 XL |
1431 | } |
1432 | _ => {} | |
dc9dc135 XL |
1433 | } |
1434 | } | |
1435 | None | |
1436 | }) | |
1437 | .and_then(|(target_base, target_field)| { | |
1438 | // With the place of a union and a field access into it, we traverse the second | |
94222f64 | 1439 | // borrowed place and look for an access to a different field of the same union. |
5869c6ff | 1440 | for (place_base, elem) in second_borrowed_place.iter_projections().rev() { |
dc9dc135 | 1441 | if let ProjectionElem::Field(field, _) = elem { |
5869c6ff XL |
1442 | if let Some(union_ty) = union_ty(place_base) { |
1443 | if field != target_field && place_base == target_base { | |
dc9dc135 | 1444 | return Some(( |
5869c6ff | 1445 | self.describe_any_place(place_base), |
ba9703b0 XL |
1446 | self.describe_any_place(first_borrowed_place.as_ref()), |
1447 | self.describe_any_place(second_borrowed_place.as_ref()), | |
dc9dc135 XL |
1448 | union_ty.to_string(), |
1449 | )); | |
1450 | } | |
1451 | } | |
1452 | } | |
dc9dc135 XL |
1453 | } |
1454 | None | |
1455 | }) | |
1456 | .unwrap_or_else(|| { | |
1457 | // If we didn't find a field access into a union, or both places match, then | |
1458 | // only return the description of the first place. | |
1459 | ( | |
ba9703b0 | 1460 | self.describe_any_place(first_borrowed_place.as_ref()), |
dc9dc135 XL |
1461 | "".to_string(), |
1462 | "".to_string(), | |
1463 | "".to_string(), | |
1464 | ) | |
1465 | }) | |
1466 | } | |
1467 | ||
1468 | /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. | |
1469 | /// | |
1470 | /// This means that some data referenced by `borrow` needs to live | |
1471 | /// past the point where the StorageDeadOrDrop of `place` occurs. | |
1472 | /// This is usually interpreted as meaning that `place` has too | |
1473 | /// short a lifetime. (But sometimes it is more useful to report | |
1474 | /// it as a more direct conflict between the execution of a | |
1475 | /// `Drop::drop` with an aliasing borrow.) | |
f2b60f7d | 1476 | #[instrument(level = "debug", skip(self))] |
c295e0f8 | 1477 | pub(crate) fn report_borrowed_value_does_not_live_long_enough( |
dc9dc135 XL |
1478 | &mut self, |
1479 | location: Location, | |
1480 | borrow: &BorrowData<'tcx>, | |
ba9703b0 | 1481 | place_span: (Place<'tcx>, Span), |
dc9dc135 XL |
1482 | kind: Option<WriteKind>, |
1483 | ) { | |
dc9dc135 | 1484 | let drop_span = place_span.1; |
dfeec247 XL |
1485 | let root_place = |
1486 | self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); | |
dc9dc135 XL |
1487 | |
1488 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
17df50a5 | 1489 | let borrow_span = borrow_spans.var_or_use_path_span(); |
dc9dc135 | 1490 | |
e1599b0c | 1491 | assert!(root_place.projection.is_empty()); |
74b04a01 | 1492 | let proper_span = self.body.local_decls[root_place.local].source_info.span; |
dc9dc135 | 1493 | |
9ffffee4 | 1494 | let root_place_projection = self.infcx.tcx.mk_place_elems(root_place.projection); |
e74abb32 | 1495 | |
dfeec247 | 1496 | if self.access_place_error_reported.contains(&( |
74b04a01 | 1497 | Place { local: root_place.local, projection: root_place_projection }, |
dfeec247 XL |
1498 | borrow_span, |
1499 | )) { | |
dc9dc135 XL |
1500 | debug!( |
1501 | "suppressing access_place error when borrow doesn't live long enough for {:?}", | |
1502 | borrow_span | |
1503 | ); | |
1504 | return; | |
1505 | } | |
1506 | ||
dfeec247 | 1507 | self.access_place_error_reported.insert(( |
74b04a01 | 1508 | Place { local: root_place.local, projection: root_place_projection }, |
dfeec247 XL |
1509 | borrow_span, |
1510 | )); | |
dc9dc135 | 1511 | |
dfeec247 XL |
1512 | let borrowed_local = borrow.borrowed_place.local; |
1513 | if self.body.local_decls[borrowed_local].is_ref_to_thread_local() { | |
1514 | let err = | |
1515 | self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span); | |
5099ac24 | 1516 | self.buffer_error(err); |
dfeec247 XL |
1517 | return; |
1518 | } | |
60c5eb7d | 1519 | |
dc9dc135 | 1520 | if let StorageDeadOrDrop::Destructor(dropped_ty) = |
416331ca | 1521 | self.classify_drop_access_kind(borrow.borrowed_place.as_ref()) |
dc9dc135 XL |
1522 | { |
1523 | // If a borrow of path `B` conflicts with drop of `D` (and | |
1524 | // we're not in the uninteresting case where `B` is a | |
1525 | // prefix of `D`), then report this as a more interesting | |
1526 | // destructor conflict. | |
416331ca | 1527 | if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) { |
dc9dc135 XL |
1528 | self.report_borrow_conflicts_with_destructor( |
1529 | location, borrow, place_span, kind, dropped_ty, | |
1530 | ); | |
1531 | return; | |
1532 | } | |
1533 | } | |
1534 | ||
416331ca | 1535 | let place_desc = self.describe_place(borrow.borrowed_place.as_ref()); |
dc9dc135 XL |
1536 | |
1537 | let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); | |
1538 | let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place); | |
1539 | ||
f2b60f7d FG |
1540 | debug!(?place_desc, ?explanation); |
1541 | ||
dc9dc135 | 1542 | let err = match (place_desc, explanation) { |
dc9dc135 XL |
1543 | // If the outlives constraint comes from inside the closure, |
1544 | // for example: | |
1545 | // | |
1546 | // let x = 0; | |
1547 | // let y = &x; | |
1548 | // Box::new(|| y) as Box<Fn() -> &'static i32> | |
1549 | // | |
1550 | // then just use the normal error. The closure isn't escaping | |
1551 | // and `move` will not help here. | |
9c376795 FG |
1552 | ( |
1553 | Some(name), | |
1554 | BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _), | |
1555 | ) => self.report_escaping_closure_capture( | |
1556 | borrow_spans, | |
1557 | borrow_span, | |
1558 | &RegionName { | |
1559 | name: self.synthesize_region_name(), | |
1560 | source: RegionNameSource::Static, | |
1561 | }, | |
1562 | ConstraintCategory::CallArgument(None), | |
1563 | var_or_use_span, | |
1564 | &format!("`{}`", name), | |
1565 | "block", | |
1566 | ), | |
dc9dc135 | 1567 | ( |
487cf647 | 1568 | Some(name), |
dc9dc135 | 1569 | BorrowExplanation::MustBeValidFor { |
ba9703b0 | 1570 | category: |
a2a8927a | 1571 | category @ (ConstraintCategory::Return(_) |
923072b8 | 1572 | | ConstraintCategory::CallArgument(_) |
ba9703b0 | 1573 | | ConstraintCategory::OpaqueType), |
dc9dc135 XL |
1574 | from_closure: false, |
1575 | ref region_name, | |
1576 | span, | |
1577 | .. | |
1578 | }, | |
ba9703b0 XL |
1579 | ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self |
1580 | .report_escaping_closure_capture( | |
1581 | borrow_spans, | |
1582 | borrow_span, | |
1583 | region_name, | |
1584 | category, | |
e74abb32 | 1585 | span, |
ba9703b0 | 1586 | &format!("`{}`", name), |
9c376795 | 1587 | "function", |
ba9703b0 | 1588 | ), |
dc9dc135 | 1589 | ( |
487cf647 | 1590 | name, |
dc9dc135 XL |
1591 | BorrowExplanation::MustBeValidFor { |
1592 | category: ConstraintCategory::Assignment, | |
1593 | from_closure: false, | |
dfeec247 XL |
1594 | region_name: |
1595 | RegionName { | |
064997fb | 1596 | source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), |
dfeec247 XL |
1597 | .. |
1598 | }, | |
dc9dc135 XL |
1599 | span, |
1600 | .. | |
1601 | }, | |
487cf647 | 1602 | ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span), |
dc9dc135 XL |
1603 | (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( |
1604 | location, | |
1605 | &name, | |
1606 | &borrow, | |
1607 | drop_span, | |
1608 | borrow_spans, | |
1609 | explanation, | |
1610 | ), | |
1611 | (None, explanation) => self.report_temporary_value_does_not_live_long_enough( | |
1612 | location, | |
1613 | &borrow, | |
1614 | drop_span, | |
1615 | borrow_spans, | |
1616 | proper_span, | |
1617 | explanation, | |
1618 | ), | |
1619 | }; | |
1620 | ||
5099ac24 | 1621 | self.buffer_error(err); |
dc9dc135 XL |
1622 | } |
1623 | ||
1624 | fn report_local_value_does_not_live_long_enough( | |
1625 | &mut self, | |
1626 | location: Location, | |
1627 | name: &str, | |
1628 | borrow: &BorrowData<'tcx>, | |
1629 | drop_span: Span, | |
1b1a35ee | 1630 | borrow_spans: UseSpans<'tcx>, |
923072b8 | 1631 | explanation: BorrowExplanation<'tcx>, |
5e7ed085 | 1632 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 XL |
1633 | debug!( |
1634 | "report_local_value_does_not_live_long_enough(\ | |
1635 | {:?}, {:?}, {:?}, {:?}, {:?}\ | |
1636 | )", | |
1637 | location, name, borrow, drop_span, borrow_spans | |
1638 | ); | |
1639 | ||
17df50a5 | 1640 | let borrow_span = borrow_spans.var_or_use_path_span(); |
dc9dc135 XL |
1641 | if let BorrowExplanation::MustBeValidFor { |
1642 | category, | |
1643 | span, | |
1644 | ref opt_place_desc, | |
1645 | from_closure: false, | |
1646 | .. | |
dfeec247 XL |
1647 | } = explanation |
1648 | { | |
dc9dc135 XL |
1649 | if let Some(diag) = self.try_report_cannot_return_reference_to_local( |
1650 | borrow, | |
1651 | borrow_span, | |
1652 | span, | |
1653 | category, | |
1654 | opt_place_desc.as_ref(), | |
1655 | ) { | |
1656 | return diag; | |
1657 | } | |
1658 | } | |
1659 | ||
dfeec247 | 1660 | let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name)); |
dc9dc135 XL |
1661 | |
1662 | if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { | |
1663 | let region_name = annotation.emit(self, &mut err); | |
1664 | ||
1665 | err.span_label( | |
1666 | borrow_span, | |
1667 | format!("`{}` would have to be valid for `{}`...", name, region_name), | |
1668 | ); | |
1669 | ||
f9f354fc XL |
1670 | err.span_label( |
1671 | drop_span, | |
1672 | format!( | |
1673 | "...but `{}` will be dropped here, when the {} returns", | |
1674 | name, | |
1675 | self.infcx | |
1676 | .tcx | |
9ffffee4 | 1677 | .opt_item_name(self.mir_def_id().to_def_id()) |
f9f354fc XL |
1678 | .map(|name| format!("function `{}`", name)) |
1679 | .unwrap_or_else(|| { | |
9ffffee4 FG |
1680 | match &self.infcx.tcx.def_kind(self.mir_def_id()) { |
1681 | DefKind::Closure => "enclosing closure", | |
1682 | DefKind::Generator => "enclosing generator", | |
f9f354fc XL |
1683 | kind => bug!("expected closure or generator, found {:?}", kind), |
1684 | } | |
1685 | .to_string() | |
1686 | }) | |
1687 | ), | |
1688 | ); | |
dc9dc135 | 1689 | |
f9f354fc XL |
1690 | err.note( |
1691 | "functions cannot return a borrow to data owned within the function's scope, \ | |
1692 | functions can only return borrows to data passed as arguments", | |
1693 | ); | |
1694 | err.note( | |
1695 | "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\ | |
1696 | references-and-borrowing.html#dangling-references>", | |
1697 | ); | |
dc9dc135 XL |
1698 | |
1699 | if let BorrowExplanation::MustBeValidFor { .. } = explanation { | |
1700 | } else { | |
1701 | explanation.add_explanation_to_diagnostic( | |
1702 | self.infcx.tcx, | |
60c5eb7d XL |
1703 | &self.body, |
1704 | &self.local_names, | |
dc9dc135 XL |
1705 | &mut err, |
1706 | "", | |
1707 | None, | |
136023e0 | 1708 | None, |
dc9dc135 XL |
1709 | ); |
1710 | } | |
1711 | } else { | |
1712 | err.span_label(borrow_span, "borrowed value does not live long enough"); | |
dfeec247 | 1713 | err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name)); |
dc9dc135 | 1714 | |
dfeec247 | 1715 | let within = if borrow_spans.for_generator() { " by generator" } else { "" }; |
dc9dc135 | 1716 | |
dfeec247 | 1717 | borrow_spans.args_span_label(&mut err, format!("value captured here{}", within)); |
dc9dc135 XL |
1718 | |
1719 | explanation.add_explanation_to_diagnostic( | |
dfeec247 XL |
1720 | self.infcx.tcx, |
1721 | &self.body, | |
1722 | &self.local_names, | |
1723 | &mut err, | |
1724 | "", | |
9ffffee4 | 1725 | Some(borrow_span), |
136023e0 | 1726 | None, |
dfeec247 | 1727 | ); |
dc9dc135 XL |
1728 | } |
1729 | ||
1730 | err | |
1731 | } | |
1732 | ||
1733 | fn report_borrow_conflicts_with_destructor( | |
1734 | &mut self, | |
1735 | location: Location, | |
1736 | borrow: &BorrowData<'tcx>, | |
ba9703b0 | 1737 | (place, drop_span): (Place<'tcx>, Span), |
dc9dc135 XL |
1738 | kind: Option<WriteKind>, |
1739 | dropped_ty: Ty<'tcx>, | |
1740 | ) { | |
1741 | debug!( | |
1742 | "report_borrow_conflicts_with_destructor(\ | |
1743 | {:?}, {:?}, ({:?}, {:?}), {:?}\ | |
1744 | )", | |
1745 | location, borrow, place, drop_span, kind, | |
1746 | ); | |
1747 | ||
1748 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
1749 | let borrow_span = borrow_spans.var_or_use(); | |
1750 | ||
416331ca | 1751 | let mut err = self.cannot_borrow_across_destructor(borrow_span); |
dc9dc135 | 1752 | |
416331ca | 1753 | let what_was_dropped = match self.describe_place(place.as_ref()) { |
60c5eb7d | 1754 | Some(name) => format!("`{}`", name), |
dc9dc135 XL |
1755 | None => String::from("temporary value"), |
1756 | }; | |
1757 | ||
416331ca | 1758 | let label = match self.describe_place(borrow.borrowed_place.as_ref()) { |
dc9dc135 XL |
1759 | Some(borrowed) => format!( |
1760 | "here, drop of {D} needs exclusive access to `{B}`, \ | |
1761 | because the type `{T}` implements the `Drop` trait", | |
1762 | D = what_was_dropped, | |
1763 | T = dropped_ty, | |
1764 | B = borrowed | |
1765 | ), | |
1766 | None => format!( | |
1767 | "here is drop of {D}; whose type `{T}` implements the `Drop` trait", | |
1768 | D = what_was_dropped, | |
1769 | T = dropped_ty | |
1770 | ), | |
1771 | }; | |
1772 | err.span_label(drop_span, label); | |
1773 | ||
1774 | // Only give this note and suggestion if they could be relevant. | |
1775 | let explanation = | |
1776 | self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place))); | |
1777 | match explanation { | |
1778 | BorrowExplanation::UsedLater { .. } | |
1779 | | BorrowExplanation::UsedLaterWhenDropped { .. } => { | |
1780 | err.note("consider using a `let` binding to create a longer lived value"); | |
1781 | } | |
1782 | _ => {} | |
1783 | } | |
1784 | ||
60c5eb7d XL |
1785 | explanation.add_explanation_to_diagnostic( |
1786 | self.infcx.tcx, | |
1787 | &self.body, | |
1788 | &self.local_names, | |
1789 | &mut err, | |
1790 | "", | |
1791 | None, | |
136023e0 | 1792 | None, |
60c5eb7d | 1793 | ); |
dc9dc135 | 1794 | |
5099ac24 | 1795 | self.buffer_error(err); |
dc9dc135 XL |
1796 | } |
1797 | ||
1798 | fn report_thread_local_value_does_not_live_long_enough( | |
1799 | &mut self, | |
1800 | drop_span: Span, | |
1801 | borrow_span: Span, | |
5e7ed085 | 1802 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 XL |
1803 | debug!( |
1804 | "report_thread_local_value_does_not_live_long_enough(\ | |
1805 | {:?}, {:?}\ | |
1806 | )", | |
1807 | drop_span, borrow_span | |
1808 | ); | |
1809 | ||
416331ca | 1810 | let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span); |
dc9dc135 XL |
1811 | |
1812 | err.span_label( | |
1813 | borrow_span, | |
1814 | "thread-local variables cannot be borrowed beyond the end of the function", | |
1815 | ); | |
1816 | err.span_label(drop_span, "end of enclosing function is here"); | |
1817 | ||
1818 | err | |
1819 | } | |
1820 | ||
f2b60f7d | 1821 | #[instrument(level = "debug", skip(self))] |
dc9dc135 XL |
1822 | fn report_temporary_value_does_not_live_long_enough( |
1823 | &mut self, | |
1824 | location: Location, | |
1825 | borrow: &BorrowData<'tcx>, | |
1826 | drop_span: Span, | |
1b1a35ee | 1827 | borrow_spans: UseSpans<'tcx>, |
dc9dc135 | 1828 | proper_span: Span, |
923072b8 | 1829 | explanation: BorrowExplanation<'tcx>, |
5e7ed085 | 1830 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dfeec247 XL |
1831 | if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = |
1832 | explanation | |
1833 | { | |
dc9dc135 XL |
1834 | if let Some(diag) = self.try_report_cannot_return_reference_to_local( |
1835 | borrow, | |
1836 | proper_span, | |
1837 | span, | |
1838 | category, | |
1839 | None, | |
1840 | ) { | |
1841 | return diag; | |
1842 | } | |
1843 | } | |
1844 | ||
416331ca | 1845 | let mut err = self.temporary_value_borrowed_for_too_long(proper_span); |
487cf647 | 1846 | err.span_label(proper_span, "creates a temporary value which is freed while still in use"); |
dfeec247 | 1847 | err.span_label(drop_span, "temporary value is freed at the end of this statement"); |
dc9dc135 XL |
1848 | |
1849 | match explanation { | |
1850 | BorrowExplanation::UsedLater(..) | |
1851 | | BorrowExplanation::UsedLaterInLoop(..) | |
1852 | | BorrowExplanation::UsedLaterWhenDropped { .. } => { | |
1853 | // Only give this note and suggestion if it could be relevant. | |
064997fb FG |
1854 | let sm = self.infcx.tcx.sess.source_map(); |
1855 | let mut suggested = false; | |
1856 | let msg = "consider using a `let` binding to create a longer lived value"; | |
1857 | ||
1858 | /// We check that there's a single level of block nesting to ensure always correct | |
1859 | /// suggestions. If we don't, then we only provide a free-form message to avoid | |
9c376795 | 1860 | /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`. |
064997fb FG |
1861 | /// We could expand the analysis to suggest hoising all of the relevant parts of |
1862 | /// the users' code to make the code compile, but that could be too much. | |
1863 | struct NestedStatementVisitor { | |
1864 | span: Span, | |
1865 | current: usize, | |
1866 | found: usize, | |
1867 | } | |
1868 | ||
1869 | impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { | |
1870 | fn visit_block(&mut self, block: &hir::Block<'tcx>) { | |
1871 | self.current += 1; | |
1872 | walk_block(self, block); | |
1873 | self.current -= 1; | |
1874 | } | |
1875 | fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { | |
1876 | if self.span == expr.span { | |
1877 | self.found = self.current; | |
1878 | } | |
1879 | walk_expr(self, expr); | |
1880 | } | |
1881 | } | |
1882 | let source_info = self.body.source_info(location); | |
1883 | if let Some(scope) = self.body.source_scopes.get(source_info.scope) | |
1884 | && let ClearCrossCrate::Set(scope_data) = &scope.local_data | |
1885 | && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) | |
1886 | && let Some(id) = node.body_id() | |
1887 | && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind | |
1888 | { | |
1889 | for stmt in block.stmts { | |
1890 | let mut visitor = NestedStatementVisitor { | |
1891 | span: proper_span, | |
1892 | current: 0, | |
1893 | found: 0, | |
1894 | }; | |
1895 | visitor.visit_stmt(stmt); | |
1896 | if visitor.found == 0 | |
1897 | && stmt.span.contains(proper_span) | |
1898 | && let Some(p) = sm.span_to_margin(stmt.span) | |
1899 | && let Ok(s) = sm.span_to_snippet(proper_span) | |
1900 | { | |
1901 | let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); | |
1902 | err.multipart_suggestion_verbose( | |
1903 | msg, | |
1904 | vec![ | |
1905 | (stmt.span.shrink_to_lo(), addition), | |
1906 | (proper_span, "binding".to_string()), | |
1907 | ], | |
1908 | Applicability::MaybeIncorrect, | |
1909 | ); | |
1910 | suggested = true; | |
1911 | break; | |
1912 | } | |
1913 | } | |
1914 | } | |
1915 | if !suggested { | |
1916 | err.note(msg); | |
1917 | } | |
dc9dc135 XL |
1918 | } |
1919 | _ => {} | |
1920 | } | |
60c5eb7d XL |
1921 | explanation.add_explanation_to_diagnostic( |
1922 | self.infcx.tcx, | |
1923 | &self.body, | |
1924 | &self.local_names, | |
1925 | &mut err, | |
1926 | "", | |
1927 | None, | |
136023e0 | 1928 | None, |
60c5eb7d | 1929 | ); |
dc9dc135 | 1930 | |
dfeec247 | 1931 | let within = if borrow_spans.for_generator() { " by generator" } else { "" }; |
dc9dc135 | 1932 | |
dfeec247 | 1933 | borrow_spans.args_span_label(&mut err, format!("value captured here{}", within)); |
dc9dc135 XL |
1934 | |
1935 | err | |
1936 | } | |
1937 | ||
1938 | fn try_report_cannot_return_reference_to_local( | |
1939 | &self, | |
1940 | borrow: &BorrowData<'tcx>, | |
1941 | borrow_span: Span, | |
1942 | return_span: Span, | |
923072b8 | 1943 | category: ConstraintCategory<'tcx>, |
dc9dc135 | 1944 | opt_place_desc: Option<&String>, |
5e7ed085 | 1945 | ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> { |
dc9dc135 | 1946 | let return_kind = match category { |
f035d41b | 1947 | ConstraintCategory::Return(_) => "return", |
dc9dc135 XL |
1948 | ConstraintCategory::Yield => "yield", |
1949 | _ => return None, | |
1950 | }; | |
1951 | ||
1952 | // FIXME use a better heuristic than Spans | |
dfeec247 XL |
1953 | let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span { |
1954 | "reference to" | |
1955 | } else { | |
1956 | "value referencing" | |
1957 | }; | |
dc9dc135 XL |
1958 | |
1959 | let (place_desc, note) = if let Some(place_desc) = opt_place_desc { | |
e74abb32 XL |
1960 | let local_kind = if let Some(local) = borrow.borrowed_place.as_local() { |
1961 | match self.body.local_kind(local) { | |
dfeec247 XL |
1962 | LocalKind::ReturnPointer | LocalKind::Temp => { |
1963 | bug!("temporary or return pointer with a name") | |
1964 | } | |
e74abb32 | 1965 | LocalKind::Var => "local variable ", |
17df50a5 XL |
1966 | LocalKind::Arg |
1967 | if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL => | |
1968 | { | |
e74abb32 XL |
1969 | "variable captured by `move` " |
1970 | } | |
dfeec247 | 1971 | LocalKind::Arg => "function parameter ", |
dc9dc135 | 1972 | } |
e74abb32 XL |
1973 | } else { |
1974 | "local data " | |
dc9dc135 XL |
1975 | }; |
1976 | ( | |
1977 | format!("{}`{}`", local_kind, place_desc), | |
1978 | format!("`{}` is borrowed here", place_desc), | |
1979 | ) | |
1980 | } else { | |
dfeec247 XL |
1981 | let root_place = |
1982 | self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); | |
1983 | let local = root_place.local; | |
74b04a01 | 1984 | match self.body.local_kind(local) { |
dfeec247 XL |
1985 | LocalKind::ReturnPointer | LocalKind::Temp => { |
1986 | ("temporary value".to_string(), "temporary value created here".to_string()) | |
1987 | } | |
dc9dc135 XL |
1988 | LocalKind::Arg => ( |
1989 | "function parameter".to_string(), | |
1990 | "function parameter borrowed here".to_string(), | |
1991 | ), | |
dfeec247 XL |
1992 | LocalKind::Var => { |
1993 | ("local binding".to_string(), "local binding introduced here".to_string()) | |
1994 | } | |
dc9dc135 XL |
1995 | } |
1996 | }; | |
1997 | ||
416331ca | 1998 | let mut err = self.cannot_return_reference_to_local( |
dc9dc135 XL |
1999 | return_span, |
2000 | return_kind, | |
2001 | reference_desc, | |
2002 | &place_desc, | |
dc9dc135 XL |
2003 | ); |
2004 | ||
2005 | if return_span != borrow_span { | |
2006 | err.span_label(borrow_span, note); | |
6a06907d XL |
2007 | |
2008 | let tcx = self.infcx.tcx; | |
6a06907d XL |
2009 | |
2010 | let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; | |
2011 | let return_ty = tcx.erase_regions(return_ty); | |
2012 | ||
2013 | // to avoid panics | |
064997fb FG |
2014 | if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) |
2015 | && self | |
136023e0 | 2016 | .infcx |
487cf647 | 2017 | .type_implements_trait(iter_trait, [return_ty], self.param_env) |
136023e0 | 2018 | .must_apply_modulo_regions() |
064997fb FG |
2019 | { |
2020 | err.span_suggestion_hidden( | |
2021 | return_span.shrink_to_hi(), | |
2022 | "use `.collect()` to allocate the iterator", | |
2023 | ".collect::<Vec<_>>()", | |
2024 | Applicability::MaybeIncorrect, | |
2025 | ); | |
6a06907d | 2026 | } |
dc9dc135 XL |
2027 | } |
2028 | ||
2029 | Some(err) | |
2030 | } | |
2031 | ||
9c376795 | 2032 | #[instrument(level = "debug", skip(self))] |
dc9dc135 XL |
2033 | fn report_escaping_closure_capture( |
2034 | &mut self, | |
1b1a35ee | 2035 | use_span: UseSpans<'tcx>, |
dc9dc135 XL |
2036 | var_span: Span, |
2037 | fr_name: &RegionName, | |
923072b8 | 2038 | category: ConstraintCategory<'tcx>, |
dc9dc135 XL |
2039 | constraint_span: Span, |
2040 | captured_var: &str, | |
9c376795 | 2041 | scope: &str, |
5e7ed085 | 2042 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 | 2043 | let tcx = self.infcx.tcx; |
e74abb32 | 2044 | let args_span = use_span.args_or_use(); |
dc9dc135 | 2045 | |
94222f64 XL |
2046 | let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { |
2047 | Ok(string) => { | |
e1599b0c | 2048 | if string.starts_with("async ") { |
94222f64 | 2049 | let pos = args_span.lo() + BytePos(6); |
923072b8 | 2050 | (args_span.with_lo(pos).with_hi(pos), "move ") |
e1599b0c | 2051 | } else if string.starts_with("async|") { |
94222f64 | 2052 | let pos = args_span.lo() + BytePos(5); |
923072b8 | 2053 | (args_span.with_lo(pos).with_hi(pos), " move") |
e1599b0c | 2054 | } else { |
923072b8 | 2055 | (args_span.shrink_to_lo(), "move ") |
94222f64 | 2056 | } |
dfeec247 | 2057 | } |
923072b8 | 2058 | Err(_) => (args_span, "move |<args>| <body>"), |
dc9dc135 | 2059 | }; |
e74abb32 XL |
2060 | let kind = match use_span.generator_kind() { |
2061 | Some(generator_kind) => match generator_kind { | |
2062 | GeneratorKind::Async(async_kind) => match async_kind { | |
2063 | AsyncGeneratorKind::Block => "async block", | |
2064 | AsyncGeneratorKind::Closure => "async closure", | |
74b04a01 | 2065 | _ => bug!("async block/closure expected, but async function found."), |
e74abb32 XL |
2066 | }, |
2067 | GeneratorKind::Gen => "generator", | |
dfeec247 | 2068 | }, |
e74abb32 XL |
2069 | None => "closure", |
2070 | }; | |
ba9703b0 | 2071 | |
9c376795 FG |
2072 | let mut err = self.cannot_capture_in_long_lived_closure( |
2073 | args_span, | |
2074 | kind, | |
2075 | captured_var, | |
2076 | var_span, | |
2077 | scope, | |
2078 | ); | |
94222f64 XL |
2079 | err.span_suggestion_verbose( |
2080 | sugg_span, | |
e74abb32 XL |
2081 | &format!( |
2082 | "to force the {} to take ownership of {} (and any \ | |
2083 | other referenced variables), use the `move` keyword", | |
dfeec247 | 2084 | kind, captured_var |
e74abb32 | 2085 | ), |
dc9dc135 XL |
2086 | suggestion, |
2087 | Applicability::MachineApplicable, | |
2088 | ); | |
2089 | ||
5869c6ff | 2090 | match category { |
f035d41b | 2091 | ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => { |
5869c6ff XL |
2092 | let msg = format!("{} is returned here", kind); |
2093 | err.span_note(constraint_span, &msg); | |
ba9703b0 | 2094 | } |
923072b8 | 2095 | ConstraintCategory::CallArgument(_) => { |
dc9dc135 | 2096 | fr_name.highlight_region_name(&mut err); |
5869c6ff XL |
2097 | if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { |
2098 | err.note( | |
2099 | "async blocks are not executed immediately and must either take a \ | |
9c376795 | 2100 | reference or ownership of outside variables they use", |
5869c6ff XL |
2101 | ); |
2102 | } else { | |
9c376795 | 2103 | let msg = format!("{scope} requires argument type to outlive `{fr_name}`"); |
5869c6ff XL |
2104 | err.span_note(constraint_span, &msg); |
2105 | } | |
dc9dc135 | 2106 | } |
dfeec247 XL |
2107 | _ => bug!( |
2108 | "report_escaping_closure_capture called with unexpected constraint \ | |
74b04a01 | 2109 | category: `{:?}`", |
dfeec247 XL |
2110 | category |
2111 | ), | |
5869c6ff XL |
2112 | } |
2113 | ||
dc9dc135 XL |
2114 | err |
2115 | } | |
2116 | ||
2117 | fn report_escaping_data( | |
2118 | &mut self, | |
2119 | borrow_span: Span, | |
2120 | name: &Option<String>, | |
2121 | upvar_span: Span, | |
064997fb | 2122 | upvar_name: Symbol, |
dc9dc135 | 2123 | escape_span: Span, |
5e7ed085 | 2124 | ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { |
dc9dc135 XL |
2125 | let tcx = self.infcx.tcx; |
2126 | ||
9ffffee4 | 2127 | let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id()); |
dc9dc135 | 2128 | |
dfeec247 XL |
2129 | let mut err = |
2130 | borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from); | |
dc9dc135 XL |
2131 | |
2132 | err.span_label( | |
2133 | upvar_span, | |
74b04a01 | 2134 | format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from), |
dc9dc135 XL |
2135 | ); |
2136 | ||
dfeec247 | 2137 | err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from)); |
dc9dc135 XL |
2138 | |
2139 | if let Some(name) = name { | |
2140 | err.span_label( | |
2141 | escape_span, | |
2142 | format!("reference to `{}` escapes the {} body here", name, escapes_from), | |
2143 | ); | |
2144 | } else { | |
2145 | err.span_label( | |
2146 | escape_span, | |
2147 | format!("reference escapes the {} body here", escapes_from), | |
2148 | ); | |
2149 | } | |
2150 | ||
2151 | err | |
2152 | } | |
2153 | ||
136023e0 XL |
2154 | fn get_moved_indexes( |
2155 | &mut self, | |
2156 | location: Location, | |
2157 | mpi: MovePathIndex, | |
2158 | ) -> (Vec<MoveSite>, Vec<Location>) { | |
923072b8 FG |
2159 | fn predecessor_locations<'tcx, 'a>( |
2160 | body: &'a mir::Body<'tcx>, | |
f9f354fc | 2161 | location: Location, |
923072b8 | 2162 | ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a { |
f9f354fc | 2163 | if location.statement_index == 0 { |
064997fb | 2164 | let predecessors = body.basic_blocks.predecessors()[location.block].to_vec(); |
f9f354fc XL |
2165 | Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb))) |
2166 | } else { | |
2167 | Either::Right(std::iter::once(Location { | |
2168 | statement_index: location.statement_index - 1, | |
2169 | ..location | |
2170 | })) | |
2171 | } | |
2172 | } | |
2173 | ||
c295e0f8 XL |
2174 | let mut mpis = vec![mpi]; |
2175 | let move_paths = &self.move_data.move_paths; | |
2176 | mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); | |
2177 | ||
dc9dc135 | 2178 | let mut stack = Vec::new(); |
c295e0f8 XL |
2179 | let mut back_edge_stack = Vec::new(); |
2180 | ||
2181 | predecessor_locations(self.body, location).for_each(|predecessor| { | |
9c376795 | 2182 | if location.dominates(predecessor, self.dominators()) { |
c295e0f8 XL |
2183 | back_edge_stack.push(predecessor) |
2184 | } else { | |
2185 | stack.push(predecessor); | |
2186 | } | |
2187 | }); | |
2188 | ||
2189 | let mut reached_start = false; | |
2190 | ||
2191 | /* Check if the mpi is initialized as an argument */ | |
2192 | let mut is_argument = false; | |
2193 | for arg in self.body.args_iter() { | |
2194 | let path = self.move_data.rev_lookup.find_local(arg); | |
2195 | if mpis.contains(&path) { | |
2196 | is_argument = true; | |
2197 | } | |
2198 | } | |
dc9dc135 XL |
2199 | |
2200 | let mut visited = FxHashSet::default(); | |
136023e0 XL |
2201 | let mut move_locations = FxHashSet::default(); |
2202 | let mut reinits = vec![]; | |
dc9dc135 XL |
2203 | let mut result = vec![]; |
2204 | ||
c295e0f8 | 2205 | let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| { |
dc9dc135 XL |
2206 | debug!( |
2207 | "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", | |
2208 | location, is_back_edge | |
2209 | ); | |
2210 | ||
2211 | if !visited.insert(location) { | |
c295e0f8 | 2212 | return true; |
dc9dc135 XL |
2213 | } |
2214 | ||
2215 | // check for moves | |
dfeec247 XL |
2216 | let stmt_kind = |
2217 | self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind); | |
dc9dc135 XL |
2218 | if let Some(StatementKind::StorageDead(..)) = stmt_kind { |
2219 | // this analysis only tries to find moves explicitly | |
2220 | // written by the user, so we ignore the move-outs | |
2221 | // created by `StorageDead` and at the beginning | |
2222 | // of a function. | |
2223 | } else { | |
2224 | // If we are found a use of a.b.c which was in error, then we want to look for | |
2225 | // moves not only of a.b.c but also a.b and a. | |
2226 | // | |
2227 | // Note that the moves data already includes "parent" paths, so we don't have to | |
2228 | // worry about the other case: that is, if there is a move of a.b.c, it is already | |
2229 | // marked as a move of a.b and a as well, so we will generate the correct errors | |
2230 | // there. | |
dc9dc135 XL |
2231 | for moi in &self.move_data.loc_map[location] { |
2232 | debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); | |
1b1a35ee XL |
2233 | let path = self.move_data.moves[*moi].path; |
2234 | if mpis.contains(&path) { | |
2235 | debug!( | |
2236 | "report_use_of_moved_or_uninitialized: found {:?}", | |
2237 | move_paths[path].place | |
2238 | ); | |
dfeec247 | 2239 | result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge }); |
136023e0 | 2240 | move_locations.insert(location); |
dc9dc135 XL |
2241 | |
2242 | // Strictly speaking, we could continue our DFS here. There may be | |
2243 | // other moves that can reach the point of error. But it is kind of | |
2244 | // confusing to highlight them. | |
2245 | // | |
2246 | // Example: | |
2247 | // | |
2248 | // ``` | |
2249 | // let a = vec![]; | |
2250 | // let b = a; | |
2251 | // let c = a; | |
2252 | // drop(a); // <-- current point of error | |
2253 | // ``` | |
2254 | // | |
2255 | // Because we stop the DFS here, we only highlight `let c = a`, | |
2256 | // and not `let b = a`. We will of course also report an error at | |
2257 | // `let c = a` which highlights `let b = a` as the move. | |
c295e0f8 | 2258 | return true; |
dc9dc135 XL |
2259 | } |
2260 | } | |
2261 | } | |
2262 | ||
2263 | // check for inits | |
2264 | let mut any_match = false; | |
c295e0f8 XL |
2265 | for ii in &self.move_data.init_loc_map[location] { |
2266 | let init = self.move_data.inits[*ii]; | |
2267 | match init.kind { | |
2268 | InitKind::Deep | InitKind::NonPanicPathOnly => { | |
2269 | if mpis.contains(&init.path) { | |
2270 | any_match = true; | |
2271 | } | |
dc9dc135 | 2272 | } |
c295e0f8 XL |
2273 | InitKind::Shallow => { |
2274 | if mpi == init.path { | |
2275 | any_match = true; | |
2276 | } | |
2277 | } | |
2278 | } | |
2279 | } | |
dc9dc135 | 2280 | if any_match { |
136023e0 | 2281 | reinits.push(location); |
c295e0f8 | 2282 | return true; |
dc9dc135 | 2283 | } |
c295e0f8 XL |
2284 | return false; |
2285 | }; | |
dc9dc135 | 2286 | |
c295e0f8 XL |
2287 | while let Some(location) = stack.pop() { |
2288 | if dfs_iter(&mut result, location, false) { | |
2289 | continue; | |
2290 | } | |
2291 | ||
2292 | let mut has_predecessor = false; | |
2293 | predecessor_locations(self.body, location).for_each(|predecessor| { | |
9c376795 | 2294 | if location.dominates(predecessor, self.dominators()) { |
c295e0f8 XL |
2295 | back_edge_stack.push(predecessor) |
2296 | } else { | |
2297 | stack.push(predecessor); | |
2298 | } | |
2299 | has_predecessor = true; | |
2300 | }); | |
2301 | ||
2302 | if !has_predecessor { | |
2303 | reached_start = true; | |
2304 | } | |
2305 | } | |
2306 | if (is_argument || !reached_start) && result.is_empty() { | |
2307 | /* Process back edges (moves in future loop iterations) only if | |
2308 | the move path is definitely initialized upon loop entry, | |
2309 | to avoid spurious "in previous iteration" errors. | |
2310 | During DFS, if there's a path from the error back to the start | |
2311 | of the function with no intervening init or move, then the | |
2312 | move path may be uninitialized at loop entry. | |
2313 | */ | |
2314 | while let Some(location) = back_edge_stack.pop() { | |
2315 | if dfs_iter(&mut result, location, true) { | |
2316 | continue; | |
2317 | } | |
2318 | ||
2319 | predecessor_locations(self.body, location) | |
2320 | .for_each(|predecessor| back_edge_stack.push(predecessor)); | |
2321 | } | |
dc9dc135 XL |
2322 | } |
2323 | ||
136023e0 XL |
2324 | // Check if we can reach these reinits from a move location. |
2325 | let reinits_reachable = reinits | |
2326 | .into_iter() | |
2327 | .filter(|reinit| { | |
2328 | let mut visited = FxHashSet::default(); | |
2329 | let mut stack = vec![*reinit]; | |
2330 | while let Some(location) = stack.pop() { | |
2331 | if !visited.insert(location) { | |
2332 | continue; | |
2333 | } | |
2334 | if move_locations.contains(&location) { | |
2335 | return true; | |
2336 | } | |
2337 | stack.extend(predecessor_locations(self.body, location)); | |
2338 | } | |
2339 | false | |
2340 | }) | |
2341 | .collect::<Vec<Location>>(); | |
2342 | (result, reinits_reachable) | |
dc9dc135 XL |
2343 | } |
2344 | ||
c295e0f8 | 2345 | pub(crate) fn report_illegal_mutation_of_borrowed( |
dc9dc135 XL |
2346 | &mut self, |
2347 | location: Location, | |
ba9703b0 | 2348 | (place, span): (Place<'tcx>, Span), |
dc9dc135 XL |
2349 | loan: &BorrowData<'tcx>, |
2350 | ) { | |
2351 | let loan_spans = self.retrieve_borrow_spans(loan); | |
2352 | let loan_span = loan_spans.args_or_use(); | |
2353 | ||
ba9703b0 | 2354 | let descr_place = self.describe_any_place(place.as_ref()); |
dc9dc135 | 2355 | if loan.kind == BorrowKind::Shallow { |
ba9703b0 | 2356 | if let Some(section) = self.classify_immutable_section(loan.assigned_place) { |
60c5eb7d XL |
2357 | let mut err = self.cannot_mutate_in_immutable_section( |
2358 | span, | |
2359 | loan_span, | |
ba9703b0 | 2360 | &descr_place, |
60c5eb7d XL |
2361 | section, |
2362 | "assign", | |
2363 | ); | |
2364 | loan_spans.var_span_label( | |
2365 | &mut err, | |
2366 | format!("borrow occurs due to use{}", loan_spans.describe()), | |
17df50a5 | 2367 | loan.kind.describe_mutability(), |
60c5eb7d | 2368 | ); |
dc9dc135 | 2369 | |
5099ac24 | 2370 | self.buffer_error(err); |
dc9dc135 | 2371 | |
60c5eb7d XL |
2372 | return; |
2373 | } | |
dc9dc135 XL |
2374 | } |
2375 | ||
ba9703b0 | 2376 | let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); |
dc9dc135 | 2377 | |
17df50a5 XL |
2378 | loan_spans.var_span_label( |
2379 | &mut err, | |
2380 | format!("borrow occurs due to use{}", loan_spans.describe()), | |
2381 | loan.kind.describe_mutability(), | |
2382 | ); | |
dfeec247 XL |
2383 | |
2384 | self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic( | |
2385 | self.infcx.tcx, | |
2386 | &self.body, | |
2387 | &self.local_names, | |
dc9dc135 | 2388 | &mut err, |
dfeec247 XL |
2389 | "", |
2390 | None, | |
136023e0 | 2391 | None, |
dc9dc135 XL |
2392 | ); |
2393 | ||
6a06907d XL |
2394 | self.explain_deref_coercion(loan, &mut err); |
2395 | ||
5099ac24 | 2396 | self.buffer_error(err); |
dc9dc135 XL |
2397 | } |
2398 | ||
5e7ed085 | 2399 | fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) { |
6a06907d XL |
2400 | let tcx = self.infcx.tcx; |
2401 | if let ( | |
2402 | Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }), | |
2403 | Some((method_did, method_substs)), | |
2404 | ) = ( | |
2405 | &self.body[loan.reserve_location.block].terminator, | |
c295e0f8 | 2406 | rustc_const_eval::util::find_self_call( |
6a06907d XL |
2407 | tcx, |
2408 | self.body, | |
2409 | loan.assigned_place.local, | |
2410 | loan.reserve_location.block, | |
2411 | ), | |
2412 | ) { | |
2413 | if tcx.is_diagnostic_item(sym::deref_method, method_did) { | |
2414 | let deref_target = | |
2415 | tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { | |
2416 | Instance::resolve(tcx, self.param_env, deref_target, method_substs) | |
2417 | .transpose() | |
2418 | }); | |
2419 | if let Some(Ok(instance)) = deref_target { | |
2420 | let deref_target_ty = instance.ty(tcx, self.param_env); | |
2421 | err.note(&format!( | |
2422 | "borrow occurs due to deref coercion to `{}`", | |
2423 | deref_target_ty | |
2424 | )); | |
2425 | err.span_note(tcx.def_span(instance.def_id()), "deref defined here"); | |
2426 | } | |
2427 | } | |
2428 | } | |
2429 | } | |
2430 | ||
dc9dc135 XL |
2431 | /// Reports an illegal reassignment; for example, an assignment to |
2432 | /// (part of) a non-`mut` local that occurs potentially after that | |
2433 | /// local has already been initialized. `place` is the path being | |
2434 | /// assigned; `err_place` is a place providing a reason why | |
2435 | /// `place` is not mutable (e.g., the non-`mut` local `x` in an | |
2436 | /// assignment to `x.f`). | |
c295e0f8 | 2437 | pub(crate) fn report_illegal_reassignment( |
dc9dc135 XL |
2438 | &mut self, |
2439 | _location: Location, | |
ba9703b0 | 2440 | (place, span): (Place<'tcx>, Span), |
dc9dc135 | 2441 | assigned_span: Span, |
ba9703b0 | 2442 | err_place: Place<'tcx>, |
dc9dc135 | 2443 | ) { |
60c5eb7d XL |
2444 | let (from_arg, local_decl, local_name) = match err_place.as_local() { |
2445 | Some(local) => ( | |
2446 | self.body.local_kind(local) == LocalKind::Arg, | |
2447 | Some(&self.body.local_decls[local]), | |
2448 | self.local_names[local], | |
2449 | ), | |
2450 | None => (false, None, None), | |
dc9dc135 XL |
2451 | }; |
2452 | ||
2453 | // If root local is initialized immediately (everything apart from let | |
2454 | // PATTERN;) then make the error refer to that local, rather than the | |
2455 | // place being assigned later. | |
2456 | let (place_description, assigned_span) = match local_decl { | |
ba9703b0 | 2457 | Some(LocalDecl { |
dfeec247 | 2458 | local_info: |
f9f354fc | 2459 | Some(box LocalInfo::User( |
ba9703b0 XL |
2460 | ClearCrossCrate::Clear |
2461 | | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { | |
2462 | opt_match_place: None, | |
2463 | .. | |
2464 | })), | |
f9f354fc XL |
2465 | )) |
2466 | | Some(box LocalInfo::StaticRef { .. }) | |
2467 | | None, | |
dc9dc135 XL |
2468 | .. |
2469 | }) | |
ba9703b0 XL |
2470 | | None => (self.describe_any_place(place.as_ref()), assigned_span), |
2471 | Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span), | |
dc9dc135 XL |
2472 | }; |
2473 | ||
ba9703b0 | 2474 | let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg); |
dc9dc135 XL |
2475 | let msg = if from_arg { |
2476 | "cannot assign to immutable argument" | |
2477 | } else { | |
2478 | "cannot assign twice to immutable variable" | |
2479 | }; | |
04454e1e FG |
2480 | if span != assigned_span && !from_arg { |
2481 | err.span_label(assigned_span, format!("first assignment to {}", place_description)); | |
dc9dc135 | 2482 | } |
5e7ed085 FG |
2483 | if let Some(decl) = local_decl |
2484 | && let Some(name) = local_name | |
2485 | && decl.can_be_made_mutable() | |
2486 | { | |
2487 | err.span_suggestion( | |
2488 | decl.source_info.span, | |
2489 | "consider making this binding mutable", | |
2490 | format!("mut {}", name), | |
2491 | Applicability::MachineApplicable, | |
2492 | ); | |
dc9dc135 XL |
2493 | } |
2494 | err.span_label(span, msg); | |
5099ac24 | 2495 | self.buffer_error(err); |
dc9dc135 XL |
2496 | } |
2497 | ||
74b04a01 | 2498 | fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { |
dc9dc135 | 2499 | let tcx = self.infcx.tcx; |
04454e1e FG |
2500 | let (kind, _place_ty) = place.projection.iter().fold( |
2501 | (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)), | |
2502 | |(kind, place_ty), &elem| { | |
2503 | ( | |
2504 | match elem { | |
2505 | ProjectionElem::Deref => match kind { | |
2506 | StorageDeadOrDrop::LocalStorageDead | |
2507 | | StorageDeadOrDrop::BoxedStorageDead => { | |
2508 | assert!( | |
2509 | place_ty.ty.is_box(), | |
2510 | "Drop of value behind a reference or raw pointer" | |
2511 | ); | |
2512 | StorageDeadOrDrop::BoxedStorageDead | |
2513 | } | |
2514 | StorageDeadOrDrop::Destructor(_) => kind, | |
2515 | }, | |
2b03887a FG |
2516 | ProjectionElem::OpaqueCast { .. } |
2517 | | ProjectionElem::Field(..) | |
2518 | | ProjectionElem::Downcast(..) => { | |
04454e1e FG |
2519 | match place_ty.ty.kind() { |
2520 | ty::Adt(def, _) if def.has_dtor(tcx) => { | |
2521 | // Report the outermost adt with a destructor | |
2522 | match kind { | |
2523 | StorageDeadOrDrop::Destructor(_) => kind, | |
2524 | StorageDeadOrDrop::LocalStorageDead | |
2525 | | StorageDeadOrDrop::BoxedStorageDead => { | |
2526 | StorageDeadOrDrop::Destructor(place_ty.ty) | |
2527 | } | |
dc9dc135 XL |
2528 | } |
2529 | } | |
04454e1e | 2530 | _ => kind, |
dc9dc135 | 2531 | } |
dc9dc135 | 2532 | } |
04454e1e FG |
2533 | ProjectionElem::ConstantIndex { .. } |
2534 | | ProjectionElem::Subslice { .. } | |
2535 | | ProjectionElem::Index(_) => kind, | |
2536 | }, | |
2537 | place_ty.projection_ty(tcx, elem), | |
2538 | ) | |
2539 | }, | |
2540 | ); | |
2541 | kind | |
dc9dc135 XL |
2542 | } |
2543 | ||
60c5eb7d | 2544 | /// Describe the reason for the fake borrow that was assigned to `place`. |
ba9703b0 XL |
2545 | fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> { |
2546 | use rustc_middle::mir::visit::Visitor; | |
2547 | struct FakeReadCauseFinder<'tcx> { | |
2548 | place: Place<'tcx>, | |
60c5eb7d XL |
2549 | cause: Option<FakeReadCause>, |
2550 | } | |
ba9703b0 | 2551 | impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> { |
60c5eb7d XL |
2552 | fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { |
2553 | match statement { | |
cdc7bbd5 | 2554 | Statement { kind: StatementKind::FakeRead(box (cause, place)), .. } |
ba9703b0 | 2555 | if *place == self.place => |
dfeec247 | 2556 | { |
60c5eb7d XL |
2557 | self.cause = Some(*cause); |
2558 | } | |
2559 | _ => (), | |
2560 | } | |
2561 | } | |
2562 | } | |
2563 | let mut visitor = FakeReadCauseFinder { place, cause: None }; | |
ba9703b0 | 2564 | visitor.visit_body(&self.body); |
60c5eb7d XL |
2565 | match visitor.cause { |
2566 | Some(FakeReadCause::ForMatchGuard) => Some("match guard"), | |
2567 | Some(FakeReadCause::ForIndex) => Some("indexing expression"), | |
2568 | _ => None, | |
2569 | } | |
2570 | } | |
2571 | ||
dc9dc135 XL |
2572 | /// Annotate argument and return type of function and closure with (synthesized) lifetime for |
2573 | /// borrow of local value that does not live long enough. | |
2574 | fn annotate_argument_and_return_for_borrow( | |
2575 | &self, | |
2576 | borrow: &BorrowData<'tcx>, | |
2577 | ) -> Option<AnnotatedBorrowFnSignature<'tcx>> { | |
2578 | // Define a fallback for when we can't match a closure. | |
2579 | let fallback = || { | |
29967ef6 | 2580 | let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id()); |
dc9dc135 XL |
2581 | if is_closure { |
2582 | None | |
2583 | } else { | |
9ffffee4 | 2584 | let ty = self.infcx.tcx.type_of(self.mir_def_id()).subst_identity(); |
1b1a35ee | 2585 | match ty.kind() { |
f9f354fc | 2586 | ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( |
064997fb | 2587 | self.mir_def_id(), |
9ffffee4 | 2588 | self.infcx.tcx.fn_sig(self.mir_def_id()).subst_identity(), |
f9f354fc | 2589 | ), |
dc9dc135 XL |
2590 | _ => None, |
2591 | } | |
2592 | } | |
2593 | }; | |
2594 | ||
2595 | // In order to determine whether we need to annotate, we need to check whether the reserve | |
2596 | // place was an assignment into a temporary. | |
2597 | // | |
2598 | // If it was, we check whether or not that temporary is eventually assigned into the return | |
2599 | // place. If it was, we can add annotations about the function's return type and arguments | |
2600 | // and it'll make sense. | |
2601 | let location = borrow.reserve_location; | |
dfeec247 | 2602 | debug!("annotate_argument_and_return_for_borrow: location={:?}", location); |
487cf647 | 2603 | if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) = |
dfeec247 | 2604 | &self.body[location.block].statements.get(location.statement_index) |
dc9dc135 | 2605 | { |
dfeec247 | 2606 | debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation); |
dc9dc135 | 2607 | // Check that the initial assignment of the reserve location is into a temporary. |
e74abb32 XL |
2608 | let mut target = match reservation.as_local() { |
2609 | Some(local) if self.body.local_kind(local) == LocalKind::Temp => local, | |
dc9dc135 XL |
2610 | _ => return None, |
2611 | }; | |
2612 | ||
2613 | // Next, look through the rest of the block, checking if we are assigning the | |
2614 | // `target` (that is, the place that contains our borrow) to anything. | |
2615 | let mut annotated_closure = None; | |
dfeec247 | 2616 | for stmt in &self.body[location.block].statements[location.statement_index + 1..] { |
dc9dc135 XL |
2617 | debug!( |
2618 | "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", | |
2619 | target, stmt | |
2620 | ); | |
dfeec247 | 2621 | if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { |
e74abb32 XL |
2622 | if let Some(assigned_to) = place.as_local() { |
2623 | debug!( | |
2624 | "annotate_argument_and_return_for_borrow: assigned_to={:?} \ | |
2625 | rvalue={:?}", | |
2626 | assigned_to, rvalue | |
2627 | ); | |
2628 | // Check if our `target` was captured by a closure. | |
2629 | if let Rvalue::Aggregate( | |
2630 | box AggregateKind::Closure(def_id, substs), | |
487cf647 FG |
2631 | operands, |
2632 | ) = rvalue | |
e74abb32 | 2633 | { |
9ffffee4 | 2634 | let def_id = def_id.expect_local(); |
e74abb32 | 2635 | for operand in operands { |
5e7ed085 FG |
2636 | let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else { |
2637 | continue; | |
e74abb32 XL |
2638 | }; |
2639 | debug!( | |
2640 | "annotate_argument_and_return_for_borrow: assigned_from={:?}", | |
dc9dc135 | 2641 | assigned_from |
e74abb32 | 2642 | ); |
dc9dc135 | 2643 | |
e74abb32 | 2644 | // Find the local from the operand. |
5e7ed085 FG |
2645 | let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { |
2646 | continue; | |
e74abb32 | 2647 | }; |
dc9dc135 | 2648 | |
e74abb32 XL |
2649 | if assigned_from_local != target { |
2650 | continue; | |
2651 | } | |
dc9dc135 | 2652 | |
e74abb32 XL |
2653 | // If a closure captured our `target` and then assigned |
2654 | // into a place then we should annotate the closure in | |
2655 | // case it ends up being assigned into the return place. | |
ba9703b0 | 2656 | annotated_closure = |
9ffffee4 | 2657 | self.annotate_fn_sig(def_id, substs.as_closure().sig()); |
e74abb32 XL |
2658 | debug!( |
2659 | "annotate_argument_and_return_for_borrow: \ | |
2660 | annotated_closure={:?} assigned_from_local={:?} \ | |
2661 | assigned_to={:?}", | |
2662 | annotated_closure, assigned_from_local, assigned_to | |
2663 | ); | |
2664 | ||
2665 | if assigned_to == mir::RETURN_PLACE { | |
2666 | // If it was assigned directly into the return place, then | |
2667 | // return now. | |
2668 | return annotated_closure; | |
2669 | } else { | |
2670 | // Otherwise, update the target. | |
2671 | target = assigned_to; | |
2672 | } | |
dc9dc135 | 2673 | } |
dc9dc135 | 2674 | |
e74abb32 XL |
2675 | // If none of our closure's operands matched, then skip to the next |
2676 | // statement. | |
2677 | continue; | |
2678 | } | |
dc9dc135 | 2679 | |
e74abb32 XL |
2680 | // Otherwise, look at other types of assignment. |
2681 | let assigned_from = match rvalue { | |
2682 | Rvalue::Ref(_, _, assigned_from) => assigned_from, | |
2683 | Rvalue::Use(operand) => match operand { | |
2684 | Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { | |
2685 | assigned_from | |
2686 | } | |
2687 | _ => continue, | |
2688 | }, | |
dc9dc135 | 2689 | _ => continue, |
e74abb32 XL |
2690 | }; |
2691 | debug!( | |
2692 | "annotate_argument_and_return_for_borrow: \ | |
2693 | assigned_from={:?}", | |
2694 | assigned_from, | |
2695 | ); | |
dc9dc135 | 2696 | |
e74abb32 | 2697 | // Find the local from the rvalue. |
5e7ed085 | 2698 | let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue }; |
e74abb32 XL |
2699 | debug!( |
2700 | "annotate_argument_and_return_for_borrow: \ | |
2701 | assigned_from_local={:?}", | |
2702 | assigned_from_local, | |
2703 | ); | |
dc9dc135 | 2704 | |
e74abb32 XL |
2705 | // Check if our local matches the target - if so, we've assigned our |
2706 | // borrow to a new place. | |
2707 | if assigned_from_local != target { | |
2708 | continue; | |
2709 | } | |
dc9dc135 | 2710 | |
e74abb32 XL |
2711 | // If we assigned our `target` into a new place, then we should |
2712 | // check if it was the return place. | |
2713 | debug!( | |
2714 | "annotate_argument_and_return_for_borrow: \ | |
2715 | assigned_from_local={:?} assigned_to={:?}", | |
2716 | assigned_from_local, assigned_to | |
2717 | ); | |
2718 | if assigned_to == mir::RETURN_PLACE { | |
2719 | // If it was then return the annotated closure if there was one, | |
2720 | // else, annotate this function. | |
2721 | return annotated_closure.or_else(fallback); | |
2722 | } | |
dc9dc135 | 2723 | |
e74abb32 XL |
2724 | // If we didn't assign into the return place, then we just update |
2725 | // the target. | |
2726 | target = assigned_to; | |
2727 | } | |
dc9dc135 XL |
2728 | } |
2729 | } | |
2730 | ||
2731 | // Check the terminator if we didn't find anything in the statements. | |
2732 | let terminator = &self.body[location.block].terminator(); | |
2733 | debug!( | |
2734 | "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}", | |
2735 | target, terminator | |
2736 | ); | |
923072b8 | 2737 | if let TerminatorKind::Call { destination, target: Some(_), args, .. } = |
dfeec247 | 2738 | &terminator.kind |
dc9dc135 | 2739 | { |
923072b8 | 2740 | if let Some(assigned_to) = destination.as_local() { |
dc9dc135 | 2741 | debug!( |
e74abb32 XL |
2742 | "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", |
2743 | assigned_to, args | |
dc9dc135 | 2744 | ); |
e74abb32 | 2745 | for operand in args { |
5e7ed085 FG |
2746 | let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else { |
2747 | continue; | |
e74abb32 | 2748 | }; |
dc9dc135 | 2749 | debug!( |
e74abb32 XL |
2750 | "annotate_argument_and_return_for_borrow: assigned_from={:?}", |
2751 | assigned_from, | |
dc9dc135 XL |
2752 | ); |
2753 | ||
e74abb32 XL |
2754 | if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { |
2755 | debug!( | |
2756 | "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", | |
2757 | assigned_from_local, | |
2758 | ); | |
2759 | ||
2760 | if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { | |
2761 | return annotated_closure.or_else(fallback); | |
2762 | } | |
dc9dc135 XL |
2763 | } |
2764 | } | |
2765 | } | |
2766 | } | |
2767 | } | |
2768 | ||
2769 | // If we haven't found an assignment into the return place, then we need not add | |
2770 | // any annotations. | |
2771 | debug!("annotate_argument_and_return_for_borrow: none found"); | |
2772 | None | |
2773 | } | |
2774 | ||
2775 | /// Annotate the first argument and return type of a function signature if they are | |
2776 | /// references. | |
2777 | fn annotate_fn_sig( | |
2778 | &self, | |
064997fb | 2779 | did: LocalDefId, |
dc9dc135 XL |
2780 | sig: ty::PolyFnSig<'tcx>, |
2781 | ) -> Option<AnnotatedBorrowFnSignature<'tcx>> { | |
2782 | debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); | |
064997fb FG |
2783 | let is_closure = self.infcx.tcx.is_closure(did.to_def_id()); |
2784 | let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); | |
dc9dc135 XL |
2785 | let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; |
2786 | ||
2787 | // We need to work out which arguments to highlight. We do this by looking | |
2788 | // at the return type, where there are three cases: | |
2789 | // | |
2790 | // 1. If there are named arguments, then we should highlight the return type and | |
2791 | // highlight any of the arguments that are also references with that lifetime. | |
2792 | // If there are no arguments that have the same lifetime as the return type, | |
2793 | // then don't highlight anything. | |
2794 | // 2. The return type is a reference with an anonymous lifetime. If this is | |
2795 | // the case, then we can take advantage of (and teach) the lifetime elision | |
2796 | // rules. | |
2797 | // | |
2798 | // We know that an error is being reported. So the arguments and return type | |
2799 | // must satisfy the elision rules. Therefore, if there is a single argument | |
2800 | // then that means the return type and first (and only) argument have the same | |
2801 | // lifetime and the borrow isn't meeting that, we can highlight the argument | |
2802 | // and return type. | |
2803 | // | |
2804 | // If there are multiple arguments then the first argument must be self (else | |
2805 | // it would not satisfy the elision rules), so we can highlight self and the | |
2806 | // return type. | |
2807 | // 3. The return type is not a reference. In this case, we don't highlight | |
2808 | // anything. | |
2809 | let return_ty = sig.output(); | |
1b1a35ee | 2810 | match return_ty.skip_binder().kind() { |
dc9dc135 XL |
2811 | ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { |
2812 | // This is case 1 from above, return type is a named reference so we need to | |
2813 | // search for relevant arguments. | |
2814 | let mut arguments = Vec::new(); | |
2815 | for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { | |
1b1a35ee | 2816 | if let ty::Ref(argument_region, _, _) = argument.kind() { |
dc9dc135 | 2817 | if argument_region == return_region { |
ba9703b0 | 2818 | // Need to use the `rustc_middle::ty` types to compare against the |
dfeec247 | 2819 | // `return_region`. Then use the `rustc_hir` type to get only |
dc9dc135 | 2820 | // the lifetime span. |
9c376795 | 2821 | if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind { |
dc9dc135 XL |
2822 | // With access to the lifetime, we can get |
2823 | // the span of it. | |
487cf647 | 2824 | arguments.push((*argument, lifetime.ident.span)); |
dc9dc135 XL |
2825 | } else { |
2826 | bug!("ty type is a ref but hir type is not"); | |
2827 | } | |
2828 | } | |
2829 | } | |
2830 | } | |
2831 | ||
2832 | // We need to have arguments. This shouldn't happen, but it's worth checking. | |
2833 | if arguments.is_empty() { | |
2834 | return None; | |
2835 | } | |
2836 | ||
2837 | // We use a mix of the HIR and the Ty types to get information | |
2838 | // as the HIR doesn't have full types for closure arguments. | |
f035d41b | 2839 | let return_ty = sig.output().skip_binder(); |
dc9dc135 | 2840 | let mut return_span = fn_decl.output.span(); |
74b04a01 | 2841 | if let hir::FnRetTy::Return(ty) = &fn_decl.output { |
9c376795 | 2842 | if let hir::TyKind::Ref(lifetime, _) = ty.kind { |
487cf647 | 2843 | return_span = lifetime.ident.span; |
dc9dc135 XL |
2844 | } |
2845 | } | |
2846 | ||
2847 | Some(AnnotatedBorrowFnSignature::NamedFunction { | |
2848 | arguments, | |
2849 | return_ty, | |
2850 | return_span, | |
2851 | }) | |
2852 | } | |
2853 | ty::Ref(_, _, _) if is_closure => { | |
2854 | // This is case 2 from above but only for closures, return type is anonymous | |
2855 | // reference so we select | |
2856 | // the first argument. | |
2857 | let argument_span = fn_decl.inputs.first()?.span; | |
2858 | let argument_ty = sig.inputs().skip_binder().first()?; | |
2859 | ||
2860 | // Closure arguments are wrapped in a tuple, so we need to get the first | |
2861 | // from that. | |
1b1a35ee | 2862 | if let ty::Tuple(elems) = argument_ty.kind() { |
5e7ed085 | 2863 | let &argument_ty = elems.first()?; |
1b1a35ee | 2864 | if let ty::Ref(_, _, _) = argument_ty.kind() { |
dc9dc135 XL |
2865 | return Some(AnnotatedBorrowFnSignature::Closure { |
2866 | argument_ty, | |
2867 | argument_span, | |
2868 | }); | |
2869 | } | |
2870 | } | |
2871 | ||
2872 | None | |
2873 | } | |
2874 | ty::Ref(_, _, _) => { | |
2875 | // This is also case 2 from above but for functions, return type is still an | |
2876 | // anonymous reference so we select the first argument. | |
2877 | let argument_span = fn_decl.inputs.first()?.span; | |
5099ac24 | 2878 | let argument_ty = *sig.inputs().skip_binder().first()?; |
dc9dc135 XL |
2879 | |
2880 | let return_span = fn_decl.output.span(); | |
f035d41b | 2881 | let return_ty = sig.output().skip_binder(); |
dc9dc135 XL |
2882 | |
2883 | // We expect the first argument to be a reference. | |
1b1a35ee | 2884 | match argument_ty.kind() { |
dc9dc135 XL |
2885 | ty::Ref(_, _, _) => {} |
2886 | _ => return None, | |
2887 | } | |
2888 | ||
2889 | Some(AnnotatedBorrowFnSignature::AnonymousFunction { | |
2890 | argument_ty, | |
2891 | argument_span, | |
2892 | return_ty, | |
2893 | return_span, | |
2894 | }) | |
2895 | } | |
2896 | _ => { | |
2897 | // This is case 3 from above, return type is not a reference so don't highlight | |
2898 | // anything. | |
2899 | None | |
2900 | } | |
2901 | } | |
2902 | } | |
2903 | } | |
2904 | ||
2905 | #[derive(Debug)] | |
2906 | enum AnnotatedBorrowFnSignature<'tcx> { | |
2907 | NamedFunction { | |
2908 | arguments: Vec<(Ty<'tcx>, Span)>, | |
2909 | return_ty: Ty<'tcx>, | |
2910 | return_span: Span, | |
2911 | }, | |
2912 | AnonymousFunction { | |
2913 | argument_ty: Ty<'tcx>, | |
2914 | argument_span: Span, | |
2915 | return_ty: Ty<'tcx>, | |
2916 | return_span: Span, | |
2917 | }, | |
2918 | Closure { | |
2919 | argument_ty: Ty<'tcx>, | |
2920 | argument_span: Span, | |
2921 | }, | |
2922 | } | |
2923 | ||
2924 | impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { | |
2925 | /// Annotate the provided diagnostic with information about borrow from the fn signature that | |
2926 | /// helps explain. | |
5e7ed085 | 2927 | pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String { |
dc9dc135 | 2928 | match self { |
5099ac24 | 2929 | &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { |
dc9dc135 | 2930 | diag.span_label( |
5099ac24 | 2931 | argument_span, |
dc9dc135 XL |
2932 | format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)), |
2933 | ); | |
2934 | ||
2935 | cx.get_region_name_for_ty(argument_ty, 0) | |
2936 | } | |
5099ac24 | 2937 | &AnnotatedBorrowFnSignature::AnonymousFunction { |
dc9dc135 XL |
2938 | argument_ty, |
2939 | argument_span, | |
2940 | return_ty, | |
2941 | return_span, | |
2942 | } => { | |
2943 | let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); | |
5099ac24 | 2944 | diag.span_label(argument_span, format!("has type `{}`", argument_ty_name)); |
dc9dc135 XL |
2945 | |
2946 | let return_ty_name = cx.get_name_for_ty(return_ty, 0); | |
2947 | let types_equal = return_ty_name == argument_ty_name; | |
2948 | diag.span_label( | |
5099ac24 | 2949 | return_span, |
dc9dc135 XL |
2950 | format!( |
2951 | "{}has type `{}`", | |
2952 | if types_equal { "also " } else { "" }, | |
2953 | return_ty_name, | |
2954 | ), | |
2955 | ); | |
2956 | ||
2957 | diag.note( | |
2958 | "argument and return type have the same lifetime due to lifetime elision rules", | |
2959 | ); | |
2960 | diag.note( | |
2961 | "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\ | |
2962 | lifetime-syntax.html#lifetime-elision>", | |
2963 | ); | |
2964 | ||
2965 | cx.get_region_name_for_ty(return_ty, 0) | |
2966 | } | |
dfeec247 | 2967 | AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => { |
dc9dc135 | 2968 | // Region of return type and arguments checked to be the same earlier. |
5099ac24 | 2969 | let region_name = cx.get_region_name_for_ty(*return_ty, 0); |
dc9dc135 XL |
2970 | for (_, argument_span) in arguments { |
2971 | diag.span_label(*argument_span, format!("has lifetime `{}`", region_name)); | |
2972 | } | |
2973 | ||
dfeec247 | 2974 | diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,)); |
dc9dc135 XL |
2975 | |
2976 | diag.help(&format!( | |
2977 | "use data from the highlighted arguments which match the `{}` lifetime of \ | |
2978 | the return type", | |
2979 | region_name, | |
2980 | )); | |
2981 | ||
2982 | region_name | |
2983 | } | |
2984 | } | |
2985 | } | |
2986 | } | |
064997fb FG |
2987 | |
2988 | /// Detect whether one of the provided spans is a statement nested within the top-most visited expr | |
2989 | struct ReferencedStatementsVisitor<'a>(&'a [Span], bool); | |
2990 | ||
2991 | impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { | |
2992 | fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { | |
2993 | match s.kind { | |
2994 | hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => { | |
2995 | self.1 = true; | |
2996 | } | |
2997 | _ => {} | |
2998 | } | |
2999 | } | |
3000 | } | |
3001 | ||
3002 | /// Given a set of spans representing statements initializing the relevant binding, visit all the | |
3003 | /// function expressions looking for branching code paths that *do not* initialize the binding. | |
3004 | struct ConditionVisitor<'b> { | |
3005 | spans: &'b [Span], | |
3006 | name: &'b str, | |
3007 | errors: Vec<(Span, String)>, | |
3008 | } | |
3009 | ||
3010 | impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { | |
3011 | fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { | |
3012 | match ex.kind { | |
3013 | hir::ExprKind::If(cond, body, None) => { | |
3014 | // `if` expressions with no `else` that initialize the binding might be missing an | |
3015 | // `else` arm. | |
3016 | let mut v = ReferencedStatementsVisitor(self.spans, false); | |
3017 | v.visit_expr(body); | |
3018 | if v.1 { | |
3019 | self.errors.push(( | |
3020 | cond.span, | |
3021 | format!( | |
3022 | "if this `if` condition is `false`, {} is not initialized", | |
3023 | self.name, | |
3024 | ), | |
3025 | )); | |
3026 | self.errors.push(( | |
3027 | ex.span.shrink_to_hi(), | |
3028 | format!("an `else` arm might be missing here, initializing {}", self.name), | |
3029 | )); | |
3030 | } | |
3031 | } | |
3032 | hir::ExprKind::If(cond, body, Some(other)) => { | |
3033 | // `if` expressions where the binding is only initialized in one of the two arms | |
3034 | // might be missing a binding initialization. | |
3035 | let mut a = ReferencedStatementsVisitor(self.spans, false); | |
3036 | a.visit_expr(body); | |
3037 | let mut b = ReferencedStatementsVisitor(self.spans, false); | |
3038 | b.visit_expr(other); | |
3039 | match (a.1, b.1) { | |
3040 | (true, true) | (false, false) => {} | |
3041 | (true, false) => { | |
3042 | if other.span.is_desugaring(DesugaringKind::WhileLoop) { | |
3043 | self.errors.push(( | |
3044 | cond.span, | |
3045 | format!( | |
3046 | "if this condition isn't met and the `while` loop runs 0 \ | |
3047 | times, {} is not initialized", | |
3048 | self.name | |
3049 | ), | |
3050 | )); | |
3051 | } else { | |
3052 | self.errors.push(( | |
3053 | body.span.shrink_to_hi().until(other.span), | |
3054 | format!( | |
3055 | "if the `if` condition is `false` and this `else` arm is \ | |
3056 | executed, {} is not initialized", | |
3057 | self.name | |
3058 | ), | |
3059 | )); | |
3060 | } | |
3061 | } | |
3062 | (false, true) => { | |
3063 | self.errors.push(( | |
3064 | cond.span, | |
3065 | format!( | |
3066 | "if this condition is `true`, {} is not initialized", | |
3067 | self.name | |
3068 | ), | |
3069 | )); | |
3070 | } | |
3071 | } | |
3072 | } | |
3073 | hir::ExprKind::Match(e, arms, loop_desugar) => { | |
3074 | // If the binding is initialized in one of the match arms, then the other match | |
3075 | // arms might be missing an initialization. | |
3076 | let results: Vec<bool> = arms | |
3077 | .iter() | |
3078 | .map(|arm| { | |
3079 | let mut v = ReferencedStatementsVisitor(self.spans, false); | |
3080 | v.visit_arm(arm); | |
3081 | v.1 | |
3082 | }) | |
3083 | .collect(); | |
3084 | if results.iter().any(|x| *x) && !results.iter().all(|x| *x) { | |
3085 | for (arm, seen) in arms.iter().zip(results) { | |
3086 | if !seen { | |
3087 | if loop_desugar == hir::MatchSource::ForLoopDesugar { | |
3088 | self.errors.push(( | |
3089 | e.span, | |
3090 | format!( | |
3091 | "if the `for` loop runs 0 times, {} is not initialized", | |
3092 | self.name | |
3093 | ), | |
3094 | )); | |
3095 | } else if let Some(guard) = &arm.guard { | |
3096 | self.errors.push(( | |
3097 | arm.pat.span.to(guard.body().span), | |
3098 | format!( | |
3099 | "if this pattern and condition are matched, {} is not \ | |
3100 | initialized", | |
3101 | self.name | |
3102 | ), | |
3103 | )); | |
3104 | } else { | |
3105 | self.errors.push(( | |
3106 | arm.pat.span, | |
3107 | format!( | |
3108 | "if this pattern is matched, {} is not initialized", | |
3109 | self.name | |
3110 | ), | |
3111 | )); | |
3112 | } | |
3113 | } | |
3114 | } | |
3115 | } | |
3116 | } | |
3117 | // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should | |
3118 | // also be accounted for. For now it is fine, as if we don't find *any* relevant | |
3119 | // branching code paths, we point at the places where the binding *is* initialized for | |
3120 | // *some* context. | |
3121 | _ => {} | |
3122 | } | |
3123 | walk_expr(self, ex); | |
3124 | } | |
3125 | } |