3 use rustc_index
::vec
::Idx
;
4 use rustc_middle
::hir
::map
::Map
;
5 use rustc_middle
::mir
::{Mutability, Place, PlaceRef, ProjectionElem}
;
6 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
9 mir
::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}
,
11 use rustc_span
::source_map
::DesugaringKind
;
12 use rustc_span
::symbol
::{kw, Symbol}
;
15 use crate::borrow_check
::diagnostics
::BorrowedContentSource
;
16 use crate::borrow_check
::MirBorrowckCtxt
;
17 use crate::util
::collect_writes
::FindAssignments
;
18 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
20 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
21 pub(crate) enum AccessKind
{
26 impl<'a
, 'tcx
> MirBorrowckCtxt
<'a
, 'tcx
> {
27 pub(crate) fn report_mutability_error(
29 access_place
: Place
<'tcx
>,
31 the_place_err
: PlaceRef
<'tcx
>,
32 error_access
: AccessKind
,
36 "report_mutability_error(\
37 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
39 access_place
, span
, the_place_err
, error_access
, location
,
45 let mut opt_source
= None
;
46 let access_place_desc
= self.describe_place(access_place
.as_ref());
47 debug
!("report_mutability_error: access_place_desc={:?}", access_place_desc
);
50 PlaceRef { local, projection: [] }
=> {
51 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
52 if access_place
.as_local().is_some() {
53 reason
= ", as it is not declared as mutable".to_string();
55 let name
= self.local_names
[local
].expect("immutable unnamed local");
56 reason
= format
!(", as `{}` is not declared as mutable", name
);
62 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
64 debug_assert
!(is_closure_or_generator(
65 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
68 let imm_borrow_derefed
= self.upvars
[upvar_index
.index()]
72 .any(|ty
| matches
!(ty
.kind(), ty
::Ref(.., hir
::Mutability
::Not
)));
74 // If the place is immutable then:
76 // - Either we deref a immutable ref to get to our final place.
77 // - We don't capture derefs of raw ptrs
78 // - Or the final place is immut because the root variable of the capture
79 // isn't marked mut and we should suggest that to the user.
80 if imm_borrow_derefed
{
81 // If we deref an immutable ref then the suggestion here doesn't help.
84 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
85 if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
86 reason
= ", as it is not declared as mutable".to_string();
88 let name
= self.upvars
[upvar_index
.index()].name
;
89 reason
= format
!(", as `{}` is not declared as mutable", name
);
94 PlaceRef { local, projection: [ProjectionElem::Deref] }
95 if self.body
.local_decls
[local
].is_ref_for_guard() =>
97 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
98 reason
= ", as it is immutable for the pattern guard".to_string();
100 PlaceRef { local, projection: [ProjectionElem::Deref] }
101 if self.body
.local_decls
[local
].is_ref_to_static() =>
103 if access_place
.projection
.len() == 1 {
104 item_msg
= format
!("immutable static item `{}`", access_place_desc
.unwrap());
105 reason
= String
::new();
107 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
108 let local_info
= &self.body
.local_decls
[local
].local_info
;
109 if let Some(box LocalInfo
::StaticRef { def_id, .. }
) = *local_info
{
110 let static_name
= &self.infcx
.tcx
.item_name(def_id
);
111 reason
= format
!(", as `{}` is an immutable static item", static_name
);
113 bug
!("is_ref_to_static return true, but not ref to static?");
117 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] }
=> {
118 if the_place_err
.local
== Local
::new(1)
119 && proj_base
.is_empty()
120 && !self.upvars
.is_empty()
122 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
123 debug_assert
!(self.body
.local_decls
[Local
::new(1)].ty
.is_region_ptr());
124 debug_assert
!(is_closure_or_generator(
127 the_place_err
.projection
,
134 reason
= if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
135 ", as it is a captured variable in a `Fn` closure".to_string()
137 ", as `Fn` closures cannot mutate their captured variables".to_string()
140 let source
= self.borrowed_content_source(PlaceRef
{
141 local
: the_place_err
.local
,
142 projection
: proj_base
,
144 let pointer_type
= source
.describe_for_immutable_place(self.infcx
.tcx
);
145 opt_source
= Some(source
);
146 if let Some(desc
) = access_place_desc
{
147 item_msg
= format
!("`{}`", desc
);
148 reason
= match error_access
{
149 AccessKind
::Mutate
=> format
!(" which is behind {}", pointer_type
),
150 AccessKind
::MutableBorrow
=> {
151 format
!(", as it is behind {}", pointer_type
)
155 item_msg
= format
!("data in {}", pointer_type
);
156 reason
= String
::new();
164 [.., ProjectionElem
::Index(_
)
165 | ProjectionElem
::ConstantIndex { .. }
166 | ProjectionElem
::Subslice { .. }
167 | ProjectionElem
::Downcast(..)],
168 } => bug
!("Unexpected immutable place."),
171 debug
!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg
, reason
);
173 // `act` and `acted_on` are strings that let us abstract over
174 // the verbs used in some diagnostic messages.
178 let span
= match error_access
{
179 AccessKind
::Mutate
=> {
180 err
= self.cannot_assign(span
, &(item_msg
+ &reason
));
182 acted_on
= "written";
185 AccessKind
::MutableBorrow
=> {
186 act
= "borrow as mutable";
187 acted_on
= "borrowed as mutable";
189 let borrow_spans
= self.borrow_spans(span
, location
);
190 let borrow_span
= borrow_spans
.args_or_use();
191 err
= self.cannot_borrow_path_as_mutable_because(borrow_span
, &item_msg
, &reason
);
192 borrow_spans
.var_span_label(
195 "mutable borrow occurs due to use of {} in closure",
196 self.describe_any_place(access_place
.as_ref()),
203 debug
!("report_mutability_error: act={:?}, acted_on={:?}", act
, acted_on
);
205 match the_place_err
{
206 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
208 // This is applicable when we have a deref of a field access to a deref of a local -
209 // something like `*((*_1).0`. The local that we get will be a reference to the
210 // struct we've got a field access of (it must be a reference since there's a deref
211 // after the field access).
215 [proj_base @
.., ProjectionElem
::Deref
, ProjectionElem
::Field(field
, _
), ProjectionElem
::Deref
],
217 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
219 if let Some((span
, message
)) = annotate_struct_field(
221 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
,
226 "consider changing this to be mutable",
228 Applicability
::MaybeIncorrect
,
233 // Suggest removing a `&mut` from the use of a mutable reference.
234 PlaceRef { local, projection: [] }
239 .map(|l
| mut_borrow_of_mutable_ref(l
, self.local_names
[local
]))
242 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
243 err
.span_label(span
, "try removing `&mut` here");
246 // We want to suggest users use `let mut` for local (user
247 // variable) mutations...
248 PlaceRef { local, projection: [] }
249 if self.body
.local_decls
[local
].can_be_made_mutable() =>
251 // ... but it doesn't make sense to suggest it on
252 // variables that are `ref x`, `ref mut x`, `&self`,
253 // or `&mut self` (such variables are simply not
255 let local_decl
= &self.body
.local_decls
[local
];
256 assert_eq
!(local_decl
.mutability
, Mutability
::Not
);
258 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
260 local_decl
.source_info
.span
,
261 "consider changing this to be mutable",
262 format
!("mut {}", self.local_names
[local
].unwrap()),
263 Applicability
::MachineApplicable
,
265 let tcx
= self.infcx
.tcx
;
266 if let ty
::Closure(id
, _
) = the_place_err
.ty(self.body
, tcx
).ty
.kind() {
267 self.show_mutating_upvar(tcx
, id
, the_place_err
, &mut err
);
271 // Also suggest adding mut for upvars
274 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
276 debug_assert
!(is_closure_or_generator(
277 Place
::ty_from(local
, proj_base
, self.body
, self.infcx
.tcx
).ty
280 let captured_place
= &self.upvars
[upvar_index
.index()].place
;
282 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
284 let upvar_hir_id
= captured_place
.get_root_variable();
286 if let Some(Node
::Binding(pat
)) = self.infcx
.tcx
.hir().find(upvar_hir_id
) {
287 if let hir
::PatKind
::Binding(
288 hir
::BindingAnnotation
::Unannotated
,
296 "consider changing this to be mutable",
297 format
!("mut {}", upvar_ident
.name
),
298 Applicability
::MachineApplicable
,
303 let tcx
= self.infcx
.tcx
;
304 if let ty
::Ref(_
, ty
, Mutability
::Mut
) = the_place_err
.ty(self.body
, tcx
).ty
.kind()
306 if let ty
::Closure(id
, _
) = ty
.kind() {
307 self.show_mutating_upvar(tcx
, id
, the_place_err
, &mut err
);
312 // complete hack to approximate old AST-borrowck
313 // diagnostic: if the span starts with a mutable borrow of
314 // a local variable, then just suggest the user remove it.
315 PlaceRef { local: _, projection: [] }
317 if let Ok(snippet
) = self.infcx
.tcx
.sess
.source_map().span_to_snippet(span
) {
318 snippet
.starts_with("&mut ")
324 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
325 err
.span_label(span
, "try removing `&mut` here");
328 PlaceRef { local, projection: [ProjectionElem::Deref] }
329 if self.body
.local_decls
[local
].is_ref_for_guard() =>
331 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
333 "variables bound in patterns are immutable until the end of the pattern guard",
337 // We want to point out when a `&` can be readily replaced
340 // FIXME: can this case be generalized to work for an
341 // arbitrary base for the projection?
342 PlaceRef { local, projection: [ProjectionElem::Deref] }
343 if self.body
.local_decls
[local
].is_user_variable() =>
345 let local_decl
= &self.body
.local_decls
[local
];
347 let (pointer_sigil
, pointer_desc
) = if local_decl
.ty
.is_region_ptr() {
350 ("*const", "pointer")
353 match self.local_names
[local
] {
354 Some(name
) if !local_decl
.from_compiler_desugaring() => {
355 let label
= match local_decl
.local_info
.as_ref().unwrap() {
356 box LocalInfo
::User(ClearCrossCrate
::Set(
357 mir
::BindingForm
::ImplicitSelf(_
),
359 let (span
, suggestion
) =
360 suggest_ampmut_self(self.infcx
.tcx
, local_decl
);
361 Some((true, span
, suggestion
))
364 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
365 mir
::VarBindingForm
{
366 binding_mode
: ty
::BindingMode
::BindByValue(_
),
371 // check if the RHS is from desugaring
372 let locations
= self.body
.find_assignments(local
);
373 let opt_assignment_rhs_span
= locations
375 .map(|&location
| self.body
.source_info(location
).span
);
376 let opt_desugaring_kind
=
377 opt_assignment_rhs_span
.and_then(|span
| span
.desugaring_kind());
378 match opt_desugaring_kind
{
379 // on for loops, RHS points to the iterator part
380 Some(DesugaringKind
::ForLoop(_
)) => {
381 self.suggest_similar_mut_method_for_for_loop(&mut err
);
384 opt_assignment_rhs_span
.unwrap(),
386 "this iterator yields `{SIGIL}` {DESC}s",
387 SIGIL
= pointer_sigil
,
392 // don't create labels for compiler-generated spans
395 let (span
, suggestion
) = suggest_ampmut(
398 opt_assignment_rhs_span
,
401 Some((true, span
, suggestion
))
406 box LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
407 mir
::VarBindingForm
{
408 binding_mode
: ty
::BindingMode
::BindByReference(_
),
412 let pattern_span
= local_decl
.source_info
.span
;
413 suggest_ref_mut(self.infcx
.tcx
, pattern_span
)
414 .map(|replacement
| (true, pattern_span
, replacement
))
417 box LocalInfo
::User(ClearCrossCrate
::Clear
) => {
418 bug
!("saw cleared local state")
425 Some((true, err_help_span
, suggested_code
)) => {
429 "consider changing this to be a mutable {}",
433 Applicability
::MachineApplicable
,
436 Some((false, err_label_span
, message
)) => {
437 err
.span_label(err_label_span
, &message
);
444 "`{NAME}` is a `{SIGIL}` {DESC}, \
445 so the data it refers to cannot be {ACTED_ON}",
447 SIGIL
= pointer_sigil
,
457 "cannot {ACT} through `{SIGIL}` {DESC}",
459 SIGIL
= pointer_sigil
,
469 projection
: [ProjectionElem
::Deref
],
470 // FIXME document what is this 1 magic number about
471 } if local
== Local
::new(1) && !self.upvars
.is_empty() => {
472 self.expected_fn_found_fn_mut_call(&mut err
, span
, act
);
475 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] }
=> {
476 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
479 Some(BorrowedContentSource
::OverloadedDeref(ty
)) => {
481 "trait `DerefMut` is required to modify through a dereference, \
482 but it is not implemented for `{}`",
486 Some(BorrowedContentSource
::OverloadedIndex(ty
)) => {
488 "trait `IndexMut` is required to modify indexed content, \
489 but it is not implemented for `{}`",
498 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
502 err
.buffer(&mut self.errors_buffer
);
505 // point to span of upvar making closure call require mutable borrow
506 fn show_mutating_upvar(
509 id
: &hir
::def_id
::DefId
,
510 the_place_err
: PlaceRef
<'tcx
>,
511 err
: &mut DiagnosticBuilder
<'_
>,
513 let closure_local_def_id
= id
.expect_local();
514 let tables
= tcx
.typeck(closure_local_def_id
);
515 let closure_hir_id
= tcx
.hir().local_def_id_to_hir_id(closure_local_def_id
);
516 if let Some((span
, closure_kind_origin
)) =
517 &tables
.closure_kind_origins().get(closure_hir_id
)
519 let reason
= if let PlaceBase
::Upvar(upvar_id
) = closure_kind_origin
.base
{
520 let upvar
= ty
::place_to_string_for_capture(tcx
, closure_kind_origin
);
521 let root_hir_id
= upvar_id
.var_path
.hir_id
;
522 // we have a origin for this closure kind starting at this root variable so it's safe to unwrap here
523 let captured_places
= tables
.closure_min_captures
[id
].get(&root_hir_id
).unwrap();
525 let origin_projection
= closure_kind_origin
528 .map(|proj
| proj
.kind
)
529 .collect
::<Vec
<_
>>();
530 let mut capture_reason
= String
::new();
531 for captured_place
in captured_places
{
532 let captured_place_kinds
= captured_place
536 .map(|proj
| proj
.kind
)
537 .collect
::<Vec
<_
>>();
538 if rustc_middle
::ty
::is_ancestor_or_same_capture(
539 &captured_place_kinds
,
542 match captured_place
.info
.capture_kind
{
543 ty
::UpvarCapture
::ByRef(ty
::UpvarBorrow
{
544 kind
: ty
::BorrowKind
::MutBorrow
| ty
::BorrowKind
::UniqueImmBorrow
,
547 capture_reason
= format
!("mutable borrow of `{}`", upvar
);
549 ty
::UpvarCapture
::ByValue(_
) => {
550 capture_reason
= format
!("possible mutation of `{}`", upvar
);
552 _
=> bug
!("upvar `{}` borrowed, but not mutably", upvar
),
557 if capture_reason
.is_empty() {
558 bug
!("upvar `{}` borrowed, but cannot find reason", upvar
);
567 "calling `{}` requires mutable binding due to {}",
568 self.describe_place(the_place_err
).unwrap(),
575 // Attempt to search similar mutable associated items for suggestion.
576 // In the future, attempt in all path but initially for RHS of for_loop
577 fn suggest_similar_mut_method_for_for_loop(&self, err
: &mut DiagnosticBuilder
<'_
>) {
580 ExprKind
::{Block, Call, DropTemps, Match, MethodCall}
,
581 HirId
, ImplItem
, ImplItemKind
, Item
, ItemKind
,
584 fn maybe_body_id_of_fn(hir_map
: &Map
<'tcx
>, id
: HirId
) -> Option
<BodyId
> {
585 match hir_map
.find(id
) {
586 Some(Node
::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }
))
587 | Some(Node
::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. }
)) => {
593 let hir_map
= self.infcx
.tcx
.hir();
594 let mir_body_hir_id
= self.mir_hir_id();
595 if let Some(fn_body_id
) = maybe_body_id_of_fn(&hir_map
, mir_body_hir_id
) {
609 kind
: MethodCall(path_segment
, ..),
625 ) = hir_map
.body(fn_body_id
).value
.kind
627 let opt_suggestions
= path_segment
629 .map(|path_hir_id
| self.infcx
.tcx
.typeck(path_hir_id
.owner
))
630 .and_then(|typeck
| typeck
.type_dependent_def_id(*hir_id
))
631 .and_then(|def_id
| self.infcx
.tcx
.impl_of_method(def_id
))
632 .map(|def_id
| self.infcx
.tcx
.associated_items(def_id
))
635 .in_definition_order()
636 .map(|assoc_item_def
| assoc_item_def
.ident
)
638 let original_method_ident
= path_segment
.ident
;
639 original_method_ident
!= ident
642 .starts_with(&original_method_ident
.name
.to_string())
644 .map(|ident
| format
!("{}()", ident
))
647 if let Some(suggestions
) = opt_suggestions
{
648 err
.span_suggestions(
649 path_segment
.ident
.span
,
650 &format
!("use mutable method"),
652 Applicability
::MaybeIncorrect
,
659 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
660 fn expected_fn_found_fn_mut_call(&self, err
: &mut DiagnosticBuilder
<'_
>, sp
: Span
, act
: &str) {
661 err
.span_label(sp
, format
!("cannot {}", act
));
663 let hir
= self.infcx
.tcx
.hir();
664 let closure_id
= self.mir_hir_id();
665 let fn_call_id
= hir
.get_parent_node(closure_id
);
666 let node
= hir
.get(fn_call_id
);
667 let item_id
= hir
.enclosing_body_owner(fn_call_id
);
668 let mut look_at_return
= true;
669 // If we can detect the expression to be an `fn` call where the closure was an argument,
670 // we point at the `fn` definition argument...
671 if let hir
::Node
::Expr(hir
::Expr { kind: hir::ExprKind::Call(func, args), .. }
) = node
{
675 .filter(|(_
, arg
)| arg
.span
== self.body
.span
)
678 let def_id
= hir
.local_def_id(item_id
);
679 let tables
= self.infcx
.tcx
.typeck(def_id
);
680 if let Some(ty
::FnDef(def_id
, _
)) =
681 tables
.node_type_opt(func
.hir_id
).as_ref().map(|ty
| ty
.kind())
683 let arg
= match hir
.get_if_local(*def_id
) {
685 hir
::Node
::Item(hir
::Item
{
686 ident
, kind
: hir
::ItemKind
::Fn(sig
, ..), ..
688 | hir
::Node
::TraitItem(hir
::TraitItem
{
690 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
693 | hir
::Node
::ImplItem(hir
::ImplItem
{
695 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
702 pos
+ if sig
.decl
.implicit_self
.has_implicit_self() {
710 .unwrap_or(ident
.span
),
714 if let Some(span
) = arg
{
715 err
.span_label(span
, "change this to accept `FnMut` instead of `Fn`");
716 err
.span_label(func
.span
, "expects `Fn` instead of `FnMut`");
717 if self.infcx
.tcx
.sess
.source_map().is_multiline(self.body
.span
) {
718 err
.span_label(self.body
.span
, "in this closure");
720 look_at_return
= false;
725 if look_at_return
&& hir
.get_return_block(closure_id
).is_some() {
726 // ...otherwise we are probably in the tail expression of the function, point at the
728 match hir
.get(hir
.get_parent_item(fn_call_id
)) {
729 hir
::Node
::Item(hir
::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }
)
730 | hir
::Node
::TraitItem(hir
::TraitItem
{
732 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
735 | hir
::Node
::ImplItem(hir
::ImplItem
{
737 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
740 err
.span_label(ident
.span
, "");
742 sig
.decl
.output
.span(),
743 "change this to return `FnMut` instead of `Fn`",
745 err
.span_label(self.body
.span
, "in this closure");
753 fn mut_borrow_of_mutable_ref(local_decl
: &LocalDecl
<'_
>, local_name
: Option
<Symbol
>) -> bool
{
754 debug
!("local_info: {:?}, ty.kind(): {:?}", local_decl
.local_info
, local_decl
.ty
.kind());
756 match local_decl
.local_info
.as_deref() {
757 // Check if mutably borrowing a mutable reference.
758 Some(LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
759 mir
::VarBindingForm
{
760 binding_mode
: ty
::BindingMode
::BindByValue(Mutability
::Not
), ..
762 )))) => matches
!(local_decl
.ty
.kind(), ty
::Ref(_
, _
, hir
::Mutability
::Mut
)),
763 Some(LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::ImplicitSelf(kind
)))) => {
764 // Check if the user variable is a `&mut self` and we can therefore
765 // suggest removing the `&mut`.
767 // Deliberately fall into this case for all implicit self types,
768 // so that we don't fall in to the next case with them.
769 *kind
== mir
::ImplicitSelfKind
::MutRef
771 _
if Some(kw
::SelfLower
) == local_name
=> {
772 // Otherwise, check if the name is the `self` keyword - in which case
773 // we have an explicit self. Do the same thing in this case and check
774 // for a `self: &mut Self` to suggest removing the `&mut`.
775 matches
!(local_decl
.ty
.kind(), ty
::Ref(_
, _
, hir
::Mutability
::Mut
))
781 fn suggest_ampmut_self
<'tcx
>(
783 local_decl
: &mir
::LocalDecl
<'tcx
>,
784 ) -> (Span
, String
) {
785 let sp
= local_decl
.source_info
.span
;
788 match tcx
.sess
.source_map().span_to_snippet(sp
) {
790 let lt_pos
= snippet
.find('
\''
);
791 if let Some(lt_pos
) = lt_pos
{
792 format
!("&{}mut self", &snippet
[lt_pos
..snippet
.len() - 4])
794 "&mut self".to_string()
797 _
=> "&mut self".to_string(),
802 // When we want to suggest a user change a local variable to be a `&mut`, there
803 // are three potential "obvious" things to highlight:
805 // let ident [: Type] [= RightHandSideExpression];
806 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
809 // We can always fallback on highlighting the first. But chances are good that
810 // the user experience will be better if we highlight one of the others if possible;
811 // for example, if the RHS is present and the Type is not, then the type is going to
812 // be inferred *from* the RHS, which means we should highlight that (and suggest
813 // that they borrow the RHS mutably).
815 // This implementation attempts to emulate AST-borrowck prioritization
816 // by trying (3.), then (2.) and finally falling back on (1.).
817 fn suggest_ampmut
<'tcx
>(
819 local_decl
: &mir
::LocalDecl
<'tcx
>,
820 opt_assignment_rhs_span
: Option
<Span
>,
821 opt_ty_info
: Option
<Span
>,
822 ) -> (Span
, String
) {
823 if let Some(assignment_rhs_span
) = opt_assignment_rhs_span
{
824 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(assignment_rhs_span
) {
825 if let (true, Some(ws_pos
)) =
826 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
828 let lt_name
= &src
[1..ws_pos
];
829 let ty
= &src
[ws_pos
..];
830 return (assignment_rhs_span
, format
!("&{} mut {}", lt_name
, ty
));
831 } else if let Some(stripped
) = src
.strip_prefix('
&'
) {
832 return (assignment_rhs_span
, format
!("&mut {}", stripped
));
837 let highlight_span
= match opt_ty_info
{
838 // if this is a variable binding with an explicit type,
839 // try to highlight that for the suggestion.
840 Some(ty_span
) => ty_span
,
842 // otherwise, just highlight the span associated with
843 // the (MIR) LocalDecl.
844 None
=> local_decl
.source_info
.span
,
847 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(highlight_span
) {
848 if let (true, Some(ws_pos
)) =
849 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
851 let lt_name
= &src
[1..ws_pos
];
852 let ty
= &src
[ws_pos
..];
853 return (highlight_span
, format
!("&{} mut{}", lt_name
, ty
));
857 let ty_mut
= local_decl
.ty
.builtin_deref(true).unwrap();
858 assert_eq
!(ty_mut
.mutbl
, hir
::Mutability
::Not
);
861 if local_decl
.ty
.is_region_ptr() {
862 format
!("&mut {}", ty_mut
.ty
)
864 format
!("*mut {}", ty_mut
.ty
)
869 fn is_closure_or_generator(ty
: Ty
<'_
>) -> bool
{
870 ty
.is_closure() || ty
.is_generator()
873 /// Adds a suggestion to a struct definition given a field access to a local.
874 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
877 /// LL | s: &'a String
878 /// | ---------- use `&'a mut String` here to make mutable
880 fn annotate_struct_field(
884 ) -> Option
<(Span
, String
)> {
885 // Expect our local to be a reference to a struct of some kind.
886 if let ty
::Ref(_
, ty
, _
) = ty
.kind() {
887 if let ty
::Adt(def
, _
) = ty
.kind() {
888 let field
= def
.all_fields().nth(field
.index())?
;
889 // Use the HIR types to construct the diagnostic message.
890 let hir_id
= tcx
.hir().local_def_id_to_hir_id(field
.did
.as_local()?
);
891 let node
= tcx
.hir().find(hir_id
)?
;
892 // Now we're dealing with the actual struct that we're going to suggest a change to,
893 // we can expect a field that is an immutable reference to a type.
894 if let hir
::Node
::Field(field
) = node
{
895 if let hir
::TyKind
::Rptr(
897 hir
::MutTy { mutbl: hir::Mutability::Not, ref ty }
,
900 // Get the snippets in two parts - the named lifetime (if there is one) and
901 // type being referenced, that way we can reconstruct the snippet without loss
903 let type_snippet
= tcx
.sess
.source_map().span_to_snippet(ty
.span
).ok()?
;
904 let lifetime_snippet
= if !lifetime
.is_elided() {
905 format
!("{} ", tcx
.sess
.source_map().span_to_snippet(lifetime
.span
).ok()?
)
912 format
!("&{}mut {}", lifetime_snippet
, &*type_snippet
,),
922 /// If possible, suggest replacing `ref` with `ref mut`.
923 fn suggest_ref_mut(tcx
: TyCtxt
<'_
>, binding_span
: Span
) -> Option
<String
> {
924 let hi_src
= tcx
.sess
.source_map().span_to_snippet(binding_span
).ok()?
;
925 if hi_src
.starts_with("ref") && hi_src
["ref".len()..].starts_with(rustc_lexer
::is_whitespace
) {
926 let replacement
= format
!("ref mut{}", &hi_src
["ref".len()..]);