3 use rustc_middle
::hir
::map
::Map
;
4 use rustc_middle
::mir
::{Mutability, Place, PlaceRef, ProjectionElem}
;
5 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
8 mir
::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location}
,
10 use rustc_span
::source_map
::DesugaringKind
;
11 use rustc_span
::symbol
::{kw, Symbol}
;
14 use crate::borrow_check
::diagnostics
::BorrowedContentSource
;
15 use crate::borrow_check
::MirBorrowckCtxt
;
16 use crate::util
::collect_writes
::FindAssignments
;
17 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
19 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
20 pub(crate) enum AccessKind
{
25 impl<'a
, 'tcx
> MirBorrowckCtxt
<'a
, 'tcx
> {
26 pub(crate) fn report_mutability_error(
28 access_place
: Place
<'tcx
>,
30 the_place_err
: PlaceRef
<'tcx
>,
31 error_access
: AccessKind
,
35 "report_mutability_error(\
36 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
38 access_place
, span
, the_place_err
, error_access
, location
,
44 let mut opt_source
= None
;
45 let access_place_desc
= self.describe_place(access_place
.as_ref());
46 debug
!("report_mutability_error: access_place_desc={:?}", access_place_desc
);
49 PlaceRef { local, projection: [] }
=> {
50 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
51 if access_place
.as_local().is_some() {
52 reason
= ", as it is not declared as mutable".to_string();
54 let name
= self.local_names
[local
].expect("immutable unnamed local");
55 reason
= format
!(", as `{}` is not declared as mutable", name
);
61 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
63 debug_assert
!(is_closure_or_generator(
64 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
67 let imm_borrow_derefed
= self.upvars
[upvar_index
.index()]
71 .any(|ty
| matches
!(ty
.kind(), ty
::Ref(.., hir
::Mutability
::Not
)));
73 // If the place is immutable then:
75 // - Either we deref a immutable ref to get to our final place.
76 // - We don't capture derefs of raw ptrs
77 // - Or the final place is immut because the root variable of the capture
78 // isn't marked mut and we should suggest that to the user.
79 if imm_borrow_derefed
{
80 // If we deref an immutable ref then the suggestion here doesn't help.
83 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
84 if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
85 reason
= ", as it is not declared as mutable".to_string();
87 let name
= self.upvars
[upvar_index
.index()].place
.to_string(self.infcx
.tcx
);
88 reason
= format
!(", as `{}` is not declared as mutable", name
);
93 PlaceRef { local, projection: [ProjectionElem::Deref] }
94 if self.body
.local_decls
[local
].is_ref_for_guard() =>
96 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
97 reason
= ", as it is immutable for the pattern guard".to_string();
99 PlaceRef { local, projection: [ProjectionElem::Deref] }
100 if self.body
.local_decls
[local
].is_ref_to_static() =>
102 if access_place
.projection
.len() == 1 {
103 item_msg
= format
!("immutable static item `{}`", access_place_desc
.unwrap());
104 reason
= String
::new();
106 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
107 let local_info
= &self.body
.local_decls
[local
].local_info
;
108 if let Some(box LocalInfo
::StaticRef { def_id, .. }
) = *local_info
{
109 let static_name
= &self.infcx
.tcx
.item_name(def_id
);
110 reason
= format
!(", as `{}` is an immutable static item", static_name
);
112 bug
!("is_ref_to_static return true, but not ref to static?");
116 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] }
=> {
117 if the_place_err
.local
== ty
::CAPTURE_STRUCT_LOCAL
118 && proj_base
.is_empty()
119 && !self.upvars
.is_empty()
121 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
123 self.body
.local_decls
[ty
::CAPTURE_STRUCT_LOCAL
].ty
.is_region_ptr()
125 debug_assert
!(is_closure_or_generator(
128 the_place_err
.projection
,
135 reason
= if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
136 ", as it is a captured variable in a `Fn` closure".to_string()
138 ", as `Fn` closures cannot mutate their captured variables".to_string()
141 let source
= self.borrowed_content_source(PlaceRef
{
142 local
: the_place_err
.local
,
143 projection
: proj_base
,
145 let pointer_type
= source
.describe_for_immutable_place(self.infcx
.tcx
);
146 opt_source
= Some(source
);
147 if let Some(desc
) = access_place_desc
{
148 item_msg
= format
!("`{}`", desc
);
149 reason
= match error_access
{
150 AccessKind
::Mutate
=> format
!(", which is behind {}", pointer_type
),
151 AccessKind
::MutableBorrow
=> {
152 format
!(", as it is behind {}", pointer_type
)
156 item_msg
= format
!("data in {}", pointer_type
);
157 reason
= String
::new();
165 [.., ProjectionElem
::Index(_
)
166 | ProjectionElem
::ConstantIndex { .. }
167 | ProjectionElem
::Subslice { .. }
168 | ProjectionElem
::Downcast(..)],
169 } => bug
!("Unexpected immutable place."),
172 debug
!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg
, reason
);
174 // `act` and `acted_on` are strings that let us abstract over
175 // the verbs used in some diagnostic messages.
179 let span
= match error_access
{
180 AccessKind
::Mutate
=> {
181 err
= self.cannot_assign(span
, &(item_msg
+ &reason
));
183 acted_on
= "written";
186 AccessKind
::MutableBorrow
=> {
187 act
= "borrow as mutable";
188 acted_on
= "borrowed as mutable";
190 let borrow_spans
= self.borrow_spans(span
, location
);
191 let borrow_span
= borrow_spans
.args_or_use();
192 err
= self.cannot_borrow_path_as_mutable_because(borrow_span
, &item_msg
, &reason
);
193 borrow_spans
.var_span_label(
196 "mutable borrow occurs due to use of {} in closure",
197 self.describe_any_place(access_place
.as_ref()),
205 debug
!("report_mutability_error: act={:?}, acted_on={:?}", act
, acted_on
);
207 match the_place_err
{
208 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
210 // This is applicable when we have a deref of a field access to a deref of a local -
211 // something like `*((*_1).0`. The local that we get will be a reference to the
212 // struct we've got a field access of (it must be a reference since there's a deref
213 // after the field access).
217 [proj_base @
.., ProjectionElem
::Deref
, ProjectionElem
::Field(field
, _
), ProjectionElem
::Deref
],
219 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
221 if let Some((span
, message
)) = annotate_struct_field(
223 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
,
228 "consider changing this to be mutable",
230 Applicability
::MaybeIncorrect
,
235 // Suggest removing a `&mut` from the use of a mutable reference.
236 PlaceRef { local, projection: [] }
241 .map(|l
| mut_borrow_of_mutable_ref(l
, self.local_names
[local
]))
244 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
245 err
.span_label(span
, "try removing `&mut` here");
248 // We want to suggest users use `let mut` for local (user
249 // variable) mutations...
250 PlaceRef { local, projection: [] }
251 if self.body
.local_decls
[local
].can_be_made_mutable() =>
253 // ... but it doesn't make sense to suggest it on
254 // variables that are `ref x`, `ref mut x`, `&self`,
255 // or `&mut self` (such variables are simply not
257 let local_decl
= &self.body
.local_decls
[local
];
258 assert_eq
!(local_decl
.mutability
, Mutability
::Not
);
260 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
262 local_decl
.source_info
.span
,
263 "consider changing this to be mutable",
264 format
!("mut {}", self.local_names
[local
].unwrap()),
265 Applicability
::MachineApplicable
,
267 let tcx
= self.infcx
.tcx
;
268 if let ty
::Closure(id
, _
) = the_place_err
.ty(self.body
, tcx
).ty
.kind() {
269 self.show_mutating_upvar(tcx
, id
, the_place_err
, &mut err
);
273 // Also suggest adding mut for upvars
276 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
278 debug_assert
!(is_closure_or_generator(
279 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
282 let captured_place
= &self.upvars
[upvar_index
.index()].place
;
284 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
286 let upvar_hir_id
= captured_place
.get_root_variable();
288 if let Some(Node
::Binding(pat
)) = self.infcx
.tcx
.hir().find(upvar_hir_id
) {
289 if let hir
::PatKind
::Binding(
290 hir
::BindingAnnotation
::Unannotated
,
298 "consider changing this to be mutable",
299 format
!("mut {}", upvar_ident
.name
),
300 Applicability
::MachineApplicable
,
305 let tcx
= self.infcx
.tcx
;
306 if let ty
::Ref(_
, ty
, Mutability
::Mut
) = the_place_err
.ty(self.body
, tcx
).ty
.kind()
308 if let ty
::Closure(id
, _
) = ty
.kind() {
309 self.show_mutating_upvar(tcx
, id
, the_place_err
, &mut err
);
314 // complete hack to approximate old AST-borrowck
315 // diagnostic: if the span starts with a mutable borrow of
316 // a local variable, then just suggest the user remove it.
317 PlaceRef { local: _, projection: [] }
319 if let Ok(snippet
) = self.infcx
.tcx
.sess
.source_map().span_to_snippet(span
) {
320 snippet
.starts_with("&mut ")
326 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
327 err
.span_label(span
, "try removing `&mut` here");
330 PlaceRef { local, projection: [ProjectionElem::Deref] }
331 if self.body
.local_decls
[local
].is_ref_for_guard() =>
333 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
335 "variables bound in patterns are immutable until the end of the pattern guard",
339 // We want to point out when a `&` can be readily replaced
342 // FIXME: can this case be generalized to work for an
343 // arbitrary base for the projection?
344 PlaceRef { local, projection: [ProjectionElem::Deref] }
345 if self.body
.local_decls
[local
].is_user_variable() =>
347 let local_decl
= &self.body
.local_decls
[local
];
349 let (pointer_sigil
, pointer_desc
) = if local_decl
.ty
.is_region_ptr() {
352 ("*const", "pointer")
355 match self.local_names
[local
] {
356 Some(name
) if !local_decl
.from_compiler_desugaring() => {
357 let label
= match local_decl
.local_info
.as_ref().unwrap() {
358 box LocalInfo
::User(ClearCrossCrate
::Set(
359 mir
::BindingForm
::ImplicitSelf(_
),
361 let (span
, suggestion
) =
362 suggest_ampmut_self(self.infcx
.tcx
, local_decl
);
363 Some((true, span
, suggestion
))
366 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
367 mir
::VarBindingForm
{
368 binding_mode
: ty
::BindingMode
::BindByValue(_
),
373 // check if the RHS is from desugaring
374 let locations
= self.body
.find_assignments(local
);
375 let opt_assignment_rhs_span
= locations
377 .map(|&location
| self.body
.source_info(location
).span
);
378 let opt_desugaring_kind
=
379 opt_assignment_rhs_span
.and_then(|span
| span
.desugaring_kind());
380 match opt_desugaring_kind
{
381 // on for loops, RHS points to the iterator part
382 Some(DesugaringKind
::ForLoop(_
)) => {
383 self.suggest_similar_mut_method_for_for_loop(&mut err
);
386 opt_assignment_rhs_span
.unwrap(),
388 "this iterator yields `{SIGIL}` {DESC}s",
389 SIGIL
= pointer_sigil
,
394 // don't create labels for compiler-generated spans
397 let (span
, suggestion
) = suggest_ampmut(
400 opt_assignment_rhs_span
,
403 Some((true, span
, suggestion
))
408 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
409 mir
::VarBindingForm
{
410 binding_mode
: ty
::BindingMode
::BindByReference(_
),
414 let pattern_span
= local_decl
.source_info
.span
;
415 suggest_ref_mut(self.infcx
.tcx
, pattern_span
)
416 .map(|replacement
| (true, pattern_span
, replacement
))
419 box LocalInfo
::User(ClearCrossCrate
::Clear
) => {
420 bug
!("saw cleared local state")
427 Some((true, err_help_span
, suggested_code
)) => {
428 let (is_trait_sig
, local_trait
) = self.is_error_in_trait(local
);
433 "consider changing this to be a mutable {}",
437 Applicability
::MachineApplicable
,
439 } else if let Some(x
) = local_trait
{
443 "consider changing that to be a mutable {}",
447 Applicability
::MachineApplicable
,
451 Some((false, err_label_span
, message
)) => {
452 err
.span_label(err_label_span
, &message
);
459 "`{NAME}` is a `{SIGIL}` {DESC}, \
460 so the data it refers to cannot be {ACTED_ON}",
462 SIGIL
= pointer_sigil
,
472 "cannot {ACT} through `{SIGIL}` {DESC}",
474 SIGIL
= pointer_sigil
,
482 PlaceRef { local, projection: [ProjectionElem::Deref] }
483 if local
== ty
::CAPTURE_STRUCT_LOCAL
&& !self.upvars
.is_empty() =>
485 self.expected_fn_found_fn_mut_call(&mut err
, span
, act
);
488 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] }
=> {
489 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
492 Some(BorrowedContentSource
::OverloadedDeref(ty
)) => {
494 "trait `DerefMut` is required to modify through a dereference, \
495 but it is not implemented for `{}`",
499 Some(BorrowedContentSource
::OverloadedIndex(ty
)) => {
501 "trait `IndexMut` is required to modify indexed content, \
502 but it is not implemented for `{}`",
511 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
515 err
.buffer(&mut self.errors_buffer
);
518 /// User cannot make signature of a trait mutable without changing the
519 /// trait. So we find if this error belongs to a trait and if so we move
520 /// suggestion to the trait or disable it if it is out of scope of this crate
521 fn is_error_in_trait(&self, local
: Local
) -> (bool
, Option
<Span
>) {
522 if self.body
.local_kind(local
) != LocalKind
::Arg
{
523 return (false, None
);
525 let hir_map
= self.infcx
.tcx
.hir();
526 let my_def
= self.body
.source
.def_id();
527 let my_hir
= hir_map
.local_def_id_to_hir_id(my_def
.as_local().unwrap());
528 let td
= if let Some(a
) =
529 self.infcx
.tcx
.impl_of_method(my_def
).and_then(|x
| self.infcx
.tcx
.trait_id_of_impl(x
))
533 return (false, None
);
537 td
.as_local().and_then(|tld
| {
538 let h
= hir_map
.local_def_id_to_hir_id(tld
);
539 match hir_map
.find(h
) {
540 Some(Node
::Item(hir
::Item
{
541 kind
: hir
::ItemKind
::Trait(_
, _
, _
, _
, items
),
544 let mut f_in_trait_opt
= None
;
545 for hir
::TraitItemRef { id: fi, kind: k, .. }
in *items
{
546 let hi
= fi
.hir_id();
547 if !matches
!(k
, hir
::AssocItemKind
::Fn { .. }
) {
550 if hir_map
.name(hi
) != hir_map
.name(my_hir
) {
553 f_in_trait_opt
= Some(hi
);
556 f_in_trait_opt
.and_then(|f_in_trait
| match hir_map
.find(f_in_trait
) {
557 Some(Node
::TraitItem(hir
::TraitItem
{
559 hir
::TraitItemKind
::Fn(
560 hir
::FnSig { decl: hir::FnDecl { inputs, .. }
, .. },
565 let hir
::Ty { span, .. }
= inputs
[local
.index() - 1];
577 // point to span of upvar making closure call require mutable borrow
578 fn show_mutating_upvar(
581 id
: &hir
::def_id
::DefId
,
582 the_place_err
: PlaceRef
<'tcx
>,
583 err
: &mut DiagnosticBuilder
<'_
>,
585 let closure_local_def_id
= id
.expect_local();
586 let tables
= tcx
.typeck(closure_local_def_id
);
587 let closure_hir_id
= tcx
.hir().local_def_id_to_hir_id(closure_local_def_id
);
588 if let Some((span
, closure_kind_origin
)) =
589 &tables
.closure_kind_origins().get(closure_hir_id
)
591 let reason
= if let PlaceBase
::Upvar(upvar_id
) = closure_kind_origin
.base
{
592 let upvar
= ty
::place_to_string_for_capture(tcx
, closure_kind_origin
);
593 let root_hir_id
= upvar_id
.var_path
.hir_id
;
594 // we have a origin for this closure kind starting at this root variable so it's safe to unwrap here
595 let captured_places
= tables
.closure_min_captures
[id
].get(&root_hir_id
).unwrap();
597 let origin_projection
= closure_kind_origin
600 .map(|proj
| proj
.kind
)
601 .collect
::<Vec
<_
>>();
602 let mut capture_reason
= String
::new();
603 for captured_place
in captured_places
{
604 let captured_place_kinds
= captured_place
608 .map(|proj
| proj
.kind
)
609 .collect
::<Vec
<_
>>();
610 if rustc_middle
::ty
::is_ancestor_or_same_capture(
611 &captured_place_kinds
,
614 match captured_place
.info
.capture_kind
{
615 ty
::UpvarCapture
::ByRef(ty
::UpvarBorrow
{
616 kind
: ty
::BorrowKind
::MutBorrow
| ty
::BorrowKind
::UniqueImmBorrow
,
619 capture_reason
= format
!("mutable borrow of `{}`", upvar
);
621 ty
::UpvarCapture
::ByValue(_
) => {
622 capture_reason
= format
!("possible mutation of `{}`", upvar
);
624 _
=> bug
!("upvar `{}` borrowed, but not mutably", upvar
),
629 if capture_reason
.is_empty() {
630 bug
!("upvar `{}` borrowed, but cannot find reason", upvar
);
639 "calling `{}` requires mutable binding due to {}",
640 self.describe_place(the_place_err
).unwrap(),
647 // Attempt to search similar mutable associated items for suggestion.
648 // In the future, attempt in all path but initially for RHS of for_loop
649 fn suggest_similar_mut_method_for_for_loop(&self, err
: &mut DiagnosticBuilder
<'_
>) {
652 ExprKind
::{Block, Call, DropTemps, Match, MethodCall}
,
653 HirId
, ImplItem
, ImplItemKind
, Item
, ItemKind
,
656 fn maybe_body_id_of_fn(hir_map
: &Map
<'tcx
>, id
: HirId
) -> Option
<BodyId
> {
657 match hir_map
.find(id
) {
658 Some(Node
::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }
))
659 | Some(Node
::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. }
)) => {
665 let hir_map
= self.infcx
.tcx
.hir();
666 let mir_body_hir_id
= self.mir_hir_id();
667 if let Some(fn_body_id
) = maybe_body_id_of_fn(&hir_map
, mir_body_hir_id
) {
681 kind
: MethodCall(path_segment
, ..),
697 ) = hir_map
.body(fn_body_id
).value
.kind
699 let opt_suggestions
= path_segment
701 .map(|path_hir_id
| self.infcx
.tcx
.typeck(path_hir_id
.owner
))
702 .and_then(|typeck
| typeck
.type_dependent_def_id(*hir_id
))
703 .and_then(|def_id
| self.infcx
.tcx
.impl_of_method(def_id
))
704 .map(|def_id
| self.infcx
.tcx
.associated_items(def_id
))
707 .in_definition_order()
708 .map(|assoc_item_def
| assoc_item_def
.ident
)
710 let original_method_ident
= path_segment
.ident
;
711 original_method_ident
!= ident
714 .starts_with(&original_method_ident
.name
.to_string())
716 .map(|ident
| format
!("{}()", ident
))
720 if let Some(mut suggestions
) = opt_suggestions
{
721 if suggestions
.peek().is_some() {
722 err
.span_suggestions(
723 path_segment
.ident
.span
,
724 &format
!("use mutable method"),
726 Applicability
::MaybeIncorrect
,
734 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
735 fn expected_fn_found_fn_mut_call(&self, err
: &mut DiagnosticBuilder
<'_
>, sp
: Span
, act
: &str) {
736 err
.span_label(sp
, format
!("cannot {}", act
));
738 let hir
= self.infcx
.tcx
.hir();
739 let closure_id
= self.mir_hir_id();
740 let fn_call_id
= hir
.get_parent_node(closure_id
);
741 let node
= hir
.get(fn_call_id
);
742 let item_id
= hir
.enclosing_body_owner(fn_call_id
);
743 let mut look_at_return
= true;
744 // If we can detect the expression to be an `fn` call where the closure was an argument,
745 // we point at the `fn` definition argument...
746 if let hir
::Node
::Expr(hir
::Expr { kind: hir::ExprKind::Call(func, args), .. }
) = node
{
750 .filter(|(_
, arg
)| arg
.span
== self.body
.span
)
753 let def_id
= hir
.local_def_id(item_id
);
754 let tables
= self.infcx
.tcx
.typeck(def_id
);
755 if let Some(ty
::FnDef(def_id
, _
)) =
756 tables
.node_type_opt(func
.hir_id
).as_ref().map(|ty
| ty
.kind())
758 let arg
= match hir
.get_if_local(*def_id
) {
760 hir
::Node
::Item(hir
::Item
{
761 ident
, kind
: hir
::ItemKind
::Fn(sig
, ..), ..
763 | hir
::Node
::TraitItem(hir
::TraitItem
{
765 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
768 | hir
::Node
::ImplItem(hir
::ImplItem
{
770 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
777 pos
+ if sig
.decl
.implicit_self
.has_implicit_self() {
785 .unwrap_or(ident
.span
),
789 if let Some(span
) = arg
{
790 err
.span_label(span
, "change this to accept `FnMut` instead of `Fn`");
791 err
.span_label(func
.span
, "expects `Fn` instead of `FnMut`");
792 if self.infcx
.tcx
.sess
.source_map().is_multiline(self.body
.span
) {
793 err
.span_label(self.body
.span
, "in this closure");
795 look_at_return
= false;
800 if look_at_return
&& hir
.get_return_block(closure_id
).is_some() {
801 // ...otherwise we are probably in the tail expression of the function, point at the
803 match hir
.get(hir
.get_parent_item(fn_call_id
)) {
804 hir
::Node
::Item(hir
::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }
)
805 | hir
::Node
::TraitItem(hir
::TraitItem
{
807 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
810 | hir
::Node
::ImplItem(hir
::ImplItem
{
812 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
815 err
.span_label(ident
.span
, "");
817 sig
.decl
.output
.span(),
818 "change this to return `FnMut` instead of `Fn`",
820 err
.span_label(self.body
.span
, "in this closure");
828 fn mut_borrow_of_mutable_ref(local_decl
: &LocalDecl
<'_
>, local_name
: Option
<Symbol
>) -> bool
{
829 debug
!("local_info: {:?}, ty.kind(): {:?}", local_decl
.local_info
, local_decl
.ty
.kind());
831 match local_decl
.local_info
.as_deref() {
832 // Check if mutably borrowing a mutable reference.
833 Some(LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
834 mir
::VarBindingForm
{
835 binding_mode
: ty
::BindingMode
::BindByValue(Mutability
::Not
), ..
837 )))) => matches
!(local_decl
.ty
.kind(), ty
::Ref(_
, _
, hir
::Mutability
::Mut
)),
838 Some(LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::ImplicitSelf(kind
)))) => {
839 // Check if the user variable is a `&mut self` and we can therefore
840 // suggest removing the `&mut`.
842 // Deliberately fall into this case for all implicit self types,
843 // so that we don't fall in to the next case with them.
844 *kind
== mir
::ImplicitSelfKind
::MutRef
846 _
if Some(kw
::SelfLower
) == local_name
=> {
847 // Otherwise, check if the name is the `self` keyword - in which case
848 // we have an explicit self. Do the same thing in this case and check
849 // for a `self: &mut Self` to suggest removing the `&mut`.
850 matches
!(local_decl
.ty
.kind(), ty
::Ref(_
, _
, hir
::Mutability
::Mut
))
856 fn suggest_ampmut_self
<'tcx
>(
858 local_decl
: &mir
::LocalDecl
<'tcx
>,
859 ) -> (Span
, String
) {
860 let sp
= local_decl
.source_info
.span
;
863 match tcx
.sess
.source_map().span_to_snippet(sp
) {
865 let lt_pos
= snippet
.find('
\''
);
866 if let Some(lt_pos
) = lt_pos
{
867 format
!("&{}mut self", &snippet
[lt_pos
..snippet
.len() - 4])
869 "&mut self".to_string()
872 _
=> "&mut self".to_string(),
877 // When we want to suggest a user change a local variable to be a `&mut`, there
878 // are three potential "obvious" things to highlight:
880 // let ident [: Type] [= RightHandSideExpression];
881 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
884 // We can always fallback on highlighting the first. But chances are good that
885 // the user experience will be better if we highlight one of the others if possible;
886 // for example, if the RHS is present and the Type is not, then the type is going to
887 // be inferred *from* the RHS, which means we should highlight that (and suggest
888 // that they borrow the RHS mutably).
890 // This implementation attempts to emulate AST-borrowck prioritization
891 // by trying (3.), then (2.) and finally falling back on (1.).
892 fn suggest_ampmut
<'tcx
>(
894 local_decl
: &mir
::LocalDecl
<'tcx
>,
895 opt_assignment_rhs_span
: Option
<Span
>,
896 opt_ty_info
: Option
<Span
>,
897 ) -> (Span
, String
) {
898 if let Some(assignment_rhs_span
) = opt_assignment_rhs_span
{
899 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(assignment_rhs_span
) {
900 let is_mutbl
= |ty
: &str| -> bool
{
901 if ty
.starts_with("mut") {
903 match rest
.chars().next() {
905 Some(c
) if c
.is_whitespace() => true,
908 // e.g. `&mutablevar`
915 if let (true, Some(ws_pos
)) =
916 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
918 let lt_name
= &src
[1..ws_pos
];
919 let ty
= src
[ws_pos
..].trim_start();
921 return (assignment_rhs_span
, format
!("&{} mut {}", lt_name
, ty
));
923 } else if let Some(stripped
) = src
.strip_prefix('
&'
) {
924 let stripped
= stripped
.trim_start();
925 if !is_mutbl(stripped
) {
926 return (assignment_rhs_span
, format
!("&mut {}", stripped
));
932 let highlight_span
= match opt_ty_info
{
933 // if this is a variable binding with an explicit type,
934 // try to highlight that for the suggestion.
935 Some(ty_span
) => ty_span
,
937 // otherwise, just highlight the span associated with
938 // the (MIR) LocalDecl.
939 None
=> local_decl
.source_info
.span
,
942 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(highlight_span
) {
943 if let (true, Some(ws_pos
)) =
944 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
946 let lt_name
= &src
[1..ws_pos
];
947 let ty
= &src
[ws_pos
..];
948 return (highlight_span
, format
!("&{} mut{}", lt_name
, ty
));
952 let ty_mut
= local_decl
.ty
.builtin_deref(true).unwrap();
953 assert_eq
!(ty_mut
.mutbl
, hir
::Mutability
::Not
);
956 if local_decl
.ty
.is_region_ptr() {
957 format
!("&mut {}", ty_mut
.ty
)
959 format
!("*mut {}", ty_mut
.ty
)
964 fn is_closure_or_generator(ty
: Ty
<'_
>) -> bool
{
965 ty
.is_closure() || ty
.is_generator()
968 /// Adds a suggestion to a struct definition given a field access to a local.
969 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
972 /// LL | s: &'a String
973 /// | ---------- use `&'a mut String` here to make mutable
975 fn annotate_struct_field(
979 ) -> Option
<(Span
, String
)> {
980 // Expect our local to be a reference to a struct of some kind.
981 if let ty
::Ref(_
, ty
, _
) = ty
.kind() {
982 if let ty
::Adt(def
, _
) = ty
.kind() {
983 let field
= def
.all_fields().nth(field
.index())?
;
984 // Use the HIR types to construct the diagnostic message.
985 let hir_id
= tcx
.hir().local_def_id_to_hir_id(field
.did
.as_local()?
);
986 let node
= tcx
.hir().find(hir_id
)?
;
987 // Now we're dealing with the actual struct that we're going to suggest a change to,
988 // we can expect a field that is an immutable reference to a type.
989 if let hir
::Node
::Field(field
) = node
{
990 if let hir
::TyKind
::Rptr(
992 hir
::MutTy { mutbl: hir::Mutability::Not, ref ty }
,
995 // Get the snippets in two parts - the named lifetime (if there is one) and
996 // type being referenced, that way we can reconstruct the snippet without loss
998 let type_snippet
= tcx
.sess
.source_map().span_to_snippet(ty
.span
).ok()?
;
999 let lifetime_snippet
= if !lifetime
.is_elided() {
1000 format
!("{} ", tcx
.sess
.source_map().span_to_snippet(lifetime
.span
).ok()?
)
1007 format
!("&{}mut {}", lifetime_snippet
, &*type_snippet
,),
1017 /// If possible, suggest replacing `ref` with `ref mut`.
1018 fn suggest_ref_mut(tcx
: TyCtxt
<'_
>, binding_span
: Span
) -> Option
<String
> {
1019 let hi_src
= tcx
.sess
.source_map().span_to_snippet(binding_span
).ok()?
;
1020 if hi_src
.starts_with("ref") && hi_src
["ref".len()..].starts_with(rustc_lexer
::is_whitespace
) {
1021 let replacement
= format
!("ref mut{}", &hi_src
["ref".len()..]);