1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use core::ops::ControlFlow;
5
6use hir::{ExprKind, Param};
7use rustc_abi::FieldIdx;
8use rustc_errors::{Applicability, Diag};
9use rustc_hir::intravisit::Visitor;
10use rustc_hir::{self as hir, BindingMode, ByRef, Node};
11use rustc_middle::bug;
12use rustc_middle::hir::place::PlaceBase;
13use rustc_middle::mir::visit::PlaceContext;
14use rustc_middle::mir::{
15 self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
16 PlaceRef, ProjectionElem,
17};
18use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
19use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
20use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
21use rustc_trait_selection::infer::InferCtxtExt;
22use rustc_trait_selection::traits;
23use tracing::debug;
24
25use crate::diagnostics::BorrowedContentSource;
26use crate::{MirBorrowckCtxt, session_diagnostics};
27
28#[derive(Copy, Clone, Debug, Eq, PartialEq)]
29pub(crate) enum AccessKind {
30 MutableBorrow,
31 Mutate,
32}
33
34impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
35 pub(crate) fn report_mutability_error(
36 &mut self,
37 access_place: Place<'tcx>,
38 span: Span,
39 the_place_err: PlaceRef<'tcx>,
40 error_access: AccessKind,
41 location: Location,
42 ) {
43 debug!(
44 "report_mutability_error(\
45 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
46 )",
47 access_place, span, the_place_err, error_access, location,
48 );
49
50 let mut err;
51 let item_msg;
52 let reason;
53 let mut opt_source = None;
54 let access_place_desc = self.describe_any_place(access_place.as_ref());
55 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
56
57 match the_place_err {
58 PlaceRef { local, projection: [] } => {
59 item_msg = access_place_desc;
60 if access_place.as_local().is_some() {
61 reason = ", as it is not declared as mutable".to_string();
62 } else {
63 let name = self.local_name(local).expect("immutable unnamed local");
64 reason = format!(", as `{name}` is not declared as mutable");
65 }
66 }
67
68 PlaceRef {
69 local,
70 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
71 } => {
72 debug_assert!(is_closure_like(
73 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
74 ));
75
76 let imm_borrow_derefed = self.upvars[upvar_index.index()]
77 .place
78 .deref_tys()
79 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
80
81 if imm_borrow_derefed {
88 return;
90 } else {
91 item_msg = access_place_desc;
92 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
93 reason = ", as it is not declared as mutable".to_string();
94 } else {
95 let name = self.upvars[upvar_index.index()].to_string(self.infcx.tcx);
96 reason = format!(", as `{name}` is not declared as mutable");
97 }
98 }
99 }
100
101 PlaceRef { local, projection: [ProjectionElem::Deref] }
102 if self.body.local_decls[local].is_ref_for_guard() =>
103 {
104 item_msg = access_place_desc;
105 reason = ", as it is immutable for the pattern guard".to_string();
106 }
107 PlaceRef { local, projection: [ProjectionElem::Deref] }
108 if self.body.local_decls[local].is_ref_to_static() =>
109 {
110 if access_place.projection.len() == 1 {
111 item_msg = format!("immutable static item {access_place_desc}");
112 reason = String::new();
113 } else {
114 item_msg = access_place_desc;
115 let local_info = self.body.local_decls[local].local_info();
116 if let LocalInfo::StaticRef { def_id, .. } = *local_info {
117 let static_name = &self.infcx.tcx.item_name(def_id);
118 reason = format!(", as `{static_name}` is an immutable static item");
119 } else {
120 bug!("is_ref_to_static return true, but not ref to static?");
121 }
122 }
123 }
124 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
125 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
126 && proj_base.is_empty()
127 && !self.upvars.is_empty()
128 {
129 item_msg = access_place_desc;
130 debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
131 debug_assert!(is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty));
132
133 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
134 ", as it is a captured variable in a `Fn` closure".to_string()
135 } else {
136 ", as `Fn` closures cannot mutate their captured variables".to_string()
137 }
138 } else {
139 let source = self.borrowed_content_source(PlaceRef {
140 local: the_place_err.local,
141 projection: proj_base,
142 });
143 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
144 opt_source = Some(source);
145 if let Some(desc) = self.describe_place(access_place.as_ref()) {
146 item_msg = format!("`{desc}`");
147 reason = match error_access {
148 AccessKind::Mutate => format!(", which is behind {pointer_type}"),
149 AccessKind::MutableBorrow => {
150 format!(", as it is behind {pointer_type}")
151 }
152 }
153 } else {
154 item_msg = format!("data in {pointer_type}");
155 reason = String::new();
156 }
157 }
158 }
159
160 PlaceRef {
161 local: _,
162 projection:
163 [
164 ..,
165 ProjectionElem::Index(_)
166 | ProjectionElem::Subtype(_)
167 | ProjectionElem::ConstantIndex { .. }
168 | ProjectionElem::OpaqueCast { .. }
169 | ProjectionElem::Subslice { .. }
170 | ProjectionElem::Downcast(..)
171 | ProjectionElem::UnwrapUnsafeBinder(_),
172 ],
173 } => bug!("Unexpected immutable place."),
174 }
175
176 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
177
178 let act;
181 let acted_on;
182 let mut suggest = true;
183 let mut mut_error = None;
184 let mut count = 1;
185
186 let span = match error_access {
187 AccessKind::Mutate => {
188 err = self.cannot_assign(span, &(item_msg + &reason));
189 act = "assign";
190 acted_on = "written";
191 span
192 }
193 AccessKind::MutableBorrow => {
194 act = "borrow as mutable";
195 acted_on = "borrowed as mutable";
196
197 let borrow_spans = self.borrow_spans(span, location);
198 let borrow_span = borrow_spans.args_or_use();
199 match the_place_err {
200 PlaceRef { local, projection: [] }
201 if self.body.local_decls[local].can_be_made_mutable() =>
202 {
203 let span = self.body.local_decls[local].source_info.span;
204 mut_error = Some(span);
205 if let Some((buffered_err, c)) = self.get_buffered_mut_error(span) {
206 err = buffered_err;
211 count = c + 1;
212 if count == 2 {
213 err.replace_span_with(span, false);
214 err.span_label(span, "not mutable");
215 }
216 suggest = false;
217 } else {
218 err = self.cannot_borrow_path_as_mutable_because(
219 borrow_span,
220 &item_msg,
221 &reason,
222 );
223 }
224 }
225 _ => {
226 err = self.cannot_borrow_path_as_mutable_because(
227 borrow_span,
228 &item_msg,
229 &reason,
230 );
231 }
232 }
233 if suggest {
234 borrow_spans.var_subdiag(
235 &mut err,
236 Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
237 |_kind, var_span| {
238 let place = self.describe_any_place(access_place.as_ref());
239 session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
240 place,
241 var_span,
242 }
243 },
244 );
245 }
246 borrow_span
247 }
248 };
249
250 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
251
252 match the_place_err {
253 PlaceRef {
260 local,
261 projection:
262 [
263 proj_base @ ..,
264 ProjectionElem::Deref,
265 ProjectionElem::Field(field, _),
266 ProjectionElem::Deref,
267 ],
268 } => {
269 err.span_label(span, format!("cannot {act}"));
270
271 let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
272 if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
273 err.span_suggestion_verbose(
274 span,
275 "consider changing this to be mutable",
276 " mut ",
277 Applicability::MaybeIncorrect,
278 );
279 }
280 }
281
282 PlaceRef { local, projection: [] }
284 if self
285 .body
286 .local_decls
287 .get(local)
288 .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
289 {
290 let decl = &self.body.local_decls[local];
291 err.span_label(span, format!("cannot {act}"));
292 if let Some(mir::Statement {
293 source_info,
294 kind:
295 mir::StatementKind::Assign(box (
296 _,
297 mir::Rvalue::Ref(
298 _,
299 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
300 _,
301 ),
302 )),
303 ..
304 }) = &self.body[location.block].statements.get(location.statement_index)
305 {
306 match *decl.local_info() {
307 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
308 binding_mode: BindingMode(ByRef::No, Mutability::Not),
309 opt_ty_info: Some(sp),
310 opt_match_place: _,
311 pat_span: _,
312 })) => {
313 if suggest {
314 err.span_note(sp, "the binding is already a mutable borrow");
315 }
316 }
317 _ => {
318 err.span_note(
319 decl.source_info.span,
320 "the binding is already a mutable borrow",
321 );
322 }
323 }
324 if let Ok(snippet) =
325 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
326 {
327 if snippet.starts_with("&mut ") {
328 err.span_suggestion_verbose(
331 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
332 "try removing `&mut` here",
333 "",
334 Applicability::MachineApplicable,
335 );
336 } else {
337 err.span_help(source_info.span, "try removing `&mut` here");
339 }
340 } else {
341 err.span_help(source_info.span, "try removing `&mut` here");
342 }
343 } else if decl.mutability.is_not() {
344 if matches!(
345 decl.local_info(),
346 LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
347 ) {
348 err.note(
349 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
350 );
351 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
352 } else {
353 err.span_suggestion_verbose(
354 decl.source_info.span.shrink_to_lo(),
355 "consider making the binding mutable",
356 "mut ",
357 Applicability::MachineApplicable,
358 );
359 };
360 }
361 }
362
363 PlaceRef { local, projection: [] }
366 if self.body.local_decls[local].can_be_made_mutable() =>
367 {
368 let local_decl = &self.body.local_decls[local];
373 assert_eq!(local_decl.mutability, Mutability::Not);
374
375 if count < 10 {
376 err.span_label(span, format!("cannot {act}"));
377 }
378 if suggest {
379 self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
380 let tcx = self.infcx.tcx;
381 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
382 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
383 }
384 }
385 }
386
387 PlaceRef {
389 local,
390 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
391 } => {
392 debug_assert!(is_closure_like(
393 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
394 ));
395
396 let captured_place = self.upvars[upvar_index.index()];
397
398 err.span_label(span, format!("cannot {act}"));
399
400 let upvar_hir_id = captured_place.get_root_variable();
401
402 if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
403 && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
404 pat.kind
405 {
406 if upvar_ident.name == kw::SelfLower {
407 for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
408 if let Some(fn_decl) = node.fn_decl() {
409 if !matches!(
410 fn_decl.implicit_self,
411 hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
412 ) {
413 err.span_suggestion_verbose(
414 upvar_ident.span.shrink_to_lo(),
415 "consider changing this to be mutable",
416 "mut ",
417 Applicability::MachineApplicable,
418 );
419 break;
420 }
421 }
422 }
423 } else {
424 err.span_suggestion_verbose(
425 upvar_ident.span.shrink_to_lo(),
426 "consider changing this to be mutable",
427 "mut ",
428 Applicability::MachineApplicable,
429 );
430 }
431 }
432
433 let tcx = self.infcx.tcx;
434 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
435 && let ty::Closure(id, _) = *ty.kind()
436 {
437 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
438 }
439 }
440
441 PlaceRef { local: _, projection: [] }
445 if self
446 .infcx
447 .tcx
448 .sess
449 .source_map()
450 .span_to_snippet(span)
451 .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
452 {
453 err.span_label(span, format!("cannot {act}"));
454 err.span_suggestion_verbose(
455 span.with_hi(span.lo() + BytePos(5)),
456 "try removing `&mut` here",
457 "",
458 Applicability::MaybeIncorrect,
459 );
460 }
461
462 PlaceRef { local, projection: [ProjectionElem::Deref] }
463 if self.body.local_decls[local].is_ref_for_guard() =>
464 {
465 err.span_label(span, format!("cannot {act}"));
466 err.note(
467 "variables bound in patterns are immutable until the end of the pattern guard",
468 );
469 }
470
471 PlaceRef { local, projection: [ProjectionElem::Deref] }
477 if self.body.local_decls[local].is_user_variable() =>
478 {
479 let local_decl = &self.body.local_decls[local];
480
481 let (pointer_sigil, pointer_desc) =
482 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
483
484 match self.local_name(local) {
485 Some(name) if !local_decl.from_compiler_desugaring() => {
486 err.span_label(
487 span,
488 format!(
489 "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
490 so the data it refers to cannot be {acted_on}",
491 ),
492 );
493
494 self.suggest_using_iter_mut(&mut err);
495 self.suggest_make_local_mut(&mut err, local, name);
496 }
497 _ => {
498 err.span_label(
499 span,
500 format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
501 );
502 }
503 }
504 }
505
506 PlaceRef { local, projection: [ProjectionElem::Deref] }
507 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
508 {
509 self.expected_fn_found_fn_mut_call(&mut err, span, act);
510 }
511
512 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
513 err.span_label(span, format!("cannot {act}"));
514
515 match opt_source {
516 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
517 err.help(format!(
518 "trait `DerefMut` is required to modify through a dereference, \
519 but it is not implemented for `{ty}`",
520 ));
521 }
522 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
523 err.help(format!(
524 "trait `IndexMut` is required to modify indexed content, \
525 but it is not implemented for `{ty}`",
526 ));
527 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
528 }
529 _ => (),
530 }
531 }
532
533 _ => {
534 err.span_label(span, format!("cannot {act}"));
535 }
536 }
537
538 if let Some(span) = mut_error {
539 self.buffer_mut_error(span, err, count);
540 } else {
541 self.buffer_error(err);
542 }
543 }
544
545 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
547 let Some(adt) = ty.ty_adt_def() else { return };
548 let did = adt.did();
549 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
550 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
551 {
552 struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
556 assign_span: Span,
557 err: &'a mut Diag<'infcx>,
558 ty: Ty<'tcx>,
559 suggested: bool,
560 }
561 impl<'a, 'infcx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
562 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
563 hir::intravisit::walk_stmt(self, stmt);
564 let expr = match stmt.kind {
565 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
566 hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr,
567 _ => {
568 return;
569 }
570 };
571 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
572 && let hir::ExprKind::Index(val, index, _) = place.kind
573 && (expr.span == self.assign_span || place.span == self.assign_span)
574 {
575 self.err.multipart_suggestions(
578 format!(
579 "use `.insert()` to insert a value into a `{}`, `.get_mut()` \
580 to modify it, or the entry API for more flexibility",
581 self.ty,
582 ),
583 vec![
584 vec![
585 (
587 val.span.shrink_to_hi().with_hi(index.span.lo()),
588 ".insert(".to_string(),
589 ),
590 (
591 index.span.shrink_to_hi().with_hi(rv.span.lo()),
592 ", ".to_string(),
593 ),
594 (rv.span.shrink_to_hi(), ")".to_string()),
595 ],
596 vec![
597 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
599 (
600 val.span.shrink_to_hi().with_hi(index.span.lo()),
601 ".get_mut(".to_string(),
602 ),
603 (
604 index.span.shrink_to_hi().with_hi(place.span.hi()),
605 ") { *val".to_string(),
606 ),
607 (rv.span.shrink_to_hi(), "; }".to_string()),
608 ],
609 vec![
610 (val.span.shrink_to_lo(), "let val = ".to_string()),
612 (
613 val.span.shrink_to_hi().with_hi(index.span.lo()),
614 ".entry(".to_string(),
615 ),
616 (
617 index.span.shrink_to_hi().with_hi(rv.span.lo()),
618 ").or_insert(".to_string(),
619 ),
620 (rv.span.shrink_to_hi(), ")".to_string()),
621 ],
622 ],
623 Applicability::MachineApplicable,
624 );
625 self.suggested = true;
626 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
627 && let hir::ExprKind::Index(val, index, _) = receiver.kind
628 && receiver.span == self.assign_span
629 {
630 self.err.multipart_suggestion(
632 format!("to modify a `{}` use `.get_mut()`", self.ty),
633 vec![
634 (val.span.shrink_to_lo(), "if let Some(val) = ".to_string()),
635 (
636 val.span.shrink_to_hi().with_hi(index.span.lo()),
637 ".get_mut(".to_string(),
638 ),
639 (
640 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
641 ") { val".to_string(),
642 ),
643 (sp.shrink_to_hi(), "; }".to_string()),
644 ],
645 Applicability::MachineApplicable,
646 );
647 self.suggested = true;
648 }
649 }
650 }
651 let def_id = self.body.source.def_id();
652 let Some(local_def_id) = def_id.as_local() else { return };
653 let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
654
655 let mut v = SuggestIndexOperatorAlternativeVisitor {
656 assign_span: span,
657 err,
658 ty,
659 suggested: false,
660 };
661 v.visit_body(&body);
662 if !v.suggested {
663 err.help(format!(
664 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
665 ));
666 }
667 }
668 }
669
670 fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
680 if self.body.local_kind(local) != LocalKind::Arg {
681 return (false, false, None);
682 }
683 let my_def = self.body.source.def_id();
684 let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap());
685 let Some(td) =
686 self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
687 else {
688 return (false, false, None);
689 };
690 (
691 true,
692 td.is_local(),
693 td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) {
694 Node::Item(hir::Item {
695 kind: hir::ItemKind::Trait(_, _, _, _, _, items), ..
696 }) => {
697 let mut f_in_trait_opt = None;
698 for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
699 let hi = fi.hir_id();
700 if !matches!(k, hir::AssocItemKind::Fn { .. }) {
701 continue;
702 }
703 if self.infcx.tcx.hir_name(hi) != self.infcx.tcx.hir_name(my_hir) {
704 continue;
705 }
706 f_in_trait_opt = Some(hi);
707 break;
708 }
709 f_in_trait_opt.and_then(|f_in_trait| {
710 if let Node::TraitItem(ti) = self.infcx.tcx.hir_node(f_in_trait)
711 && let hir::TraitItemKind::Fn(sig, _) = ti.kind
712 && let Some(ty) = sig.decl.inputs.get(local.index() - 1)
713 && let hir::TyKind::Ref(_, mut_ty) = ty.kind
714 && let hir::Mutability::Not = mut_ty.mutbl
715 && sig.decl.implicit_self.has_implicit_self()
716 {
717 Some(ty.span)
718 } else {
719 None
720 }
721 })
722 }
723 _ => None,
724 }),
725 )
726 }
727
728 fn construct_mut_suggestion_for_local_binding_patterns(
729 &self,
730 err: &mut Diag<'_>,
731 local: Local,
732 ) {
733 let local_decl = &self.body.local_decls[local];
734 debug!("local_decl: {:?}", local_decl);
735 let pat_span = match *local_decl.local_info() {
736 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
737 binding_mode: BindingMode(ByRef::No, Mutability::Not),
738 opt_ty_info: _,
739 opt_match_place: _,
740 pat_span,
741 })) => pat_span,
742 _ => local_decl.source_info.span,
743 };
744
745 let def_id = self.body.source.def_id();
752 if let Some(local_def_id) = def_id.as_local()
753 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
754 && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
755 && let node = self.infcx.tcx.hir_node(hir_id)
756 && let hir::Node::LetStmt(hir::LetStmt {
757 pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
758 ..
759 })
760 | hir::Node::Param(Param {
761 pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
762 ..
763 }) = node
764 {
765 err.multipart_suggestion(
766 "consider changing this to be mutable",
767 vec![
768 (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
769 (
770 local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
771 ")".to_string(),
772 ),
773 ],
774 Applicability::MachineApplicable,
775 );
776 return;
777 }
778
779 err.span_suggestion_verbose(
780 local_decl.source_info.span.shrink_to_lo(),
781 "consider changing this to be mutable",
782 "mut ",
783 Applicability::MachineApplicable,
784 );
785 }
786
787 fn show_mutating_upvar(
789 &self,
790 tcx: TyCtxt<'_>,
791 closure_local_def_id: hir::def_id::LocalDefId,
792 the_place_err: PlaceRef<'tcx>,
793 err: &mut Diag<'_>,
794 ) {
795 let tables = tcx.typeck(closure_local_def_id);
796 if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
797 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
798 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
799 let root_hir_id = upvar_id.var_path.hir_id;
800 let captured_places =
803 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
804
805 let origin_projection = closure_kind_origin
806 .projections
807 .iter()
808 .map(|proj| proj.kind)
809 .collect::<Vec<_>>();
810 let mut capture_reason = String::new();
811 for captured_place in captured_places {
812 let captured_place_kinds = captured_place
813 .place
814 .projections
815 .iter()
816 .map(|proj| proj.kind)
817 .collect::<Vec<_>>();
818 if rustc_middle::ty::is_ancestor_or_same_capture(
819 &captured_place_kinds,
820 &origin_projection,
821 ) {
822 match captured_place.info.capture_kind {
823 ty::UpvarCapture::ByRef(
824 ty::BorrowKind::Mutable | ty::BorrowKind::UniqueImmutable,
825 ) => {
826 capture_reason = format!("mutable borrow of `{upvar}`");
827 }
828 ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
829 capture_reason = format!("possible mutation of `{upvar}`");
830 }
831 _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
832 }
833 break;
834 }
835 }
836 if capture_reason.is_empty() {
837 bug!("upvar `{upvar}` borrowed, but cannot find reason");
838 }
839 capture_reason
840 } else {
841 bug!("not an upvar")
842 };
843 if let Some(place_name) = self.describe_place(the_place_err) {
849 err.span_label(
850 *span,
851 format!("calling `{place_name}` requires mutable binding due to {reason}"),
852 );
853 } else if span.from_expansion() {
854 err.span_label(
855 *span,
856 format!("a call in this macro requires a mutable binding due to {reason}",),
857 );
858 }
859 }
860 }
861
862 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span) {
865 use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
866 use hir::{BorrowKind, Expr};
867
868 let tcx = self.infcx.tcx;
869 struct Finder {
870 span: Span,
871 }
872
873 impl<'tcx> Visitor<'tcx> for Finder {
874 type Result = ControlFlow<&'tcx Expr<'tcx>>;
875 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
876 if e.span == self.span {
877 ControlFlow::Break(e)
878 } else {
879 hir::intravisit::walk_expr(self, e)
880 }
881 }
882 }
883 if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id())
884 && let Block(block, _) = body.value.kind
885 {
886 if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
890 && let Call(_, [expr]) = expr.kind
891 {
892 match expr.kind {
893 MethodCall(path_segment, _, _, span) => {
894 let opt_suggestions = tcx
897 .typeck(path_segment.hir_id.owner.def_id)
898 .type_dependent_def_id(expr.hir_id)
899 .and_then(|def_id| tcx.impl_of_method(def_id))
900 .map(|def_id| tcx.associated_items(def_id))
901 .map(|assoc_items| {
902 assoc_items
903 .in_definition_order()
904 .map(|assoc_item_def| assoc_item_def.ident(tcx))
905 .filter(|&ident| {
906 let original_method_ident = path_segment.ident;
907 original_method_ident != ident
908 && ident.as_str().starts_with(
909 &original_method_ident.name.to_string(),
910 )
911 })
912 .map(|ident| format!("{ident}()"))
913 .peekable()
914 });
915
916 if let Some(mut suggestions) = opt_suggestions
917 && suggestions.peek().is_some()
918 {
919 err.span_suggestions(
920 span,
921 "use mutable method",
922 suggestions,
923 Applicability::MaybeIncorrect,
924 );
925 }
926 }
927 AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
928 err.span_suggestion_verbose(
930 expr.span.shrink_to_lo(),
931 "use a mutable iterator instead",
932 "mut ",
933 Applicability::MachineApplicable,
934 );
935 }
936 _ => {}
937 }
938 }
939 }
940 }
941
942 fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
944 err.span_label(sp, format!("cannot {act}"));
945
946 let tcx = self.infcx.tcx;
947 let closure_id = self.mir_hir_id();
948 let closure_span = tcx.def_span(self.mir_def_id());
949 let fn_call_id = tcx.parent_hir_id(closure_id);
950 let node = tcx.hir_node(fn_call_id);
951 let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
952 let mut look_at_return = true;
953
954 let get_call_details = || {
957 let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
958 return None;
959 };
960
961 let typeck_results = tcx.typeck(def_id);
962
963 match kind {
964 hir::ExprKind::Call(expr, args) => {
965 if let Some(ty::FnDef(def_id, _)) =
966 typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
967 {
968 Some((*def_id, expr.span, *args))
969 } else {
970 None
971 }
972 }
973 hir::ExprKind::MethodCall(_, _, args, span) => typeck_results
974 .type_dependent_def_id(*hir_id)
975 .map(|def_id| (def_id, *span, *args)),
976 _ => None,
977 }
978 };
979
980 if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
983 let arg_pos = call_args
984 .iter()
985 .enumerate()
986 .filter(|(_, arg)| arg.hir_id == closure_id)
987 .map(|(pos, _)| pos)
988 .next();
989
990 let arg = match tcx.hir_get_if_local(callee_def_id) {
991 Some(
992 hir::Node::Item(hir::Item {
993 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
994 })
995 | hir::Node::TraitItem(hir::TraitItem {
996 ident,
997 kind: hir::TraitItemKind::Fn(sig, _),
998 ..
999 })
1000 | hir::Node::ImplItem(hir::ImplItem {
1001 ident,
1002 kind: hir::ImplItemKind::Fn(sig, _),
1003 ..
1004 }),
1005 ) => Some(
1006 arg_pos
1007 .and_then(|pos| {
1008 sig.decl.inputs.get(
1009 pos + if sig.decl.implicit_self.has_implicit_self() {
1010 1
1011 } else {
1012 0
1013 },
1014 )
1015 })
1016 .map(|arg| arg.span)
1017 .unwrap_or(ident.span),
1018 ),
1019 _ => None,
1020 };
1021 if let Some(span) = arg {
1022 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1023 err.span_label(call_span, "expects `Fn` instead of `FnMut`");
1024 err.span_label(closure_span, "in this closure");
1025 look_at_return = false;
1026 }
1027 }
1028
1029 if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
1030 match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
1033 hir::Node::Item(hir::Item {
1034 kind: hir::ItemKind::Fn { ident, sig, .. }, ..
1035 })
1036 | hir::Node::TraitItem(hir::TraitItem {
1037 ident,
1038 kind: hir::TraitItemKind::Fn(sig, _),
1039 ..
1040 })
1041 | hir::Node::ImplItem(hir::ImplItem {
1042 ident,
1043 kind: hir::ImplItemKind::Fn(sig, _),
1044 ..
1045 }) => {
1046 err.span_label(ident.span, "");
1047 err.span_label(
1048 sig.decl.output.span(),
1049 "change this to return `FnMut` instead of `Fn`",
1050 );
1051 err.span_label(closure_span, "in this closure");
1052 }
1053 _ => {}
1054 }
1055 }
1056 }
1057
1058 fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
1059 let source = self.body.source;
1060 if let InstanceKind::Item(def_id) = source.instance
1061 && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
1062 self.infcx.tcx.hir_get_if_local(def_id)
1063 && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
1064 && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
1065 {
1066 let mut cur_expr = expr;
1067 while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
1068 if path_segment.ident.name == sym::iter {
1069 let res = self
1071 .infcx
1072 .tcx
1073 .typeck(path_segment.hir_id.owner.def_id)
1074 .type_dependent_def_id(cur_expr.hir_id)
1075 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
1076 .map(|def_id| self.infcx.tcx.associated_items(def_id))
1077 .map(|assoc_items| {
1078 assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
1079 });
1080
1081 if let Some(mut res) = res
1082 && res.peek().is_some()
1083 {
1084 err.span_suggestion_verbose(
1085 path_segment.ident.span,
1086 "you may want to use `iter_mut` here",
1087 "iter_mut",
1088 Applicability::MaybeIncorrect,
1089 );
1090 }
1091 break;
1092 } else {
1093 cur_expr = recv;
1094 }
1095 }
1096 }
1097 }
1098
1099 fn find_assignments(&self, local: Local) -> Vec<Location> {
1102 use rustc_middle::mir::visit::Visitor;
1103
1104 struct FindLocalAssignmentVisitor {
1105 needle: Local,
1106 locations: Vec<Location>,
1107 }
1108
1109 impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
1110 fn visit_local(
1111 &mut self,
1112 local: Local,
1113 place_context: PlaceContext,
1114 location: Location,
1115 ) {
1116 if self.needle != local {
1117 return;
1118 }
1119
1120 if place_context.is_place_assignment() {
1121 self.locations.push(location);
1122 }
1123 }
1124 }
1125
1126 let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
1127 visitor.visit_body(self.body);
1128 visitor.locations
1129 }
1130
1131 fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
1132 let local_decl = &self.body.local_decls[local];
1133
1134 let (pointer_sigil, pointer_desc) =
1135 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
1136
1137 let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1138
1139 if is_trait_sig && !is_local {
1140 err.span_label(
1142 local_decl.source_info.span,
1143 format!("this is an immutable {pointer_desc}"),
1144 );
1145 return;
1146 }
1147 let decl_span = local_decl.source_info.span;
1148
1149 let amp_mut_sugg = match *local_decl.local_info() {
1150 LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
1151 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
1152 let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
1153 Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
1154 }
1155
1156 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1157 binding_mode: BindingMode(ByRef::No, _),
1158 opt_ty_info,
1159 ..
1160 })) => {
1161 let opt_assignment_rhs_span =
1163 self.find_assignments(local).first().map(|&location| {
1164 if let Some(mir::Statement {
1165 source_info: _,
1166 kind:
1167 mir::StatementKind::Assign(box (
1168 _,
1169 mir::Rvalue::Use(mir::Operand::Copy(place)),
1170 )),
1171 }) = self.body[location.block].statements.get(location.statement_index)
1172 {
1173 self.body.local_decls[place.local].source_info.span
1174 } else {
1175 self.body.source_info(location).span
1176 }
1177 });
1178 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
1179 Some(DesugaringKind::ForLoop) => {
1181 let span = opt_assignment_rhs_span.unwrap();
1182 self.suggest_similar_mut_method_for_for_loop(err, span);
1183 err.span_label(
1184 span,
1185 format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1186 );
1187 None
1188 }
1189 Some(_) => None,
1191 None if opt_assignment_rhs_span
1193 .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
1194 {
1195 None
1196 }
1197 None => {
1198 if name != kw::SelfLower {
1199 suggest_ampmut(
1200 self.infcx.tcx,
1201 local_decl.ty,
1202 decl_span,
1203 opt_assignment_rhs_span,
1204 opt_ty_info,
1205 )
1206 } else {
1207 match local_decl.local_info() {
1208 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1209 opt_ty_info: None,
1210 ..
1211 })) => {
1212 let (span, sugg) =
1213 suggest_ampmut_self(self.infcx.tcx, decl_span);
1214 Some(AmpMutSugg {
1215 has_sugg: true,
1216 span,
1217 suggestion: sugg,
1218 additional: None,
1219 })
1220 }
1221 _ => suggest_ampmut(
1223 self.infcx.tcx,
1224 local_decl.ty,
1225 decl_span,
1226 opt_assignment_rhs_span,
1227 opt_ty_info,
1228 ),
1229 }
1230 }
1231 }
1232 }
1233 }
1234
1235 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1236 binding_mode: BindingMode(ByRef::Yes(_), _),
1237 ..
1238 })) => {
1239 let pattern_span: Span = local_decl.source_info.span;
1240 suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
1241 has_sugg: true,
1242 span,
1243 suggestion: "mut ".to_owned(),
1244 additional: None,
1245 })
1246 }
1247
1248 _ => unreachable!(),
1249 };
1250
1251 match amp_mut_sugg {
1252 Some(AmpMutSugg {
1253 has_sugg: true,
1254 span: err_help_span,
1255 suggestion: suggested_code,
1256 additional,
1257 }) => {
1258 let mut sugg = vec![(err_help_span, suggested_code)];
1259 if let Some(s) = additional {
1260 sugg.push(s);
1261 }
1262
1263 if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
1264 {
1265 err.multipart_suggestion_verbose(
1266 format!(
1267 "consider changing this to be a mutable {pointer_desc}{}",
1268 if is_trait_sig {
1269 " in the `impl` method and the `trait` definition"
1270 } else {
1271 ""
1272 }
1273 ),
1274 sugg,
1275 Applicability::MachineApplicable,
1276 );
1277 }
1278 }
1279 Some(AmpMutSugg {
1280 has_sugg: false, span: err_label_span, suggestion: message, ..
1281 }) => {
1282 let def_id = self.body.source.def_id();
1283 let hir_id = if let Some(local_def_id) = def_id.as_local()
1284 && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
1285 {
1286 BindingFinder { span: err_label_span }.visit_body(&body).break_value()
1287 } else {
1288 None
1289 };
1290
1291 if let Some(hir_id) = hir_id
1292 && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
1293 {
1294 let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
1295 if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
1296 && let Some(expr) = local.init
1297 && let ty = tables.node_type_opt(expr.hir_id)
1298 && let Some(ty) = ty
1299 && let ty::Ref(..) = ty.kind()
1300 {
1301 match self
1302 .infcx
1303 .type_implements_trait_shallow(
1304 clone_trait,
1305 ty.peel_refs(),
1306 self.infcx.param_env,
1307 )
1308 .as_deref()
1309 {
1310 Some([]) => {
1311 }
1320 None => {
1321 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
1322 expr.kind
1323 && segment.ident.name == sym::clone
1324 {
1325 err.span_help(
1326 span,
1327 format!(
1328 "`{}` doesn't implement `Clone`, so this call clones \
1329 the reference `{ty}`",
1330 ty.peel_refs(),
1331 ),
1332 );
1333 }
1334 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
1336 self.infcx.tcx,
1337 clone_trait,
1338 [ty.peel_refs()],
1339 ));
1340 let obligation = traits::Obligation::new(
1341 self.infcx.tcx,
1342 traits::ObligationCause::dummy(),
1343 self.infcx.param_env,
1344 trait_ref,
1345 );
1346 self.infcx.err_ctxt().suggest_derive(
1347 &obligation,
1348 err,
1349 trait_ref.upcast(self.infcx.tcx),
1350 );
1351 }
1352 Some(errors) => {
1353 if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
1354 expr.kind
1355 && segment.ident.name == sym::clone
1356 {
1357 err.span_help(
1358 span,
1359 format!(
1360 "`{}` doesn't implement `Clone` because its \
1361 implementations trait bounds could not be met, so \
1362 this call clones the reference `{ty}`",
1363 ty.peel_refs(),
1364 ),
1365 );
1366 err.note(format!(
1367 "the following trait bounds weren't met: {}",
1368 errors
1369 .iter()
1370 .map(|e| e.obligation.predicate.to_string())
1371 .collect::<Vec<_>>()
1372 .join("\n"),
1373 ));
1374 }
1375 for error in errors {
1377 if let traits::FulfillmentErrorCode::Select(
1378 traits::SelectionError::Unimplemented,
1379 ) = error.code
1380 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
1381 pred,
1382 )) = error.obligation.predicate.kind().skip_binder()
1383 {
1384 self.infcx.err_ctxt().suggest_derive(
1385 &error.obligation,
1386 err,
1387 error.obligation.predicate.kind().rebind(pred),
1388 );
1389 }
1390 }
1391 }
1392 }
1393 }
1394 let (changing, span, sugg) = match local.ty {
1395 Some(ty) => ("changing", ty.span, message),
1396 None => {
1397 ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
1398 }
1399 };
1400 err.span_suggestion_verbose(
1401 span,
1402 format!("consider {changing} this binding's type"),
1403 sugg,
1404 Applicability::HasPlaceholders,
1405 );
1406 } else {
1407 err.span_label(
1408 err_label_span,
1409 format!("consider changing this binding's type to be: `{message}`"),
1410 );
1411 }
1412 }
1413 None => {}
1414 }
1415 }
1416}
1417
1418struct BindingFinder {
1419 span: Span,
1420}
1421
1422impl<'tcx> Visitor<'tcx> for BindingFinder {
1423 type Result = ControlFlow<hir::HirId>;
1424 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
1425 if let hir::StmtKind::Let(local) = s.kind
1426 && local.pat.span == self.span
1427 {
1428 ControlFlow::Break(local.hir_id)
1429 } else {
1430 hir::intravisit::walk_stmt(self, s)
1431 }
1432 }
1433
1434 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result {
1435 if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat
1436 && *span == self.span
1437 {
1438 ControlFlow::Break(param.hir_id)
1439 } else {
1440 ControlFlow::Continue(())
1441 }
1442 }
1443}
1444
1445fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1446 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1447
1448 match *local_decl.local_info() {
1449 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1451 binding_mode: BindingMode(ByRef::No, Mutability::Not),
1452 ..
1453 })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1454 LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1455 kind == hir::ImplicitSelfKind::RefMut
1461 }
1462 _ if Some(kw::SelfLower) == local_name => {
1463 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1467 }
1468 _ => false,
1469 }
1470}
1471
1472fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
1473 match tcx.sess.source_map().span_to_snippet(span) {
1474 Ok(snippet) if snippet.ends_with("self") => {
1475 (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
1476 }
1477 _ => (span, "&mut self".to_string()),
1478 }
1479}
1480
1481struct AmpMutSugg {
1482 has_sugg: bool,
1483 span: Span,
1484 suggestion: String,
1485 additional: Option<(Span, String)>,
1486}
1487
1488fn suggest_ampmut<'tcx>(
1504 tcx: TyCtxt<'tcx>,
1505 decl_ty: Ty<'tcx>,
1506 decl_span: Span,
1507 opt_assignment_rhs_span: Option<Span>,
1508 opt_ty_info: Option<Span>,
1509) -> Option<AmpMutSugg> {
1510 if let Some(rhs_span) = opt_assignment_rhs_span
1519 && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
1520 && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
1521 {
1522 if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
1524 let const_idx = rhs_str.find("const").unwrap() as u32;
1525 let const_span = rhs_span
1526 .with_lo(rhs_span.lo() + BytePos(const_idx))
1527 .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
1528
1529 return Some(AmpMutSugg {
1530 has_sugg: true,
1531 span: const_span,
1532 suggestion: "mut".to_owned(),
1533 additional: None,
1534 });
1535 }
1536
1537 let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
1539 match rest.chars().next() {
1540 Some(c) if c.is_whitespace() => true,
1542 Some('(') => true,
1544 Some('{') => true,
1546 _ => false,
1548 }
1549 } else {
1550 false
1551 };
1552 if !is_mut {
1555 let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
1557
1558 return Some(AmpMutSugg {
1561 has_sugg: true,
1562 span,
1563 suggestion: "mut ".to_owned(),
1564 additional: None,
1565 });
1566 }
1567 }
1568
1569 let (binding_exists, span) = match opt_ty_info {
1570 Some(ty_span) => (true, ty_span),
1574
1575 None => (false, decl_span),
1579 };
1580
1581 if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
1585 && src.starts_with("&'")
1586 && let Some(ws_pos) = src.find(char::is_whitespace)
1588 {
1589 let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1590 Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
1591 } else if binding_exists {
1593 let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1595 Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
1596 } else {
1597 let ty = decl_ty.builtin_deref(true).unwrap();
1600
1601 Some(AmpMutSugg {
1602 has_sugg: false,
1603 span,
1604 suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
1605 additional: None,
1606 })
1607 }
1608}
1609
1610fn is_closure_like(ty: Ty<'_>) -> bool {
1612 ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
1613}
1614
1615fn get_mut_span_in_struct_field<'tcx>(
1623 tcx: TyCtxt<'tcx>,
1624 ty: Ty<'tcx>,
1625 field: FieldIdx,
1626) -> Option<Span> {
1627 if let ty::Ref(_, ty, _) = ty.kind()
1629 && let ty::Adt(def, _) = ty.kind()
1630 && let field = def.all_fields().nth(field.index())?
1631 && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?)
1634 && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1635 {
1636 return Some(lt.ident.span.between(ty.span));
1637 }
1638
1639 None
1640}
1641
1642fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
1644 let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
1645 if let Some(rest) = pattern_str.strip_prefix("ref")
1646 && rest.starts_with(rustc_lexer::is_whitespace)
1647 {
1648 let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
1649 Some(span)
1650 } else {
1651 None
1652 }
1653}