Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f8c1b0c

Browse files
committed
Add struct field suggestions.
This commit adds suggestions to change the definitions of fields in struct definitions from immutable references to mutable references.
1 parent 5de5281 commit f8c1b0c

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

src/librustc_mir/borrow_check/mutability_errors.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,33 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
218218
debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
219219

220220
match the_place_err {
221+
// Suggest making an existing shared borrow in a struct definition a mutable borrow.
222+
//
223+
// This is applicable when we have a deref of a field access to a deref of a local -
224+
// something like `*((*_1).0`. The local that we get will be a reference to the
225+
// struct we've got a field access of (it must be a reference since there's a deref
226+
// after the field access).
227+
Place::Projection(box Projection {
228+
base: Place::Projection(box Projection {
229+
base: Place::Projection(box Projection {
230+
base,
231+
elem: ProjectionElem::Deref,
232+
}),
233+
elem: ProjectionElem::Field(field, _),
234+
}),
235+
elem: ProjectionElem::Deref,
236+
}) => {
237+
err.span_label(span, format!("cannot {ACT}", ACT = act));
238+
239+
if let Some((span, message)) = annotate_struct_field(
240+
self.infcx.tcx,
241+
base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx),
242+
field,
243+
) {
244+
err.span_label(span, message);
245+
}
246+
},
247+
221248
// Suggest removing a `&mut` from the use of a mutable reference.
222249
Place::Local(local)
223250
if {
@@ -592,3 +619,56 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
592619
fn is_closure_or_generator(ty: ty::Ty) -> bool {
593620
ty.is_closure() || ty.is_generator()
594621
}
622+
623+
/// Add a suggestion to a struct definition given a field access to a local.
624+
/// This function expects the local to be a reference to a struct in order to produce a suggestion.
625+
///
626+
/// ```text
627+
/// LL | s: &'a String
628+
/// | ---------- use `&'a mut String` here to make mutable
629+
/// ```
630+
fn annotate_struct_field(
631+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
632+
ty: ty::Ty<'tcx>,
633+
field: &mir::Field,
634+
) -> Option<(Span, String)> {
635+
// Expect our local to be a reference to a struct of some kind.
636+
if let ty::TyKind::Ref(_, ty, _) = ty.sty {
637+
if let ty::TyKind::Adt(def, _) = ty.sty {
638+
let field = def.all_fields().nth(field.index())?;
639+
let span = tcx.def_span(field.did);
640+
641+
// Use the HIR types to construct the diagnostic message.
642+
let node_id = tcx.hir.as_local_node_id(field.did)?;
643+
let node = tcx.hir.find(node_id)?;
644+
// Now we're dealing with the actual struct that we're going to suggest a change to,
645+
// we can expect a field that is an immutable reference to a type.
646+
if let hir::Node::Field(field) = node {
647+
if let hir::TyKind::Rptr(lifetime, hir::MutTy {
648+
mutbl: hir::Mutability::MutImmutable,
649+
ref ty
650+
}) = field.ty.node {
651+
// Get the snippets in two parts - the named lifetime (if there is one) and
652+
// type being referenced, that way we can reconstruct the snippet without loss
653+
// of detail.
654+
let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
655+
let lifetime_snippet = if !lifetime.is_elided() {
656+
format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
657+
} else {
658+
String::new()
659+
};
660+
661+
return Some((
662+
span,
663+
format!(
664+
"use `&{}mut {}` here to make mutable",
665+
lifetime_snippet, &*type_snippet,
666+
),
667+
));
668+
}
669+
}
670+
}
671+
}
672+
673+
None
674+
}

src/test/ui/did_you_mean/issue-38147-2.nll.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
22
--> $DIR/issue-38147-2.rs:17:9
33
|
4+
LL | s: &'a String
5+
| ------------- use `&'a mut String` here to make mutable
6+
...
47
LL | self.s.push('x');
58
| ^^^^^^ cannot borrow as mutable
69

src/test/ui/did_you_mean/issue-38147-3.nll.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
22
--> $DIR/issue-38147-3.rs:17:9
33
|
4+
LL | s: &'a String
5+
| ------------- use `&'a mut String` here to make mutable
6+
...
47
LL | self.s.push('x');
58
| ^^^^^^ cannot borrow as mutable
69

0 commit comments

Comments
 (0)