3 use rustc_index
::vec
::Idx
;
4 use rustc_middle
::mir
::{self, ClearCrossCrate, Local, LocalInfo, Location}
;
5 use rustc_middle
::mir
::{Mutability, Place, PlaceRef, ProjectionElem}
;
6 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
7 use rustc_span
::source_map
::DesugaringKind
;
8 use rustc_span
::symbol
::kw
;
11 use crate::borrow_check
::diagnostics
::BorrowedContentSource
;
12 use crate::borrow_check
::MirBorrowckCtxt
;
13 use crate::util
::collect_writes
::FindAssignments
;
14 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
16 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
17 pub(crate) enum AccessKind
{
22 impl<'a
, 'tcx
> MirBorrowckCtxt
<'a
, 'tcx
> {
23 pub(crate) fn report_mutability_error(
25 access_place
: Place
<'tcx
>,
27 the_place_err
: PlaceRef
<'tcx
>,
28 error_access
: AccessKind
,
32 "report_mutability_error(\
33 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
35 access_place
, span
, the_place_err
, error_access
, location
,
41 let mut opt_source
= None
;
42 let access_place_desc
= self.describe_place(access_place
.as_ref());
43 debug
!("report_mutability_error: access_place_desc={:?}", access_place_desc
);
46 PlaceRef { local, projection: [] }
=> {
47 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
48 if access_place
.as_local().is_some() {
49 reason
= ", as it is not declared as mutable".to_string();
51 let name
= self.local_names
[local
].expect("immutable unnamed local");
52 reason
= format
!(", as `{}` is not declared as mutable", name
);
58 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
60 debug_assert
!(is_closure_or_generator(
61 Place
::ty_from(local
, proj_base
, *self.body
, self.infcx
.tcx
).ty
64 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
65 if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
66 reason
= ", as it is not declared as mutable".to_string();
68 let name
= self.upvars
[upvar_index
.index()].name
;
69 reason
= format
!(", as `{}` is not declared as mutable", name
);
73 PlaceRef { local, projection: [ProjectionElem::Deref] }
74 if self.body
.local_decls
[local
].is_ref_for_guard() =>
76 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
77 reason
= ", as it is immutable for the pattern guard".to_string();
79 PlaceRef { local, projection: [ProjectionElem::Deref] }
80 if self.body
.local_decls
[local
].is_ref_to_static() =>
82 if access_place
.projection
.len() == 1 {
83 item_msg
= format
!("immutable static item `{}`", access_place_desc
.unwrap());
84 reason
= String
::new();
86 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
87 let local_info
= &self.body
.local_decls
[local
].local_info
;
88 if let LocalInfo
::StaticRef { def_id, .. }
= *local_info
{
89 let static_name
= &self.infcx
.tcx
.item_name(def_id
);
90 reason
= format
!(", as `{}` is an immutable static item", static_name
);
92 bug
!("is_ref_to_static return true, but not ref to static?");
96 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] }
=> {
97 if the_place_err
.local
== Local
::new(1)
98 && proj_base
.is_empty()
99 && !self.upvars
.is_empty()
101 item_msg
= format
!("`{}`", access_place_desc
.unwrap());
102 debug_assert
!(self.body
.local_decls
[Local
::new(1)].ty
.is_region_ptr());
103 debug_assert
!(is_closure_or_generator(
106 the_place_err
.projection
,
113 reason
= if self.is_upvar_field_projection(access_place
.as_ref()).is_some() {
114 ", as it is a captured variable in a `Fn` closure".to_string()
116 ", as `Fn` closures cannot mutate their captured variables".to_string()
119 let source
= self.borrowed_content_source(PlaceRef
{
120 local
: the_place_err
.local
,
121 projection
: proj_base
,
123 let pointer_type
= source
.describe_for_immutable_place(self.infcx
.tcx
);
124 opt_source
= Some(source
);
125 if let Some(desc
) = access_place_desc
{
126 item_msg
= format
!("`{}`", desc
);
127 reason
= match error_access
{
128 AccessKind
::Mutate
=> format
!(" which is behind {}", pointer_type
),
129 AccessKind
::MutableBorrow
=> {
130 format
!(", as it is behind {}", pointer_type
)
134 item_msg
= format
!("data in {}", pointer_type
);
135 reason
= String
::new();
143 [.., ProjectionElem
::Index(_
)
144 | ProjectionElem
::ConstantIndex { .. }
145 | ProjectionElem
::Subslice { .. }
146 | ProjectionElem
::Downcast(..)],
147 } => bug
!("Unexpected immutable place."),
150 debug
!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg
, reason
);
152 // `act` and `acted_on` are strings that let us abstract over
153 // the verbs used in some diagnostic messages.
157 let span
= match error_access
{
158 AccessKind
::Mutate
=> {
159 err
= self.cannot_assign(span
, &(item_msg
+ &reason
));
161 acted_on
= "written";
164 AccessKind
::MutableBorrow
=> {
165 act
= "borrow as mutable";
166 acted_on
= "borrowed as mutable";
168 let borrow_spans
= self.borrow_spans(span
, location
);
169 let borrow_span
= borrow_spans
.args_or_use();
170 err
= self.cannot_borrow_path_as_mutable_because(borrow_span
, &item_msg
, &reason
);
171 borrow_spans
.var_span_label(
174 "mutable borrow occurs due to use of {} in closure",
175 self.describe_any_place(access_place
.as_ref()),
182 debug
!("report_mutability_error: act={:?}, acted_on={:?}", act
, acted_on
);
184 match the_place_err
{
185 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
187 // This is applicable when we have a deref of a field access to a deref of a local -
188 // something like `*((*_1).0`. The local that we get will be a reference to the
189 // struct we've got a field access of (it must be a reference since there's a deref
190 // after the field access).
194 [proj_base @
.., ProjectionElem
::Deref
, ProjectionElem
::Field(field
, _
), ProjectionElem
::Deref
],
196 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
198 if let Some((span
, message
)) = annotate_struct_field(
200 Place
::ty_from(local
, proj_base
, *self.body
, self.infcx
.tcx
).ty
,
205 "consider changing this to be mutable",
207 Applicability
::MaybeIncorrect
,
212 // Suggest removing a `&mut` from the use of a mutable reference.
213 PlaceRef { local, projection: [] }
219 if let LocalInfo
::User(ClearCrossCrate
::Set(
220 mir
::BindingForm
::ImplicitSelf(kind
),
221 )) = local_decl
.local_info
223 // Check if the user variable is a `&mut self` and we can therefore
224 // suggest removing the `&mut`.
226 // Deliberately fall into this case for all implicit self types,
227 // so that we don't fall in to the next case with them.
228 kind
== mir
::ImplicitSelfKind
::MutRef
229 } else if Some(kw
::SelfLower
) == self.local_names
[local
] {
230 // Otherwise, check if the name is the self kewyord - in which case
231 // we have an explicit self. Do the same thing in this case and check
232 // for a `self: &mut Self` to suggest removing the `&mut`.
233 if let ty
::Ref(_
, _
, hir
::Mutability
::Mut
) = local_decl
.ty
.kind
{
245 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
246 err
.span_label(span
, "try removing `&mut` here");
249 // We want to suggest users use `let mut` for local (user
250 // variable) mutations...
251 PlaceRef { local, projection: [] }
252 if self.body
.local_decls
[local
].can_be_made_mutable() =>
254 // ... but it doesn't make sense to suggest it on
255 // variables that are `ref x`, `ref mut x`, `&self`,
256 // or `&mut self` (such variables are simply not
258 let local_decl
= &self.body
.local_decls
[local
];
259 assert_eq
!(local_decl
.mutability
, Mutability
::Not
);
261 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
263 local_decl
.source_info
.span
,
264 "consider changing this to be mutable",
265 format
!("mut {}", self.local_names
[local
].unwrap()),
266 Applicability
::MachineApplicable
,
270 // Also suggest adding mut for upvars
273 projection
: [proj_base @
.., ProjectionElem
::Field(upvar_index
, _
)],
275 debug_assert
!(is_closure_or_generator(
276 Place
::ty_from(local
, proj_base
, *self.body
, self.infcx
.tcx
).ty
279 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
281 let upvar_hir_id
= self.upvars
[upvar_index
.index()].var_hir_id
;
282 if let Some(Node
::Binding(pat
)) = self.infcx
.tcx
.hir().find(upvar_hir_id
) {
283 if let hir
::PatKind
::Binding(
284 hir
::BindingAnnotation
::Unannotated
,
292 "consider changing this to be mutable",
293 format
!("mut {}", upvar_ident
.name
),
294 Applicability
::MachineApplicable
,
300 // complete hack to approximate old AST-borrowck
301 // diagnostic: if the span starts with a mutable borrow of
302 // a local variable, then just suggest the user remove it.
303 PlaceRef { local: _, projection: [] }
305 if let Ok(snippet
) = self.infcx
.tcx
.sess
.source_map().span_to_snippet(span
) {
306 snippet
.starts_with("&mut ")
312 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
313 err
.span_label(span
, "try removing `&mut` here");
316 PlaceRef { local, projection: [ProjectionElem::Deref] }
317 if self.body
.local_decls
[local
].is_ref_for_guard() =>
319 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
321 "variables bound in patterns are immutable until the end of the pattern guard",
325 // We want to point out when a `&` can be readily replaced
328 // FIXME: can this case be generalized to work for an
329 // arbitrary base for the projection?
330 PlaceRef { local, projection: [ProjectionElem::Deref] }
331 if self.body
.local_decls
[local
].is_user_variable() =>
333 let local_decl
= &self.body
.local_decls
[local
];
335 let (pointer_sigil
, pointer_desc
) = if local_decl
.ty
.is_region_ptr() {
338 ("*const", "pointer")
341 match self.local_names
[local
] {
342 Some(name
) if !local_decl
.from_compiler_desugaring() => {
343 let label
= match local_decl
.local_info
{
344 LocalInfo
::User(ClearCrossCrate
::Set(
345 mir
::BindingForm
::ImplicitSelf(_
),
347 let (span
, suggestion
) =
348 suggest_ampmut_self(self.infcx
.tcx
, local_decl
);
349 Some((true, span
, suggestion
))
352 LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
353 mir
::VarBindingForm
{
354 binding_mode
: ty
::BindingMode
::BindByValue(_
),
359 // check if the RHS is from desugaring
360 let locations
= self.body
.find_assignments(local
);
361 let opt_assignment_rhs_span
= locations
363 .map(|&location
| self.body
.source_info(location
).span
);
364 let opt_desugaring_kind
=
365 opt_assignment_rhs_span
.and_then(|span
| span
.desugaring_kind());
366 match opt_desugaring_kind
{
367 // on for loops, RHS points to the iterator part
368 Some(DesugaringKind
::ForLoop
) => Some((
370 opt_assignment_rhs_span
.unwrap(),
372 "this iterator yields `{SIGIL}` {DESC}s",
373 SIGIL
= pointer_sigil
,
377 // don't create labels for compiler-generated spans
380 let (span
, suggestion
) = suggest_ampmut(
383 opt_assignment_rhs_span
,
386 Some((true, span
, suggestion
))
391 LocalInfo
::User(ClearCrossCrate
::Set(mir
::BindingForm
::Var(
392 mir
::VarBindingForm
{
393 binding_mode
: ty
::BindingMode
::BindByReference(_
),
397 let pattern_span
= local_decl
.source_info
.span
;
398 suggest_ref_mut(self.infcx
.tcx
, pattern_span
)
399 .map(|replacement
| (true, pattern_span
, replacement
))
402 LocalInfo
::User(ClearCrossCrate
::Clear
) => {
403 bug
!("saw cleared local state")
410 Some((true, err_help_span
, suggested_code
)) => {
414 "consider changing this to be a mutable {}",
418 Applicability
::MachineApplicable
,
421 Some((false, err_label_span
, message
)) => {
422 err
.span_label(err_label_span
, &message
);
429 "`{NAME}` is a `{SIGIL}` {DESC}, \
430 so the data it refers to cannot be {ACTED_ON}",
432 SIGIL
= pointer_sigil
,
442 "cannot {ACT} through `{SIGIL}` {DESC}",
444 SIGIL
= pointer_sigil
,
454 projection
: [ProjectionElem
::Deref
],
455 // FIXME document what is this 1 magic number about
456 } if local
== Local
::new(1) && !self.upvars
.is_empty() => {
457 self.expected_fn_found_fn_mut_call(&mut err
, span
, act
);
460 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] }
=> {
461 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
464 Some(BorrowedContentSource
::OverloadedDeref(ty
)) => {
466 "trait `DerefMut` is required to modify through a dereference, \
467 but it is not implemented for `{}`",
471 Some(BorrowedContentSource
::OverloadedIndex(ty
)) => {
473 "trait `IndexMut` is required to modify indexed content, \
474 but it is not implemented for `{}`",
483 err
.span_label(span
, format
!("cannot {ACT}", ACT
= act
));
487 err
.buffer(&mut self.errors_buffer
);
490 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
491 fn expected_fn_found_fn_mut_call(&self, err
: &mut DiagnosticBuilder
<'_
>, sp
: Span
, act
: &str) {
492 err
.span_label(sp
, format
!("cannot {}", act
));
494 let hir
= self.infcx
.tcx
.hir();
495 let closure_id
= hir
.as_local_hir_id(self.mir_def_id
).unwrap();
496 let fn_call_id
= hir
.get_parent_node(closure_id
);
497 let node
= hir
.get(fn_call_id
);
498 let item_id
= hir
.get_parent_item(fn_call_id
);
499 let mut look_at_return
= true;
500 // If we can detect the expression to be an `fn` call where the closure was an argument,
501 // we point at the `fn` definition argument...
502 if let hir
::Node
::Expr(hir
::Expr { kind: hir::ExprKind::Call(func, args), .. }
) = node
{
506 .filter(|(_
, arg
)| arg
.span
== self.body
.span
)
509 let def_id
= hir
.local_def_id(item_id
);
510 let tables
= self.infcx
.tcx
.typeck_tables_of(def_id
);
511 if let Some(ty
::FnDef(def_id
, _
)) =
512 tables
.node_type_opt(func
.hir_id
).as_ref().map(|ty
| &ty
.kind
)
514 let arg
= match hir
.get_if_local(*def_id
) {
516 hir
::Node
::Item(hir
::Item
{
517 ident
, kind
: hir
::ItemKind
::Fn(sig
, ..), ..
519 | hir
::Node
::TraitItem(hir
::TraitItem
{
521 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
524 | hir
::Node
::ImplItem(hir
::ImplItem
{
526 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
533 pos
+ if sig
.decl
.implicit_self
.has_implicit_self() {
541 .unwrap_or(ident
.span
),
545 if let Some(span
) = arg
{
546 err
.span_label(span
, "change this to accept `FnMut` instead of `Fn`");
547 err
.span_label(func
.span
, "expects `Fn` instead of `FnMut`");
548 if self.infcx
.tcx
.sess
.source_map().is_multiline(self.body
.span
) {
549 err
.span_label(self.body
.span
, "in this closure");
551 look_at_return
= false;
556 if look_at_return
&& hir
.get_return_block(closure_id
).is_some() {
557 // ...otherwise we are probably in the tail expression of the function, point at the
559 match hir
.get(hir
.get_parent_item(fn_call_id
)) {
560 hir
::Node
::Item(hir
::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }
)
561 | hir
::Node
::TraitItem(hir
::TraitItem
{
563 kind
: hir
::TraitItemKind
::Fn(sig
, _
),
566 | hir
::Node
::ImplItem(hir
::ImplItem
{
568 kind
: hir
::ImplItemKind
::Fn(sig
, _
),
571 err
.span_label(ident
.span
, "");
573 sig
.decl
.output
.span(),
574 "change this to return `FnMut` instead of `Fn`",
576 err
.span_label(self.body
.span
, "in this closure");
584 fn suggest_ampmut_self
<'tcx
>(
586 local_decl
: &mir
::LocalDecl
<'tcx
>,
587 ) -> (Span
, String
) {
588 let sp
= local_decl
.source_info
.span
;
591 match tcx
.sess
.source_map().span_to_snippet(sp
) {
593 let lt_pos
= snippet
.find('
\''
);
594 if let Some(lt_pos
) = lt_pos
{
595 format
!("&{}mut self", &snippet
[lt_pos
..snippet
.len() - 4])
597 "&mut self".to_string()
600 _
=> "&mut self".to_string(),
605 // When we want to suggest a user change a local variable to be a `&mut`, there
606 // are three potential "obvious" things to highlight:
608 // let ident [: Type] [= RightHandSideExpression];
609 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
612 // We can always fallback on highlighting the first. But chances are good that
613 // the user experience will be better if we highlight one of the others if possible;
614 // for example, if the RHS is present and the Type is not, then the type is going to
615 // be inferred *from* the RHS, which means we should highlight that (and suggest
616 // that they borrow the RHS mutably).
618 // This implementation attempts to emulate AST-borrowck prioritization
619 // by trying (3.), then (2.) and finally falling back on (1.).
620 fn suggest_ampmut
<'tcx
>(
622 local_decl
: &mir
::LocalDecl
<'tcx
>,
623 opt_assignment_rhs_span
: Option
<Span
>,
624 opt_ty_info
: Option
<Span
>,
625 ) -> (Span
, String
) {
626 if let Some(assignment_rhs_span
) = opt_assignment_rhs_span
{
627 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(assignment_rhs_span
) {
628 if let (true, Some(ws_pos
)) =
629 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
631 let lt_name
= &src
[1..ws_pos
];
632 let ty
= &src
[ws_pos
..];
633 return (assignment_rhs_span
, format
!("&{} mut {}", lt_name
, ty
));
634 } else if src
.starts_with('
&'
) {
635 let borrowed_expr
= &src
[1..];
636 return (assignment_rhs_span
, format
!("&mut {}", borrowed_expr
));
641 let highlight_span
= match opt_ty_info
{
642 // if this is a variable binding with an explicit type,
643 // try to highlight that for the suggestion.
644 Some(ty_span
) => ty_span
,
646 // otherwise, just highlight the span associated with
647 // the (MIR) LocalDecl.
648 None
=> local_decl
.source_info
.span
,
651 if let Ok(src
) = tcx
.sess
.source_map().span_to_snippet(highlight_span
) {
652 if let (true, Some(ws_pos
)) =
653 (src
.starts_with("&'"), src
.find(|c
: char| -> bool { c.is_whitespace() }
))
655 let lt_name
= &src
[1..ws_pos
];
656 let ty
= &src
[ws_pos
..];
657 return (highlight_span
, format
!("&{} mut{}", lt_name
, ty
));
661 let ty_mut
= local_decl
.ty
.builtin_deref(true).unwrap();
662 assert_eq
!(ty_mut
.mutbl
, hir
::Mutability
::Not
);
665 if local_decl
.ty
.is_region_ptr() {
666 format
!("&mut {}", ty_mut
.ty
)
668 format
!("*mut {}", ty_mut
.ty
)
673 fn is_closure_or_generator(ty
: Ty
<'_
>) -> bool
{
674 ty
.is_closure() || ty
.is_generator()
677 /// Adds a suggestion to a struct definition given a field access to a local.
678 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
681 /// LL | s: &'a String
682 /// | ---------- use `&'a mut String` here to make mutable
684 fn annotate_struct_field(
688 ) -> Option
<(Span
, String
)> {
689 // Expect our local to be a reference to a struct of some kind.
690 if let ty
::Ref(_
, ty
, _
) = ty
.kind
{
691 if let ty
::Adt(def
, _
) = ty
.kind
{
692 let field
= def
.all_fields().nth(field
.index())?
;
693 // Use the HIR types to construct the diagnostic message.
694 let hir_id
= tcx
.hir().as_local_hir_id(field
.did
)?
;
695 let node
= tcx
.hir().find(hir_id
)?
;
696 // Now we're dealing with the actual struct that we're going to suggest a change to,
697 // we can expect a field that is an immutable reference to a type.
698 if let hir
::Node
::Field(field
) = node
{
699 if let hir
::TyKind
::Rptr(
701 hir
::MutTy { mutbl: hir::Mutability::Not, ref ty }
,
704 // Get the snippets in two parts - the named lifetime (if there is one) and
705 // type being referenced, that way we can reconstruct the snippet without loss
707 let type_snippet
= tcx
.sess
.source_map().span_to_snippet(ty
.span
).ok()?
;
708 let lifetime_snippet
= if !lifetime
.is_elided() {
709 format
!("{} ", tcx
.sess
.source_map().span_to_snippet(lifetime
.span
).ok()?
)
716 format
!("&{}mut {}", lifetime_snippet
, &*type_snippet
,),
726 /// If possible, suggest replacing `ref` with `ref mut`.
727 fn suggest_ref_mut(tcx
: TyCtxt
<'_
>, binding_span
: Span
) -> Option
<String
> {
728 let hi_src
= tcx
.sess
.source_map().span_to_snippet(binding_span
).ok()?
;
729 if hi_src
.starts_with("ref") && hi_src
["ref".len()..].starts_with(rustc_lexer
::is_whitespace
) {
730 let replacement
= format
!("ref mut{}", &hi_src
["ref".len()..]);