3 use rustc_middle
::hir
::map
::Map
;
4 use rustc_middle
::mir
::{Mutability, Place, PlaceRef, ProjectionElem}
;
5 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
9 self, BindingForm
, ClearCrossCrate
, ImplicitSelfKind
, Local
, LocalDecl
, LocalInfo
,
13 use rustc_span
::source_map
::DesugaringKind
;
14 use rustc_span
::symbol
::{kw, Symbol}
;
15 use rustc_span
::{BytePos, Span}
;
17 use crate::diagnostics
::BorrowedContentSource
;
18 use crate::MirBorrowckCtxt
;
19 use rustc_const_eval
::util
::collect_writes
::FindAssignments
;
20 use rustc_errors
::{Applicability, Diagnostic}
;
22 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
23 pub(crate) enum AccessKind
{
28 impl<'a
, 'tcx
> MirBorrowckCtxt
<'a
, 'tcx
> {
29 pub(crate) fn report_mutability_error(
31 access_place
: Place
<'tcx
>,
33 the_place_err
: PlaceRef
<'tcx
>,
34 error_access
: AccessKind
,
38 "report_mutability_error(\
39 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
41 access_place
, span
, the_place_err
, error_access
, location
,
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
);
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();
57 let name
= self.local_names
[local
].expect("immutable unnamed local");
58 reason
= format
!(", as `{name}` is not declared as mutable");
64 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
66 debug_assert
!(is_closure_or_generator(
67 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
70 let imm_borrow_derefed
= self.upvars
[upvar_index
.index()]
74 .any(|ty
| matches
!(ty
.kind(), ty
::Ref(.., hir
::Mutability
::Not
)));
76 // If the place is immutable then:
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.
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();
90 let name
= self.upvars
[upvar_index
.index()].place
.to_string(self.infcx
.tcx
);
91 reason
= format
!(", as `{name}` is not declared as mutable");
96 PlaceRef { local, projection: [ProjectionElem::Deref] }
97 if self.body
.local_decls
[local
].is_ref_for_guard() =>
99 item_msg
= access_place_desc
;
100 reason
= ", as it is immutable for the pattern guard".to_string();
102 PlaceRef { local, projection: [ProjectionElem::Deref] }
103 if self.body
.local_decls
[local
].is_ref_to_static() =>
105 if access_place
.projection
.len() == 1 {
106 item_msg
= format
!("immutable static item {access_place_desc}");
107 reason
= String
::new();
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");
115 bug
!("is_ref_to_static return true, but not ref to static?");
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()
124 item_msg
= access_place_desc
;
126 self.body
.local_decls
[ty
::CAPTURE_STRUCT_LOCAL
].ty
.is_region_ptr()
128 debug_assert
!(is_closure_or_generator(
131 the_place_err
.projection
,
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()
141 ", as `Fn` closures cannot mutate their captured variables".to_string()
144 let source
= self.borrowed_content_source(PlaceRef
{
145 local
: the_place_err
.local
,
146 projection
: proj_base
,
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}")
159 item_msg
= format
!("data in {pointer_type}");
160 reason
= String
::new();
170 ProjectionElem
::Index(_
)
171 | ProjectionElem
::ConstantIndex { .. }
172 | ProjectionElem
::Subslice { .. }
173 | ProjectionElem
::Downcast(..),
175 } => bug
!("Unexpected immutable place."),
178 debug
!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg
, reason
);
180 // `act` and `acted_on` are strings that let us abstract over
181 // the verbs used in some diagnostic messages.
185 let span
= match error_access
{
186 AccessKind
::Mutate
=> {
187 err
= self.cannot_assign(span
, &(item_msg
+ &reason
));
189 acted_on
= "written";
192 AccessKind
::MutableBorrow
=> {
193 act
= "borrow as mutable";
194 acted_on
= "borrowed as mutable";
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(
202 "mutable borrow occurs due to use of {} in closure",
203 self.describe_any_place(access_place
.as_ref()),
211 debug
!("report_mutability_error: act={:?}, acted_on={:?}", act
, acted_on
);
213 match the_place_err
{
214 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
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).
225 ProjectionElem
::Deref
,
226 ProjectionElem
::Field(field
, _
),
227 ProjectionElem
::Deref
,
230 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
232 if let Some(span
) = get_mut_span_in_struct_field(
234 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
,
237 err
.span_suggestion_verbose(
239 "consider changing this to be mutable",
241 Applicability
::MaybeIncorrect
,
246 // Suggest removing a `&mut` from the use of a mutable reference.
247 PlaceRef { local, projection: [] }
252 .map(|l
| mut_borrow_of_mutable_ref(l
, self.local_names
[local
]))
255 let decl
= &self.body
.local_decls
[local
];
256 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
257 if let Some(mir
::Statement
{
260 mir
::StatementKind
::Assign(box (
264 mir
::BorrowKind
::Mut { allow_two_phase_borrow: false }
,
269 }) = &self.body
[location
.block
].statements
.get(location
.statement_index
)
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
),
280 err
.span_note(sp
, "the binding is already a mutable borrow");
284 decl
.source_info
.span
,
285 "the binding is already a mutable borrow",
290 self.infcx
.tcx
.sess
.source_map().span_to_snippet(source_info
.span
)
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",
299 Applicability
::MachineApplicable
,
302 // This can occur with things like `(&mut self).foo()`.
303 err
.span_help(source_info
.span
, "try removing `&mut` here");
306 err
.span_help(source_info
.span
, "try removing `&mut` here");
308 } else if decl
.mutability
== Mutability
::Not
311 Some(box LocalInfo
::User(ClearCrossCrate
::Set(BindingForm
::ImplicitSelf(
312 ImplicitSelfKind
::MutRef
316 err
.span_suggestion_verbose(
317 decl
.source_info
.span
.shrink_to_lo(),
318 "consider making the binding mutable",
320 Applicability
::MachineApplicable
,
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() =>
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
334 let local_decl
= &self.body
.local_decls
[local
];
335 assert_eq
!(local_decl
.mutability
, Mutability
::Not
);
337 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
339 local_decl
.source_info
.span
,
340 "consider changing this to be mutable",
341 format
!("mut {}", self.local_names
[local
].unwrap()),
342 Applicability
::MachineApplicable
,
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
);
350 // Also suggest adding mut for upvars
353 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
355 debug_assert
!(is_closure_or_generator(
356 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
359 let captured_place
= &self.upvars
[upvar_index
.index()].place
;
361 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
363 let upvar_hir_id
= captured_place
.get_root_variable();
365 if let Some(Node
::Binding(pat
)) = self.infcx
.tcx
.hir().find(upvar_hir_id
)
366 && let hir
::PatKind
::Binding(
367 hir
::BindingAnnotation
::Unannotated
,
375 "consider changing this to be mutable",
376 format
!("mut {}", upvar_ident
.name
),
377 Applicability
::MachineApplicable
,
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()
385 self.show_mutating_upvar(tcx
, id
, the_place_err
, &mut err
);
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: [] }
394 if let Ok(snippet
) = self.infcx
.tcx
.sess
.source_map().span_to_snippet(span
) {
395 snippet
.starts_with("&mut ")
401 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
404 "try removing `&mut` here",
406 Applicability
::MaybeIncorrect
,
410 PlaceRef { local, projection: [ProjectionElem::Deref] }
411 if self.body
.local_decls
[local
].is_ref_for_guard() =>
413 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
415 "variables bound in patterns are immutable until the end of the pattern guard",
419 // We want to point out when a `&` can be readily replaced
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() =>
427 let local_decl
= &self.body
.local_decls
[local
];
429 let (pointer_sigil
, pointer_desc
) = if local_decl
.ty
.is_region_ptr() {
432 ("*const", "pointer")
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(_
),
441 let (span
, suggestion
) =
442 suggest_ampmut_self(self.infcx
.tcx
, local_decl
);
443 Some((true, span
, suggestion
))
446 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
447 mir
::VarBindingForm
{
448 binding_mode
: ty
::BindingMode
::BindByValue(_
),
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
{
459 mir
::StatementKind
::Assign(box (
461 mir
::Rvalue
::Use(mir
::Operand
::Copy(place
)),
463 }) = self.body
[location
.block
]
465 .get(location
.statement_index
)
467 self.body
.local_decls
[place
.local
].source_info
.span
469 self.body
.source_info(location
).span
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
);
478 opt_assignment_rhs_span
.unwrap(),
480 "this iterator yields `{SIGIL}` {DESC}s",
481 SIGIL
= pointer_sigil
,
486 // don't create labels for compiler-generated spans
489 let (span
, suggestion
) = if name
!= kw
::SelfLower
{
493 opt_assignment_rhs_span
,
497 match local_decl
.local_info
.as_deref() {
498 Some(LocalInfo
::User(ClearCrossCrate
::Set(
499 mir
::BindingForm
::Var(mir
::VarBindingForm
{
504 suggest_ampmut_self(self.infcx
.tcx
, local_decl
)
506 // explicit self (eg `self: &'a Self`)
510 opt_assignment_rhs_span
,
515 Some((true, span
, suggestion
))
520 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
521 mir
::VarBindingForm
{
522 binding_mode
: ty
::BindingMode
::BindByReference(_
),
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
))
531 box LocalInfo
::User(ClearCrossCrate
::Clear
) => {
532 bug
!("saw cleared local state")
539 Some((true, err_help_span
, suggested_code
)) => {
540 let (is_trait_sig
, local_trait
) = self.is_error_in_trait(local
);
545 "consider changing this to be a mutable {pointer_desc}"
548 Applicability
::MachineApplicable
,
550 } else if let Some(x
) = local_trait
{
554 "consider changing that to be a mutable {pointer_desc}"
557 Applicability
::MachineApplicable
,
561 Some((false, err_label_span
, message
)) => {
562 err
.span_label(err_label_span
, &message
);
569 "`{NAME}` is a `{SIGIL}` {DESC}, \
570 so the data it refers to cannot be {ACTED_ON}",
572 SIGIL
= pointer_sigil
,
582 "cannot {ACT} through `{SIGIL}` {DESC}",
584 SIGIL
= pointer_sigil
,
592 PlaceRef { local, projection: [ProjectionElem::Deref] }
593 if local
== ty
::CAPTURE_STRUCT_LOCAL
&& !self.upvars
.is_empty() =>
595 self.expected_fn_found_fn_mut_call(&mut err
, span
, act
);
598 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] }
=> {
599 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
602 Some(BorrowedContentSource
::OverloadedDeref(ty
)) => {
604 "trait `DerefMut` is required to modify through a dereference, \
605 but it is not implemented for `{ty}`",
608 Some(BorrowedContentSource
::OverloadedIndex(ty
)) => {
610 "trait `IndexMut` is required to modify indexed content, \
611 but it is not implemented for `{ty}`",
619 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
623 self.buffer_error(err
);
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
);
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());
637 self.infcx
.tcx
.impl_of_method(my_def
).and_then(|x
| self.infcx
.tcx
.trait_id_of_impl(x
))
639 return (false, None
);
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
),
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 { .. }
) {
654 if hir_map
.name(hi
) != hir_map
.name(my_hir
) {
657 f_in_trait_opt
= Some(hi
);
660 f_in_trait_opt
.and_then(|f_in_trait
| match hir_map
.find(f_in_trait
) {
661 Some(Node
::TraitItem(hir
::TraitItem
{
663 hir
::TraitItemKind
::Fn(
664 hir
::FnSig { decl: hir::FnDecl { inputs, .. }
, .. },
669 let hir
::Ty { span, .. }
= inputs
[local
.index() - 1];
680 // point to span of upvar making closure call require mutable borrow
681 fn show_mutating_upvar(
684 id
: hir
::def_id
::DefId
,
685 the_place_err
: PlaceRef
<'tcx
>,
686 err
: &mut Diagnostic
,
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
)
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();
700 let origin_projection
= closure_kind_origin
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
711 .map(|proj
| proj
.kind
)
712 .collect
::<Vec
<_
>>();
713 if rustc_middle
::ty
::is_ancestor_or_same_capture(
714 &captured_place_kinds
,
717 match captured_place
.info
.capture_kind
{
718 ty
::UpvarCapture
::ByRef(
719 ty
::BorrowKind
::MutBorrow
| ty
::BorrowKind
::UniqueImmBorrow
,
721 capture_reason
= format
!("mutable borrow of `{upvar}`");
723 ty
::UpvarCapture
::ByValue
=> {
724 capture_reason
= format
!("possible mutation of `{upvar}`");
726 _
=> bug
!("upvar `{upvar}` borrowed, but not mutably"),
731 if capture_reason
.is_empty() {
732 bug
!("upvar `{upvar}` borrowed, but cannot find reason");
741 "calling `{}` requires mutable binding due to {}",
742 self.describe_place(the_place_err
).unwrap(),
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
) {
754 ExprKind
::{Block, Call, DropTemps, Match, MethodCall}
,
755 HirId
, ImplItem
, ImplItemKind
, Item
, ItemKind
,
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), .. }
)) => {
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
) {
807 ) = hir_map
.body(fn_body_id
).value
.kind
809 let opt_suggestions
= path_segment
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
))
817 .in_definition_order()
818 .map(|assoc_item_def
| assoc_item_def
.ident(self.infcx
.tcx
))
820 let original_method_ident
= path_segment
.ident
;
821 original_method_ident
!= ident
824 .starts_with(&original_method_ident
.name
.to_string())
826 .map(|ident
| format
!("{ident}()"))
830 if let Some(mut suggestions
) = opt_suggestions
831 && suggestions
.peek().is_some()
833 err
.span_suggestions(
835 "use mutable method",
837 Applicability
::MaybeIncorrect
,
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}"));
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
{
860 .filter(|(_
, arg
)| arg
.span
== self.body
.span
)
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())
868 let arg
= match hir
.get_if_local(*def_id
) {
870 hir
::Node
::Item(hir
::Item
{
871 ident
, kind
: hir
::ItemKind
::Fn(sig
, ..), ..
873 | hir
::Node
::TraitItem(hir
::TraitItem
{
875 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
878 | hir
::Node
::ImplItem(hir
::ImplItem
{
880 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
887 pos
+ if sig
.decl
.implicit_self
.has_implicit_self() {
895 .unwrap_or(ident
.span
),
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");
905 look_at_return
= false;
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
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
{
917 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
920 | hir
::Node
::ImplItem(hir
::ImplItem
{
922 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
925 err
.span_label(ident
.span
, "");
927 sig
.decl
.output
.span(),
928 "change this to return `FnMut` instead of `Fn`",
930 err
.span_label(self.body
.span
, "in this closure");
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());
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
), ..
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`.
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
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
))
966 fn suggest_ampmut_self
<'tcx
>(
968 local_decl
: &mir
::LocalDecl
<'tcx
>,
969 ) -> (Span
, String
) {
970 let sp
= local_decl
.source_info
.span
;
973 match tcx
.sess
.source_map().span_to_snippet(sp
) {
975 let lt_pos
= snippet
.find('
\''
);
976 if let Some(lt_pos
) = lt_pos
{
977 format
!("&{}mut self", &snippet
[lt_pos
..snippet
.len() - 4])
979 "&mut self".to_string()
982 _
=> "&mut self".to_string(),
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:
990 // let ident [: Type] [= RightHandSideExpression];
991 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
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).
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
>(
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
)
1011 let is_mutbl
= |ty
: &str| -> bool
{
1012 if let Some(rest
) = ty
.strip_prefix("mut") {
1013 match rest
.chars().next() {
1015 Some(c
) if c
.is_whitespace() => true,
1020 // e.g. `&mutablevar`
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();
1031 return (assignment_rhs_span
, format
!("&{lt_name} mut {ty}"));
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}"));
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
,
1046 // otherwise, just highlight the span associated with
1047 // the (MIR) LocalDecl.
1048 None
=> local_decl
.source_info
.span
,
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
))
1054 let lt_name
= &src
[1..ws_pos
];
1055 let ty
= &src
[ws_pos
..];
1056 return (highlight_span
, format
!("&{} mut{}", lt_name
, ty
));
1059 let ty_mut
= local_decl
.ty
.builtin_deref(true).unwrap();
1060 assert_eq
!(ty_mut
.mutbl
, hir
::Mutability
::Not
);
1063 if local_decl
.ty
.is_region_ptr() {
1064 format
!("&mut {}", ty_mut
.ty
)
1066 format
!("*mut {}", ty_mut
.ty
)
1071 fn is_closure_or_generator(ty
: Ty
<'_
>) -> bool
{
1072 ty
.is_closure() || ty
.is_generator()
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.
1079 /// LL | s: &'a String
1080 /// | ^^^ returns a span taking up the space here
1082 fn get_mut_span_in_struct_field
<'tcx
>(
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
1098 return Some(lt
.span
.between(ty
.span
));
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()..]);