Skip to content

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

+80
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

+3
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

+3
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)