Skip to content

Commit 71d3a71

Browse files
committed
Auto merge of #54831 - davidtwco:issue-52663-struct-field-suggestion, r=nikomatsakis
NLL is missing struct field suggestion Part of #52663. This commit adds suggestions to change the definitions of fields in struct definitions from immutable references to mutable references. r? @nikomatsakis cc @pnkfelix
2 parents 4623d48 + 9e49ac0 commit 71d3a71

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

src/librustc_mir/borrow_check/mutability_errors.rs

+83
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,38 @@ 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_suggestion_with_applicability(
245+
span,
246+
"consider changing this to be mutable",
247+
message,
248+
Applicability::MaybeIncorrect,
249+
);
250+
}
251+
},
252+
221253
// Suggest removing a `&mut` from the use of a mutable reference.
222254
Place::Local(local)
223255
if {
@@ -592,3 +624,54 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
592624
fn is_closure_or_generator(ty: ty::Ty) -> bool {
593625
ty.is_closure() || ty.is_generator()
594626
}
627+
628+
/// Add a suggestion to a struct definition given a field access to a local.
629+
/// This function expects the local to be a reference to a struct in order to produce a suggestion.
630+
///
631+
/// ```text
632+
/// LL | s: &'a String
633+
/// | ---------- use `&'a mut String` here to make mutable
634+
/// ```
635+
fn annotate_struct_field(
636+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
637+
ty: ty::Ty<'tcx>,
638+
field: &mir::Field,
639+
) -> Option<(Span, String)> {
640+
// Expect our local to be a reference to a struct of some kind.
641+
if let ty::TyKind::Ref(_, ty, _) = ty.sty {
642+
if let ty::TyKind::Adt(def, _) = ty.sty {
643+
let field = def.all_fields().nth(field.index())?;
644+
// Use the HIR types to construct the diagnostic message.
645+
let node_id = tcx.hir.as_local_node_id(field.did)?;
646+
let node = tcx.hir.find(node_id)?;
647+
// Now we're dealing with the actual struct that we're going to suggest a change to,
648+
// we can expect a field that is an immutable reference to a type.
649+
if let hir::Node::Field(field) = node {
650+
if let hir::TyKind::Rptr(lifetime, hir::MutTy {
651+
mutbl: hir::Mutability::MutImmutable,
652+
ref ty
653+
}) = field.ty.node {
654+
// Get the snippets in two parts - the named lifetime (if there is one) and
655+
// type being referenced, that way we can reconstruct the snippet without loss
656+
// of detail.
657+
let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
658+
let lifetime_snippet = if !lifetime.is_elided() {
659+
format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
660+
} else {
661+
String::new()
662+
};
663+
664+
return Some((
665+
field.ty.span,
666+
format!(
667+
"&{}mut {}",
668+
lifetime_snippet, &*type_snippet,
669+
),
670+
));
671+
}
672+
}
673+
}
674+
}
675+
676+
None
677+
}

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+
| ---------- help: consider changing this to be mutable: `&'a mut String`
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+
| ---------- help: consider changing this to be mutable: `&'a mut String`
6+
...
47
LL | self.s.push('x');
58
| ^^^^^^ cannot borrow as mutable
69

0 commit comments

Comments
 (0)