Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NLL is missing struct field suggestion #54831

Merged
merged 2 commits into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/librustc_mir/borrow_check/mutability_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,38 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);

match the_place_err {
// Suggest making an existing shared borrow in a struct definition a mutable borrow.
//
// This is applicable when we have a deref of a field access to a deref of a local -
// something like `*((*_1).0`. The local that we get will be a reference to the
// struct we've got a field access of (it must be a reference since there's a deref
// after the field access).
Place::Projection(box Projection {
base: Place::Projection(box Projection {
base: Place::Projection(box Projection {
base,
elem: ProjectionElem::Deref,
}),
elem: ProjectionElem::Field(field, _),
}),
elem: ProjectionElem::Deref,
}) => {
err.span_label(span, format!("cannot {ACT}", ACT = act));

if let Some((span, message)) = annotate_struct_field(
self.infcx.tcx,
base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx),
field,
) {
err.span_suggestion_with_applicability(
span,
"consider changing this to be mutable",
message,
Applicability::MaybeIncorrect,
);
}
},

// Suggest removing a `&mut` from the use of a mutable reference.
Place::Local(local)
if {
Expand Down Expand Up @@ -592,3 +624,54 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
fn is_closure_or_generator(ty: ty::Ty) -> bool {
ty.is_closure() || ty.is_generator()
}

/// Add a suggestion to a struct definition given a field access to a local.
/// This function expects the local to be a reference to a struct in order to produce a suggestion.
///
/// ```text
/// LL | s: &'a String
/// | ---------- use `&'a mut String` here to make mutable
/// ```
fn annotate_struct_field(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
ty: ty::Ty<'tcx>,
field: &mir::Field,
) -> Option<(Span, String)> {
// Expect our local to be a reference to a struct of some kind.
if let ty::TyKind::Ref(_, ty, _) = ty.sty {
if let ty::TyKind::Adt(def, _) = ty.sty {
let field = def.all_fields().nth(field.index())?;
// Use the HIR types to construct the diagnostic message.
let node_id = tcx.hir.as_local_node_id(field.did)?;
let node = tcx.hir.find(node_id)?;
// Now we're dealing with the actual struct that we're going to suggest a change to,
// we can expect a field that is an immutable reference to a type.
if let hir::Node::Field(field) = node {
if let hir::TyKind::Rptr(lifetime, hir::MutTy {
mutbl: hir::Mutability::MutImmutable,
ref ty
}) = field.ty.node {
// Get the snippets in two parts - the named lifetime (if there is one) and
// type being referenced, that way we can reconstruct the snippet without loss
// of detail.
let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
let lifetime_snippet = if !lifetime.is_elided() {
format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
} else {
String::new()
};

return Some((
field.ty.span,
format!(
"&{}mut {}",
lifetime_snippet, &*type_snippet,
),
));
}
}
}
}

None
}
3 changes: 3 additions & 0 deletions src/test/ui/did_you_mean/issue-38147-2.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
--> $DIR/issue-38147-2.rs:17:9
|
LL | s: &'a String
| ---------- help: consider changing this to be mutable: `&'a mut String`
davidtwco marked this conversation as resolved.
Show resolved Hide resolved
...
LL | self.s.push('x');
| ^^^^^^ cannot borrow as mutable

Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/did_you_mean/issue-38147-3.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
--> $DIR/issue-38147-3.rs:17:9
|
LL | s: &'a String
| ---------- help: consider changing this to be mutable: `&'a mut String`
...
LL | self.s.push('x');
| ^^^^^^ cannot borrow as mutable

Expand Down