]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / src / librustc_mir / borrow_check / diagnostics / mutability_errors.rs
1 use rustc_hir as hir;
2 use rustc_hir::Node;
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;
9 use rustc_span::Span;
10
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};
15
16 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
17 pub(crate) enum AccessKind {
18 MutableBorrow,
19 Mutate,
20 }
21
22 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
23 pub(crate) fn report_mutability_error(
24 &mut self,
25 access_place: Place<'tcx>,
26 span: Span,
27 the_place_err: PlaceRef<'tcx>,
28 error_access: AccessKind,
29 location: Location,
30 ) {
31 debug!(
32 "report_mutability_error(\
33 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
34 )",
35 access_place, span, the_place_err, error_access, location,
36 );
37
38 let mut err;
39 let item_msg;
40 let reason;
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);
44
45 match the_place_err {
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();
50 } else {
51 let name = self.local_names[local].expect("immutable unnamed local");
52 reason = format!(", as `{}` is not declared as mutable", name);
53 }
54 }
55
56 PlaceRef {
57 local,
58 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
59 } => {
60 debug_assert!(is_closure_or_generator(
61 Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty
62 ));
63
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();
67 } else {
68 let name = self.upvars[upvar_index.index()].name;
69 reason = format!(", as `{}` is not declared as mutable", name);
70 }
71 }
72
73 PlaceRef { local, projection: [ProjectionElem::Deref] }
74 if self.body.local_decls[local].is_ref_for_guard() =>
75 {
76 item_msg = format!("`{}`", access_place_desc.unwrap());
77 reason = ", as it is immutable for the pattern guard".to_string();
78 }
79 PlaceRef { local, projection: [ProjectionElem::Deref] }
80 if self.body.local_decls[local].is_ref_to_static() =>
81 {
82 if access_place.projection.len() == 1 {
83 item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
84 reason = String::new();
85 } else {
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);
91 } else {
92 bug!("is_ref_to_static return true, but not ref to static?");
93 }
94 }
95 }
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()
100 {
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(
104 Place::ty_from(
105 the_place_err.local,
106 the_place_err.projection,
107 *self.body,
108 self.infcx.tcx
109 )
110 .ty
111 ));
112
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()
115 } else {
116 ", as `Fn` closures cannot mutate their captured variables".to_string()
117 }
118 } else {
119 let source = self.borrowed_content_source(PlaceRef {
120 local: the_place_err.local,
121 projection: proj_base,
122 });
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)
131 }
132 }
133 } else {
134 item_msg = format!("data in {}", pointer_type);
135 reason = String::new();
136 }
137 }
138 }
139
140 PlaceRef {
141 local: _,
142 projection:
143 [.., ProjectionElem::Index(_)
144 | ProjectionElem::ConstantIndex { .. }
145 | ProjectionElem::Subslice { .. }
146 | ProjectionElem::Downcast(..)],
147 } => bug!("Unexpected immutable place."),
148 }
149
150 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
151
152 // `act` and `acted_on` are strings that let us abstract over
153 // the verbs used in some diagnostic messages.
154 let act;
155 let acted_on;
156
157 let span = match error_access {
158 AccessKind::Mutate => {
159 err = self.cannot_assign(span, &(item_msg + &reason));
160 act = "assign";
161 acted_on = "written";
162 span
163 }
164 AccessKind::MutableBorrow => {
165 act = "borrow as mutable";
166 acted_on = "borrowed as mutable";
167
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(
172 &mut err,
173 format!(
174 "mutable borrow occurs due to use of {} in closure",
175 self.describe_any_place(access_place.as_ref()),
176 ),
177 );
178 borrow_span
179 }
180 };
181
182 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
183
184 match the_place_err {
185 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
186 //
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).
191 PlaceRef {
192 local,
193 projection:
194 [proj_base @ .., ProjectionElem::Deref, ProjectionElem::Field(field, _), ProjectionElem::Deref],
195 } => {
196 err.span_label(span, format!("cannot {ACT}", ACT = act));
197
198 if let Some((span, message)) = annotate_struct_field(
199 self.infcx.tcx,
200 Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty,
201 field,
202 ) {
203 err.span_suggestion(
204 span,
205 "consider changing this to be mutable",
206 message,
207 Applicability::MaybeIncorrect,
208 );
209 }
210 }
211
212 // Suggest removing a `&mut` from the use of a mutable reference.
213 PlaceRef { local, projection: [] }
214 if {
215 self.body
216 .local_decls
217 .get(local)
218 .map(|local_decl| {
219 if let LocalInfo::User(ClearCrossCrate::Set(
220 mir::BindingForm::ImplicitSelf(kind),
221 )) = local_decl.local_info
222 {
223 // Check if the user variable is a `&mut self` and we can therefore
224 // suggest removing the `&mut`.
225 //
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 {
234 true
235 } else {
236 false
237 }
238 } else {
239 false
240 }
241 })
242 .unwrap_or(false)
243 } =>
244 {
245 err.span_label(span, format!("cannot {ACT}", ACT = act));
246 err.span_label(span, "try removing `&mut` here");
247 }
248
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() =>
253 {
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
257 // mutable).
258 let local_decl = &self.body.local_decls[local];
259 assert_eq!(local_decl.mutability, Mutability::Not);
260
261 err.span_label(span, format!("cannot {ACT}", ACT = act));
262 err.span_suggestion(
263 local_decl.source_info.span,
264 "consider changing this to be mutable",
265 format!("mut {}", self.local_names[local].unwrap()),
266 Applicability::MachineApplicable,
267 );
268 }
269
270 // Also suggest adding mut for upvars
271 PlaceRef {
272 local,
273 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
274 } => {
275 debug_assert!(is_closure_or_generator(
276 Place::ty_from(local, proj_base, *self.body, self.infcx.tcx).ty
277 ));
278
279 err.span_label(span, format!("cannot {ACT}", ACT = act));
280
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,
285 _,
286 upvar_ident,
287 _,
288 ) = pat.kind
289 {
290 err.span_suggestion(
291 upvar_ident.span,
292 "consider changing this to be mutable",
293 format!("mut {}", upvar_ident.name),
294 Applicability::MachineApplicable,
295 );
296 }
297 }
298 }
299
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: [] }
304 if {
305 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
306 snippet.starts_with("&mut ")
307 } else {
308 false
309 }
310 } =>
311 {
312 err.span_label(span, format!("cannot {ACT}", ACT = act));
313 err.span_label(span, "try removing `&mut` here");
314 }
315
316 PlaceRef { local, projection: [ProjectionElem::Deref] }
317 if self.body.local_decls[local].is_ref_for_guard() =>
318 {
319 err.span_label(span, format!("cannot {ACT}", ACT = act));
320 err.note(
321 "variables bound in patterns are immutable until the end of the pattern guard",
322 );
323 }
324
325 // We want to point out when a `&` can be readily replaced
326 // with an `&mut`.
327 //
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() =>
332 {
333 let local_decl = &self.body.local_decls[local];
334
335 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
336 ("&", "reference")
337 } else {
338 ("*const", "pointer")
339 };
340
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(_),
346 )) => {
347 let (span, suggestion) =
348 suggest_ampmut_self(self.infcx.tcx, local_decl);
349 Some((true, span, suggestion))
350 }
351
352 LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
353 mir::VarBindingForm {
354 binding_mode: ty::BindingMode::BindByValue(_),
355 opt_ty_info,
356 ..
357 },
358 ))) => {
359 // check if the RHS is from desugaring
360 let locations = self.body.find_assignments(local);
361 let opt_assignment_rhs_span = locations
362 .first()
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((
369 false,
370 opt_assignment_rhs_span.unwrap(),
371 format!(
372 "this iterator yields `{SIGIL}` {DESC}s",
373 SIGIL = pointer_sigil,
374 DESC = pointer_desc
375 ),
376 )),
377 // don't create labels for compiler-generated spans
378 Some(_) => None,
379 None => {
380 let (span, suggestion) = suggest_ampmut(
381 self.infcx.tcx,
382 local_decl,
383 opt_assignment_rhs_span,
384 opt_ty_info,
385 );
386 Some((true, span, suggestion))
387 }
388 }
389 }
390
391 LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
392 mir::VarBindingForm {
393 binding_mode: ty::BindingMode::BindByReference(_),
394 ..
395 },
396 ))) => {
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))
400 }
401
402 LocalInfo::User(ClearCrossCrate::Clear) => {
403 bug!("saw cleared local state")
404 }
405
406 _ => unreachable!(),
407 };
408
409 match label {
410 Some((true, err_help_span, suggested_code)) => {
411 err.span_suggestion(
412 err_help_span,
413 &format!(
414 "consider changing this to be a mutable {}",
415 pointer_desc
416 ),
417 suggested_code,
418 Applicability::MachineApplicable,
419 );
420 }
421 Some((false, err_label_span, message)) => {
422 err.span_label(err_label_span, &message);
423 }
424 None => {}
425 }
426 err.span_label(
427 span,
428 format!(
429 "`{NAME}` is a `{SIGIL}` {DESC}, \
430 so the data it refers to cannot be {ACTED_ON}",
431 NAME = name,
432 SIGIL = pointer_sigil,
433 DESC = pointer_desc,
434 ACTED_ON = acted_on
435 ),
436 );
437 }
438 _ => {
439 err.span_label(
440 span,
441 format!(
442 "cannot {ACT} through `{SIGIL}` {DESC}",
443 ACT = act,
444 SIGIL = pointer_sigil,
445 DESC = pointer_desc
446 ),
447 );
448 }
449 }
450 }
451
452 PlaceRef {
453 local,
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);
458 }
459
460 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
461 err.span_label(span, format!("cannot {ACT}", ACT = act));
462
463 match opt_source {
464 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
465 err.help(&format!(
466 "trait `DerefMut` is required to modify through a dereference, \
467 but it is not implemented for `{}`",
468 ty,
469 ));
470 }
471 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
472 err.help(&format!(
473 "trait `IndexMut` is required to modify indexed content, \
474 but it is not implemented for `{}`",
475 ty,
476 ));
477 }
478 _ => (),
479 }
480 }
481
482 _ => {
483 err.span_label(span, format!("cannot {ACT}", ACT = act));
484 }
485 }
486
487 err.buffer(&mut self.errors_buffer);
488 }
489
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));
493
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 {
503 let arg_pos = args
504 .iter()
505 .enumerate()
506 .filter(|(_, arg)| arg.span == self.body.span)
507 .map(|(pos, _)| pos)
508 .next();
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)
513 {
514 let arg = match hir.get_if_local(*def_id) {
515 Some(
516 hir::Node::Item(hir::Item {
517 ident, kind: hir::ItemKind::Fn(sig, ..), ..
518 })
519 | hir::Node::TraitItem(hir::TraitItem {
520 ident,
521 kind: hir::TraitItemKind::Fn(sig, _),
522 ..
523 })
524 | hir::Node::ImplItem(hir::ImplItem {
525 ident,
526 kind: hir::ImplItemKind::Fn(sig, _),
527 ..
528 }),
529 ) => Some(
530 arg_pos
531 .and_then(|pos| {
532 sig.decl.inputs.get(
533 pos + if sig.decl.implicit_self.has_implicit_self() {
534 1
535 } else {
536 0
537 },
538 )
539 })
540 .map(|arg| arg.span)
541 .unwrap_or(ident.span),
542 ),
543 _ => None,
544 };
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");
550 }
551 look_at_return = false;
552 }
553 }
554 }
555
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
558 // return type.
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 {
562 ident,
563 kind: hir::TraitItemKind::Fn(sig, _),
564 ..
565 })
566 | hir::Node::ImplItem(hir::ImplItem {
567 ident,
568 kind: hir::ImplItemKind::Fn(sig, _),
569 ..
570 }) => {
571 err.span_label(ident.span, "");
572 err.span_label(
573 sig.decl.output.span(),
574 "change this to return `FnMut` instead of `Fn`",
575 );
576 err.span_label(self.body.span, "in this closure");
577 }
578 _ => {}
579 }
580 }
581 }
582 }
583
584 fn suggest_ampmut_self<'tcx>(
585 tcx: TyCtxt<'tcx>,
586 local_decl: &mir::LocalDecl<'tcx>,
587 ) -> (Span, String) {
588 let sp = local_decl.source_info.span;
589 (
590 sp,
591 match tcx.sess.source_map().span_to_snippet(sp) {
592 Ok(snippet) => {
593 let lt_pos = snippet.find('\'');
594 if let Some(lt_pos) = lt_pos {
595 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
596 } else {
597 "&mut self".to_string()
598 }
599 }
600 _ => "&mut self".to_string(),
601 },
602 )
603 }
604
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:
607 //
608 // let ident [: Type] [= RightHandSideExpression];
609 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
610 // (1.) (2.) (3.)
611 //
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).
617 //
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>(
621 tcx: TyCtxt<'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() }))
630 {
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));
637 }
638 }
639 }
640
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,
645
646 // otherwise, just highlight the span associated with
647 // the (MIR) LocalDecl.
648 None => local_decl.source_info.span,
649 };
650
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() }))
654 {
655 let lt_name = &src[1..ws_pos];
656 let ty = &src[ws_pos..];
657 return (highlight_span, format!("&{} mut{}", lt_name, ty));
658 }
659 }
660
661 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
662 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
663 (
664 highlight_span,
665 if local_decl.ty.is_region_ptr() {
666 format!("&mut {}", ty_mut.ty)
667 } else {
668 format!("*mut {}", ty_mut.ty)
669 },
670 )
671 }
672
673 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
674 ty.is_closure() || ty.is_generator()
675 }
676
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.
679 ///
680 /// ```text
681 /// LL | s: &'a String
682 /// | ---------- use `&'a mut String` here to make mutable
683 /// ```
684 fn annotate_struct_field(
685 tcx: TyCtxt<'tcx>,
686 ty: Ty<'tcx>,
687 field: &mir::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(
700 lifetime,
701 hir::MutTy { mutbl: hir::Mutability::Not, ref ty },
702 ) = field.ty.kind
703 {
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
706 // of detail.
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()?)
710 } else {
711 String::new()
712 };
713
714 return Some((
715 field.ty.span,
716 format!("&{}mut {}", lifetime_snippet, &*type_snippet,),
717 ));
718 }
719 }
720 }
721 }
722
723 None
724 }
725
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()..]);
731 Some(replacement)
732 } else {
733 None
734 }
735 }