]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_borrowck / src / diagnostics / mutability_errors.rs
1 use rustc_hir as hir;
2 use rustc_hir::Node;
3 use rustc_middle::hir::map::Map;
4 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
5 use rustc_middle::ty::{self, Ty, TyCtxt};
6 use rustc_middle::{
7 hir::place::PlaceBase,
8 mir::{
9 self, BindingForm, ClearCrossCrate, ImplicitSelfKind, Local, LocalDecl, LocalInfo,
10 LocalKind, Location,
11 },
12 };
13 use rustc_span::source_map::DesugaringKind;
14 use rustc_span::symbol::{kw, Symbol};
15 use rustc_span::{BytePos, Span};
16
17 use crate::diagnostics::BorrowedContentSource;
18 use crate::MirBorrowckCtxt;
19 use rustc_const_eval::util::collect_writes::FindAssignments;
20 use rustc_errors::{Applicability, Diagnostic};
21
22 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
23 pub(crate) enum AccessKind {
24 MutableBorrow,
25 Mutate,
26 }
27
28 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
29 pub(crate) fn report_mutability_error(
30 &mut self,
31 access_place: Place<'tcx>,
32 span: Span,
33 the_place_err: PlaceRef<'tcx>,
34 error_access: AccessKind,
35 location: Location,
36 ) {
37 debug!(
38 "report_mutability_error(\
39 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
40 )",
41 access_place, span, the_place_err, error_access, location,
42 );
43
44 let mut err;
45 let item_msg;
46 let reason;
47 let mut opt_source = None;
48 let access_place_desc = self.describe_any_place(access_place.as_ref());
49 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
50
51 match the_place_err {
52 PlaceRef { local, projection: [] } => {
53 item_msg = access_place_desc;
54 if access_place.as_local().is_some() {
55 reason = ", as it is not declared as mutable".to_string();
56 } else {
57 let name = self.local_names[local].expect("immutable unnamed local");
58 reason = format!(", as `{name}` is not declared as mutable");
59 }
60 }
61
62 PlaceRef {
63 local,
64 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
65 } => {
66 debug_assert!(is_closure_or_generator(
67 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
68 ));
69
70 let imm_borrow_derefed = self.upvars[upvar_index.index()]
71 .place
72 .place
73 .deref_tys()
74 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
75
76 // If the place is immutable then:
77 //
78 // - Either we deref an immutable ref to get to our final place.
79 // - We don't capture derefs of raw ptrs
80 // - Or the final place is immut because the root variable of the capture
81 // isn't marked mut and we should suggest that to the user.
82 if imm_borrow_derefed {
83 // If we deref an immutable ref then the suggestion here doesn't help.
84 return;
85 } else {
86 item_msg = access_place_desc;
87 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
88 reason = ", as it is not declared as mutable".to_string();
89 } else {
90 let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
91 reason = format!(", as `{name}` is not declared as mutable");
92 }
93 }
94 }
95
96 PlaceRef { local, projection: [ProjectionElem::Deref] }
97 if self.body.local_decls[local].is_ref_for_guard() =>
98 {
99 item_msg = access_place_desc;
100 reason = ", as it is immutable for the pattern guard".to_string();
101 }
102 PlaceRef { local, projection: [ProjectionElem::Deref] }
103 if self.body.local_decls[local].is_ref_to_static() =>
104 {
105 if access_place.projection.len() == 1 {
106 item_msg = format!("immutable static item {access_place_desc}");
107 reason = String::new();
108 } else {
109 item_msg = access_place_desc;
110 let local_info = &self.body.local_decls[local].local_info;
111 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
112 let static_name = &self.infcx.tcx.item_name(def_id);
113 reason = format!(", as `{static_name}` is an immutable static item");
114 } else {
115 bug!("is_ref_to_static return true, but not ref to static?");
116 }
117 }
118 }
119 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
120 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
121 && proj_base.is_empty()
122 && !self.upvars.is_empty()
123 {
124 item_msg = access_place_desc;
125 debug_assert!(
126 self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr()
127 );
128 debug_assert!(is_closure_or_generator(
129 Place::ty_from(
130 the_place_err.local,
131 the_place_err.projection,
132 self.body,
133 self.infcx.tcx
134 )
135 .ty
136 ));
137
138 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
139 ", as it is a captured variable in a `Fn` closure".to_string()
140 } else {
141 ", as `Fn` closures cannot mutate their captured variables".to_string()
142 }
143 } else {
144 let source = self.borrowed_content_source(PlaceRef {
145 local: the_place_err.local,
146 projection: proj_base,
147 });
148 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
149 opt_source = Some(source);
150 if let Some(desc) = self.describe_place(access_place.as_ref()) {
151 item_msg = format!("`{desc}`");
152 reason = match error_access {
153 AccessKind::Mutate => format!(", which is behind {pointer_type}"),
154 AccessKind::MutableBorrow => {
155 format!(", as it is behind {pointer_type}")
156 }
157 }
158 } else {
159 item_msg = format!("data in {pointer_type}");
160 reason = String::new();
161 }
162 }
163 }
164
165 PlaceRef {
166 local: _,
167 projection:
168 [
169 ..,
170 ProjectionElem::Index(_)
171 | ProjectionElem::ConstantIndex { .. }
172 | ProjectionElem::Subslice { .. }
173 | ProjectionElem::Downcast(..),
174 ],
175 } => bug!("Unexpected immutable place."),
176 }
177
178 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
179
180 // `act` and `acted_on` are strings that let us abstract over
181 // the verbs used in some diagnostic messages.
182 let act;
183 let acted_on;
184
185 let span = match error_access {
186 AccessKind::Mutate => {
187 err = self.cannot_assign(span, &(item_msg + &reason));
188 act = "assign";
189 acted_on = "written";
190 span
191 }
192 AccessKind::MutableBorrow => {
193 act = "borrow as mutable";
194 acted_on = "borrowed as mutable";
195
196 let borrow_spans = self.borrow_spans(span, location);
197 let borrow_span = borrow_spans.args_or_use();
198 err = self.cannot_borrow_path_as_mutable_because(borrow_span, &item_msg, &reason);
199 borrow_spans.var_span_label(
200 &mut err,
201 format!(
202 "mutable borrow occurs due to use of {} in closure",
203 self.describe_any_place(access_place.as_ref()),
204 ),
205 "mutable",
206 );
207 borrow_span
208 }
209 };
210
211 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
212
213 match the_place_err {
214 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
215 //
216 // This is applicable when we have a deref of a field access to a deref of a local -
217 // something like `*((*_1).0`. The local that we get will be a reference to the
218 // struct we've got a field access of (it must be a reference since there's a deref
219 // after the field access).
220 PlaceRef {
221 local,
222 projection:
223 &[
224 ref proj_base @ ..,
225 ProjectionElem::Deref,
226 ProjectionElem::Field(field, _),
227 ProjectionElem::Deref,
228 ],
229 } => {
230 err.span_label(span, format!("cannot {ACT}", ACT = act));
231
232 if let Some(span) = get_mut_span_in_struct_field(
233 self.infcx.tcx,
234 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
235 field,
236 ) {
237 err.span_suggestion_verbose(
238 span,
239 "consider changing this to be mutable",
240 " mut ",
241 Applicability::MaybeIncorrect,
242 );
243 }
244 }
245
246 // Suggest removing a `&mut` from the use of a mutable reference.
247 PlaceRef { local, projection: [] }
248 if self
249 .body
250 .local_decls
251 .get(local)
252 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
253 .unwrap_or(false) =>
254 {
255 let decl = &self.body.local_decls[local];
256 err.span_label(span, format!("cannot {ACT}", ACT = act));
257 if let Some(mir::Statement {
258 source_info,
259 kind:
260 mir::StatementKind::Assign(box (
261 _,
262 mir::Rvalue::Ref(
263 _,
264 mir::BorrowKind::Mut { allow_two_phase_borrow: false },
265 _,
266 ),
267 )),
268 ..
269 }) = &self.body[location.block].statements.get(location.statement_index)
270 {
271 match decl.local_info {
272 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
273 mir::VarBindingForm {
274 binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
275 opt_ty_info: Some(sp),
276 opt_match_place: _,
277 pat_span: _,
278 },
279 )))) => {
280 err.span_note(sp, "the binding is already a mutable borrow");
281 }
282 _ => {
283 err.span_note(
284 decl.source_info.span,
285 "the binding is already a mutable borrow",
286 );
287 }
288 }
289 if let Ok(snippet) =
290 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
291 {
292 if snippet.starts_with("&mut ") {
293 // We don't have access to the HIR to get accurate spans, but we can
294 // give a best effort structured suggestion.
295 err.span_suggestion_verbose(
296 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
297 "try removing `&mut` here",
298 "",
299 Applicability::MachineApplicable,
300 );
301 } else {
302 // This can occur with things like `(&mut self).foo()`.
303 err.span_help(source_info.span, "try removing `&mut` here");
304 }
305 } else {
306 err.span_help(source_info.span, "try removing `&mut` here");
307 }
308 } else if decl.mutability == Mutability::Not
309 && !matches!(
310 decl.local_info,
311 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(
312 ImplicitSelfKind::MutRef
313 ))))
314 )
315 {
316 err.span_suggestion_verbose(
317 decl.source_info.span.shrink_to_lo(),
318 "consider making the binding mutable",
319 "mut ",
320 Applicability::MachineApplicable,
321 );
322 }
323 }
324
325 // We want to suggest users use `let mut` for local (user
326 // variable) mutations...
327 PlaceRef { local, projection: [] }
328 if self.body.local_decls[local].can_be_made_mutable() =>
329 {
330 // ... but it doesn't make sense to suggest it on
331 // variables that are `ref x`, `ref mut x`, `&self`,
332 // or `&mut self` (such variables are simply not
333 // mutable).
334 let local_decl = &self.body.local_decls[local];
335 assert_eq!(local_decl.mutability, Mutability::Not);
336
337 err.span_label(span, format!("cannot {ACT}", ACT = act));
338 err.span_suggestion(
339 local_decl.source_info.span,
340 "consider changing this to be mutable",
341 format!("mut {}", self.local_names[local].unwrap()),
342 Applicability::MachineApplicable,
343 );
344 let tcx = self.infcx.tcx;
345 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
346 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
347 }
348 }
349
350 // Also suggest adding mut for upvars
351 PlaceRef {
352 local,
353 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
354 } => {
355 debug_assert!(is_closure_or_generator(
356 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
357 ));
358
359 let captured_place = &self.upvars[upvar_index.index()].place;
360
361 err.span_label(span, format!("cannot {ACT}", ACT = act));
362
363 let upvar_hir_id = captured_place.get_root_variable();
364
365 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
366 && let hir::PatKind::Binding(
367 hir::BindingAnnotation::Unannotated,
368 _,
369 upvar_ident,
370 _,
371 ) = pat.kind
372 {
373 err.span_suggestion(
374 upvar_ident.span,
375 "consider changing this to be mutable",
376 format!("mut {}", upvar_ident.name),
377 Applicability::MachineApplicable,
378 );
379 }
380
381 let tcx = self.infcx.tcx;
382 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
383 && let ty::Closure(id, _) = *ty.kind()
384 {
385 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
386 }
387 }
388
389 // complete hack to approximate old AST-borrowck
390 // diagnostic: if the span starts with a mutable borrow of
391 // a local variable, then just suggest the user remove it.
392 PlaceRef { local: _, projection: [] }
393 if {
394 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
395 snippet.starts_with("&mut ")
396 } else {
397 false
398 }
399 } =>
400 {
401 err.span_label(span, format!("cannot {ACT}", ACT = act));
402 err.span_suggestion(
403 span,
404 "try removing `&mut` here",
405 "",
406 Applicability::MaybeIncorrect,
407 );
408 }
409
410 PlaceRef { local, projection: [ProjectionElem::Deref] }
411 if self.body.local_decls[local].is_ref_for_guard() =>
412 {
413 err.span_label(span, format!("cannot {ACT}", ACT = act));
414 err.note(
415 "variables bound in patterns are immutable until the end of the pattern guard",
416 );
417 }
418
419 // We want to point out when a `&` can be readily replaced
420 // with an `&mut`.
421 //
422 // FIXME: can this case be generalized to work for an
423 // arbitrary base for the projection?
424 PlaceRef { local, projection: [ProjectionElem::Deref] }
425 if self.body.local_decls[local].is_user_variable() =>
426 {
427 let local_decl = &self.body.local_decls[local];
428
429 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
430 ("&", "reference")
431 } else {
432 ("*const", "pointer")
433 };
434
435 match self.local_names[local] {
436 Some(name) if !local_decl.from_compiler_desugaring() => {
437 let label = match local_decl.local_info.as_ref().unwrap() {
438 box LocalInfo::User(ClearCrossCrate::Set(
439 mir::BindingForm::ImplicitSelf(_),
440 )) => {
441 let (span, suggestion) =
442 suggest_ampmut_self(self.infcx.tcx, local_decl);
443 Some((true, span, suggestion))
444 }
445
446 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
447 mir::VarBindingForm {
448 binding_mode: ty::BindingMode::BindByValue(_),
449 opt_ty_info,
450 ..
451 },
452 ))) => {
453 // check if the RHS is from desugaring
454 let opt_assignment_rhs_span =
455 self.body.find_assignments(local).first().map(|&location| {
456 if let Some(mir::Statement {
457 source_info: _,
458 kind:
459 mir::StatementKind::Assign(box (
460 _,
461 mir::Rvalue::Use(mir::Operand::Copy(place)),
462 )),
463 }) = self.body[location.block]
464 .statements
465 .get(location.statement_index)
466 {
467 self.body.local_decls[place.local].source_info.span
468 } else {
469 self.body.source_info(location).span
470 }
471 });
472 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
473 // on for loops, RHS points to the iterator part
474 Some(DesugaringKind::ForLoop) => {
475 self.suggest_similar_mut_method_for_for_loop(&mut err);
476 Some((
477 false,
478 opt_assignment_rhs_span.unwrap(),
479 format!(
480 "this iterator yields `{SIGIL}` {DESC}s",
481 SIGIL = pointer_sigil,
482 DESC = pointer_desc
483 ),
484 ))
485 }
486 // don't create labels for compiler-generated spans
487 Some(_) => None,
488 None => {
489 let (span, suggestion) = if name != kw::SelfLower {
490 suggest_ampmut(
491 self.infcx.tcx,
492 local_decl,
493 opt_assignment_rhs_span,
494 *opt_ty_info,
495 )
496 } else {
497 match local_decl.local_info.as_deref() {
498 Some(LocalInfo::User(ClearCrossCrate::Set(
499 mir::BindingForm::Var(mir::VarBindingForm {
500 opt_ty_info: None,
501 ..
502 }),
503 ))) => {
504 suggest_ampmut_self(self.infcx.tcx, local_decl)
505 }
506 // explicit self (eg `self: &'a Self`)
507 _ => suggest_ampmut(
508 self.infcx.tcx,
509 local_decl,
510 opt_assignment_rhs_span,
511 *opt_ty_info,
512 ),
513 }
514 };
515 Some((true, span, suggestion))
516 }
517 }
518 }
519
520 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
521 mir::VarBindingForm {
522 binding_mode: ty::BindingMode::BindByReference(_),
523 ..
524 },
525 ))) => {
526 let pattern_span = local_decl.source_info.span;
527 suggest_ref_mut(self.infcx.tcx, pattern_span)
528 .map(|replacement| (true, pattern_span, replacement))
529 }
530
531 box LocalInfo::User(ClearCrossCrate::Clear) => {
532 bug!("saw cleared local state")
533 }
534
535 _ => unreachable!(),
536 };
537
538 match label {
539 Some((true, err_help_span, suggested_code)) => {
540 let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
541 if !is_trait_sig {
542 err.span_suggestion(
543 err_help_span,
544 &format!(
545 "consider changing this to be a mutable {pointer_desc}"
546 ),
547 suggested_code,
548 Applicability::MachineApplicable,
549 );
550 } else if let Some(x) = local_trait {
551 err.span_suggestion(
552 x,
553 &format!(
554 "consider changing that to be a mutable {pointer_desc}"
555 ),
556 suggested_code,
557 Applicability::MachineApplicable,
558 );
559 }
560 }
561 Some((false, err_label_span, message)) => {
562 err.span_label(err_label_span, &message);
563 }
564 None => {}
565 }
566 err.span_label(
567 span,
568 format!(
569 "`{NAME}` is a `{SIGIL}` {DESC}, \
570 so the data it refers to cannot be {ACTED_ON}",
571 NAME = name,
572 SIGIL = pointer_sigil,
573 DESC = pointer_desc,
574 ACTED_ON = acted_on
575 ),
576 );
577 }
578 _ => {
579 err.span_label(
580 span,
581 format!(
582 "cannot {ACT} through `{SIGIL}` {DESC}",
583 ACT = act,
584 SIGIL = pointer_sigil,
585 DESC = pointer_desc
586 ),
587 );
588 }
589 }
590 }
591
592 PlaceRef { local, projection: [ProjectionElem::Deref] }
593 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
594 {
595 self.expected_fn_found_fn_mut_call(&mut err, span, act);
596 }
597
598 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
599 err.span_label(span, format!("cannot {ACT}", ACT = act));
600
601 match opt_source {
602 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
603 err.help(&format!(
604 "trait `DerefMut` is required to modify through a dereference, \
605 but it is not implemented for `{ty}`",
606 ));
607 }
608 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
609 err.help(&format!(
610 "trait `IndexMut` is required to modify indexed content, \
611 but it is not implemented for `{ty}`",
612 ));
613 }
614 _ => (),
615 }
616 }
617
618 _ => {
619 err.span_label(span, format!("cannot {ACT}", ACT = act));
620 }
621 }
622
623 self.buffer_error(err);
624 }
625
626 /// User cannot make signature of a trait mutable without changing the
627 /// trait. So we find if this error belongs to a trait and if so we move
628 /// suggestion to the trait or disable it if it is out of scope of this crate
629 fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
630 if self.body.local_kind(local) != LocalKind::Arg {
631 return (false, None);
632 }
633 let hir_map = self.infcx.tcx.hir();
634 let my_def = self.body.source.def_id();
635 let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
636 let Some(td) =
637 self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
638 else {
639 return (false, None);
640 };
641 (
642 true,
643 td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) {
644 Some(Node::Item(hir::Item {
645 kind: hir::ItemKind::Trait(_, _, _, _, items),
646 ..
647 })) => {
648 let mut f_in_trait_opt = None;
649 for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
650 let hi = fi.hir_id();
651 if !matches!(k, hir::AssocItemKind::Fn { .. }) {
652 continue;
653 }
654 if hir_map.name(hi) != hir_map.name(my_hir) {
655 continue;
656 }
657 f_in_trait_opt = Some(hi);
658 break;
659 }
660 f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
661 Some(Node::TraitItem(hir::TraitItem {
662 kind:
663 hir::TraitItemKind::Fn(
664 hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
665 _,
666 ),
667 ..
668 })) => {
669 let hir::Ty { span, .. } = inputs[local.index() - 1];
670 Some(span)
671 }
672 _ => None,
673 })
674 }
675 _ => None,
676 }),
677 )
678 }
679
680 // point to span of upvar making closure call require mutable borrow
681 fn show_mutating_upvar(
682 &self,
683 tcx: TyCtxt<'_>,
684 id: hir::def_id::DefId,
685 the_place_err: PlaceRef<'tcx>,
686 err: &mut Diagnostic,
687 ) {
688 let closure_local_def_id = id.expect_local();
689 let tables = tcx.typeck(closure_local_def_id);
690 let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
691 if let Some((span, closure_kind_origin)) =
692 &tables.closure_kind_origins().get(closure_hir_id)
693 {
694 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
695 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
696 let root_hir_id = upvar_id.var_path.hir_id;
697 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
698 let captured_places = tables.closure_min_captures[&id].get(&root_hir_id).unwrap();
699
700 let origin_projection = closure_kind_origin
701 .projections
702 .iter()
703 .map(|proj| proj.kind)
704 .collect::<Vec<_>>();
705 let mut capture_reason = String::new();
706 for captured_place in captured_places {
707 let captured_place_kinds = captured_place
708 .place
709 .projections
710 .iter()
711 .map(|proj| proj.kind)
712 .collect::<Vec<_>>();
713 if rustc_middle::ty::is_ancestor_or_same_capture(
714 &captured_place_kinds,
715 &origin_projection,
716 ) {
717 match captured_place.info.capture_kind {
718 ty::UpvarCapture::ByRef(
719 ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
720 ) => {
721 capture_reason = format!("mutable borrow of `{upvar}`");
722 }
723 ty::UpvarCapture::ByValue => {
724 capture_reason = format!("possible mutation of `{upvar}`");
725 }
726 _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
727 }
728 break;
729 }
730 }
731 if capture_reason.is_empty() {
732 bug!("upvar `{upvar}` borrowed, but cannot find reason");
733 }
734 capture_reason
735 } else {
736 bug!("not an upvar")
737 };
738 err.span_label(
739 *span,
740 format!(
741 "calling `{}` requires mutable binding due to {}",
742 self.describe_place(the_place_err).unwrap(),
743 reason
744 ),
745 );
746 }
747 }
748
749 // Attempt to search similar mutable associated items for suggestion.
750 // In the future, attempt in all path but initially for RHS of for_loop
751 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
752 use hir::{
753 BodyId, Expr,
754 ExprKind::{Block, Call, DropTemps, Match, MethodCall},
755 HirId, ImplItem, ImplItemKind, Item, ItemKind,
756 };
757
758 fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option<BodyId> {
759 match hir_map.find(id) {
760 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
761 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
762 Some(*body_id)
763 }
764 _ => None,
765 }
766 }
767 let hir_map = self.infcx.tcx.hir();
768 let mir_body_hir_id = self.mir_hir_id();
769 if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) {
770 if let Block(
771 hir::Block {
772 expr:
773 Some(Expr {
774 kind:
775 DropTemps(Expr {
776 kind:
777 Match(
778 Expr {
779 kind:
780 Call(
781 _,
782 [
783 Expr {
784 kind:
785 MethodCall(
786 path_segment,
787 _args,
788 span,
789 ),
790 hir_id,
791 ..
792 },
793 ..,
794 ],
795 ),
796 ..
797 },
798 ..,
799 ),
800 ..
801 }),
802 ..
803 }),
804 ..
805 },
806 _,
807 ) = hir_map.body(fn_body_id).value.kind
808 {
809 let opt_suggestions = path_segment
810 .hir_id
811 .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
812 .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
813 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
814 .map(|def_id| self.infcx.tcx.associated_items(def_id))
815 .map(|assoc_items| {
816 assoc_items
817 .in_definition_order()
818 .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
819 .filter(|&ident| {
820 let original_method_ident = path_segment.ident;
821 original_method_ident != ident
822 && ident
823 .as_str()
824 .starts_with(&original_method_ident.name.to_string())
825 })
826 .map(|ident| format!("{ident}()"))
827 .peekable()
828 });
829
830 if let Some(mut suggestions) = opt_suggestions
831 && suggestions.peek().is_some()
832 {
833 err.span_suggestions(
834 *span,
835 "use mutable method",
836 suggestions,
837 Applicability::MaybeIncorrect,
838 );
839 }
840 }
841 };
842 }
843
844 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
845 fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
846 err.span_label(sp, format!("cannot {act}"));
847
848 let hir = self.infcx.tcx.hir();
849 let closure_id = self.mir_hir_id();
850 let fn_call_id = hir.get_parent_node(closure_id);
851 let node = hir.get(fn_call_id);
852 let item_id = hir.enclosing_body_owner(fn_call_id);
853 let mut look_at_return = true;
854 // If we can detect the expression to be an `fn` call where the closure was an argument,
855 // we point at the `fn` definition argument...
856 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
857 let arg_pos = args
858 .iter()
859 .enumerate()
860 .filter(|(_, arg)| arg.span == self.body.span)
861 .map(|(pos, _)| pos)
862 .next();
863 let def_id = hir.local_def_id(item_id);
864 let tables = self.infcx.tcx.typeck(def_id);
865 if let Some(ty::FnDef(def_id, _)) =
866 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
867 {
868 let arg = match hir.get_if_local(*def_id) {
869 Some(
870 hir::Node::Item(hir::Item {
871 ident, kind: hir::ItemKind::Fn(sig, ..), ..
872 })
873 | hir::Node::TraitItem(hir::TraitItem {
874 ident,
875 kind: hir::TraitItemKind::Fn(sig, _),
876 ..
877 })
878 | hir::Node::ImplItem(hir::ImplItem {
879 ident,
880 kind: hir::ImplItemKind::Fn(sig, _),
881 ..
882 }),
883 ) => Some(
884 arg_pos
885 .and_then(|pos| {
886 sig.decl.inputs.get(
887 pos + if sig.decl.implicit_self.has_implicit_self() {
888 1
889 } else {
890 0
891 },
892 )
893 })
894 .map(|arg| arg.span)
895 .unwrap_or(ident.span),
896 ),
897 _ => None,
898 };
899 if let Some(span) = arg {
900 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
901 err.span_label(func.span, "expects `Fn` instead of `FnMut`");
902 if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) {
903 err.span_label(self.body.span, "in this closure");
904 }
905 look_at_return = false;
906 }
907 }
908 }
909
910 if look_at_return && hir.get_return_block(closure_id).is_some() {
911 // ...otherwise we are probably in the tail expression of the function, point at the
912 // return type.
913 match hir.get_by_def_id(hir.get_parent_item(fn_call_id)) {
914 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
915 | hir::Node::TraitItem(hir::TraitItem {
916 ident,
917 kind: hir::TraitItemKind::Fn(sig, _),
918 ..
919 })
920 | hir::Node::ImplItem(hir::ImplItem {
921 ident,
922 kind: hir::ImplItemKind::Fn(sig, _),
923 ..
924 }) => {
925 err.span_label(ident.span, "");
926 err.span_label(
927 sig.decl.output.span(),
928 "change this to return `FnMut` instead of `Fn`",
929 );
930 err.span_label(self.body.span, "in this closure");
931 }
932 _ => {}
933 }
934 }
935 }
936 }
937
938 fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
939 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
940
941 match local_decl.local_info.as_deref() {
942 // Check if mutably borrowing a mutable reference.
943 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
944 mir::VarBindingForm {
945 binding_mode: ty::BindingMode::BindByValue(Mutability::Not), ..
946 },
947 )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
948 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => {
949 // Check if the user variable is a `&mut self` and we can therefore
950 // suggest removing the `&mut`.
951 //
952 // Deliberately fall into this case for all implicit self types,
953 // so that we don't fall in to the next case with them.
954 *kind == mir::ImplicitSelfKind::MutRef
955 }
956 _ if Some(kw::SelfLower) == local_name => {
957 // Otherwise, check if the name is the `self` keyword - in which case
958 // we have an explicit self. Do the same thing in this case and check
959 // for a `self: &mut Self` to suggest removing the `&mut`.
960 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
961 }
962 _ => false,
963 }
964 }
965
966 fn suggest_ampmut_self<'tcx>(
967 tcx: TyCtxt<'tcx>,
968 local_decl: &mir::LocalDecl<'tcx>,
969 ) -> (Span, String) {
970 let sp = local_decl.source_info.span;
971 (
972 sp,
973 match tcx.sess.source_map().span_to_snippet(sp) {
974 Ok(snippet) => {
975 let lt_pos = snippet.find('\'');
976 if let Some(lt_pos) = lt_pos {
977 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
978 } else {
979 "&mut self".to_string()
980 }
981 }
982 _ => "&mut self".to_string(),
983 },
984 )
985 }
986
987 // When we want to suggest a user change a local variable to be a `&mut`, there
988 // are three potential "obvious" things to highlight:
989 //
990 // let ident [: Type] [= RightHandSideExpression];
991 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
992 // (1.) (2.) (3.)
993 //
994 // We can always fallback on highlighting the first. But chances are good that
995 // the user experience will be better if we highlight one of the others if possible;
996 // for example, if the RHS is present and the Type is not, then the type is going to
997 // be inferred *from* the RHS, which means we should highlight that (and suggest
998 // that they borrow the RHS mutably).
999 //
1000 // This implementation attempts to emulate AST-borrowck prioritization
1001 // by trying (3.), then (2.) and finally falling back on (1.).
1002 fn suggest_ampmut<'tcx>(
1003 tcx: TyCtxt<'tcx>,
1004 local_decl: &mir::LocalDecl<'tcx>,
1005 opt_assignment_rhs_span: Option<Span>,
1006 opt_ty_info: Option<Span>,
1007 ) -> (Span, String) {
1008 if let Some(assignment_rhs_span) = opt_assignment_rhs_span
1009 && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
1010 {
1011 let is_mutbl = |ty: &str| -> bool {
1012 if let Some(rest) = ty.strip_prefix("mut") {
1013 match rest.chars().next() {
1014 // e.g. `&mut x`
1015 Some(c) if c.is_whitespace() => true,
1016 // e.g. `&mut(x)`
1017 Some('(') => true,
1018 // e.g. `&mut{x}`
1019 Some('{') => true,
1020 // e.g. `&mutablevar`
1021 _ => false,
1022 }
1023 } else {
1024 false
1025 }
1026 };
1027 if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1028 let lt_name = &src[1..ws_pos];
1029 let ty = src[ws_pos..].trim_start();
1030 if !is_mutbl(ty) {
1031 return (assignment_rhs_span, format!("&{lt_name} mut {ty}"));
1032 }
1033 } else if let Some(stripped) = src.strip_prefix('&') {
1034 let stripped = stripped.trim_start();
1035 if !is_mutbl(stripped) {
1036 return (assignment_rhs_span, format!("&mut {stripped}"));
1037 }
1038 }
1039 }
1040
1041 let highlight_span = match opt_ty_info {
1042 // if this is a variable binding with an explicit type,
1043 // try to highlight that for the suggestion.
1044 Some(ty_span) => ty_span,
1045
1046 // otherwise, just highlight the span associated with
1047 // the (MIR) LocalDecl.
1048 None => local_decl.source_info.span,
1049 };
1050
1051 if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span)
1052 && let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace))
1053 {
1054 let lt_name = &src[1..ws_pos];
1055 let ty = &src[ws_pos..];
1056 return (highlight_span, format!("&{} mut{}", lt_name, ty));
1057 }
1058
1059 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
1060 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1061 (
1062 highlight_span,
1063 if local_decl.ty.is_region_ptr() {
1064 format!("&mut {}", ty_mut.ty)
1065 } else {
1066 format!("*mut {}", ty_mut.ty)
1067 },
1068 )
1069 }
1070
1071 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1072 ty.is_closure() || ty.is_generator()
1073 }
1074
1075 /// Given a field that needs to be mutable, returns a span where the " mut " could go.
1076 /// This function expects the local to be a reference to a struct in order to produce a span.
1077 ///
1078 /// ```text
1079 /// LL | s: &'a String
1080 /// | ^^^ returns a span taking up the space here
1081 /// ```
1082 fn get_mut_span_in_struct_field<'tcx>(
1083 tcx: TyCtxt<'tcx>,
1084 ty: Ty<'tcx>,
1085 field: mir::Field,
1086 ) -> Option<Span> {
1087 // Expect our local to be a reference to a struct of some kind.
1088 if let ty::Ref(_, ty, _) = ty.kind()
1089 && let ty::Adt(def, _) = ty.kind()
1090 && let field = def.all_fields().nth(field.index())?
1091 // Use the HIR types to construct the diagnostic message.
1092 && let node = tcx.hir().find_by_def_id(field.did.as_local()?)?
1093 // Now we're dealing with the actual struct that we're going to suggest a change to,
1094 // we can expect a field that is an immutable reference to a type.
1095 && let hir::Node::Field(field) = node
1096 && let hir::TyKind::Rptr(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1097 {
1098 return Some(lt.span.between(ty.span));
1099 }
1100
1101 None
1102 }
1103
1104 /// If possible, suggest replacing `ref` with `ref mut`.
1105 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
1106 let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
1107 if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
1108 let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
1109 Some(replacement)
1110 } else {
1111 None
1112 }
1113 }