Skip to content

Commit a05f82f

Browse files
committed
Suggest match ergonomics, not ref/ref mut
1 parent 7b10133 commit a05f82f

File tree

10 files changed

+1055
-97
lines changed

10 files changed

+1055
-97
lines changed

src/librustc/mir/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ pub struct VarBindingForm<'tcx> {
523523
/// (b) it gives a way to separate this case from the remaining cases
524524
/// for diagnostics.
525525
pub opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
526+
/// Span of the pattern in which this variable was bound.
527+
pub pat_span: Span,
526528
}
527529

528530
#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
@@ -540,7 +542,8 @@ CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
540542
impl_stable_hash_for!(struct self::VarBindingForm<'tcx> {
541543
binding_mode,
542544
opt_ty_info,
543-
opt_match_place
545+
opt_match_place,
546+
pat_span
544547
});
545548

546549
mod binding_form_impl {
@@ -710,6 +713,7 @@ impl<'tcx> LocalDecl<'tcx> {
710713
binding_mode: ty::BindingMode::BindByValue(_),
711714
opt_ty_info: _,
712715
opt_match_place: _,
716+
pat_span: _,
713717
}))) => true,
714718

715719
// FIXME: might be able to thread the distinction between
@@ -729,6 +733,7 @@ impl<'tcx> LocalDecl<'tcx> {
729733
binding_mode: ty::BindingMode::BindByValue(_),
730734
opt_ty_info: _,
731735
opt_match_place: _,
736+
pat_span: _,
732737
}))) => true,
733738

734739
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true,

src/librustc_borrowck/borrowck/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
12351235
ty::BindByReference(..) => {
12361236
let let_span = self.tcx.hir.span(node_id);
12371237
let suggestion = suggest_ref_mut(self.tcx, let_span);
1238-
if let Some((let_span, replace_str)) = suggestion {
1238+
if let Some(replace_str) = suggestion {
12391239
db.span_suggestion(
12401240
let_span,
12411241
"use a mutable reference instead",

src/librustc_mir/borrow_check/move_errors.rs

+99-88
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use rustc::hir;
11+
use core::unicode::property::Pattern_White_Space;
1212
use rustc::mir::*;
1313
use rustc::ty;
1414
use rustc_errors::DiagnosticBuilder;
@@ -36,25 +36,27 @@ use util::borrowck_errors::{BorrowckErrors, Origin};
3636
// let (&x, &y) = (&String::new(), &String::new());
3737
#[derive(Debug)]
3838
enum GroupedMoveError<'tcx> {
39-
// Match place can't be moved from
39+
// Place expression can't be moved from,
4040
// e.g. match x[0] { s => (), } where x: &[String]
41-
MovesFromMatchPlace {
41+
MovesFromPlace {
4242
original_path: Place<'tcx>,
4343
span: Span,
4444
move_from: Place<'tcx>,
4545
kind: IllegalMoveOriginKind<'tcx>,
4646
binds_to: Vec<Local>,
4747
},
48-
// Part of a pattern can't be moved from,
48+
// Part of a value expression can't be moved from,
4949
// e.g. match &String::new() { &x => (), }
50-
MovesFromPattern {
50+
MovesFromValue {
5151
original_path: Place<'tcx>,
5252
span: Span,
5353
move_from: MovePathIndex,
5454
kind: IllegalMoveOriginKind<'tcx>,
5555
binds_to: Vec<Local>,
5656
},
5757
// Everything that isn't from pattern matching.
58+
// FIXME(ashtneoi): I think this is only for moves into temporaries, as
59+
// when returning a value. Clarification needed.
5860
OtherIllegalMove {
5961
original_path: Place<'tcx>,
6062
span: Span,
@@ -119,6 +121,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
119121
opt_match_place: Some((ref opt_match_place, match_span)),
120122
binding_mode: _,
121123
opt_ty_info: _,
124+
pat_span: _,
122125
}))) = local_decl.is_user_variable
123126
{
124127
self.append_binding_error(
@@ -166,7 +169,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
166169
// Error with the match place
167170
LookupResult::Parent(_) => {
168171
for ge in &mut *grouped_errors {
169-
if let GroupedMoveError::MovesFromMatchPlace { span, binds_to, .. } = ge {
172+
if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
170173
if match_span == *span {
171174
debug!("appending local({:?}) to list", bind_to);
172175
if !binds_to.is_empty() {
@@ -184,7 +187,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
184187
} else {
185188
(vec![bind_to], match_span)
186189
};
187-
grouped_errors.push(GroupedMoveError::MovesFromMatchPlace {
190+
grouped_errors.push(GroupedMoveError::MovesFromPlace {
188191
span,
189192
move_from: match_place.clone(),
190193
original_path,
@@ -200,7 +203,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
200203
_ => unreachable!("Probably not unreachable..."),
201204
};
202205
for ge in &mut *grouped_errors {
203-
if let GroupedMoveError::MovesFromPattern {
206+
if let GroupedMoveError::MovesFromValue {
204207
span,
205208
move_from: other_mpi,
206209
binds_to,
@@ -215,7 +218,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
215218
}
216219
}
217220
debug!("found a new move error location");
218-
grouped_errors.push(GroupedMoveError::MovesFromPattern {
221+
grouped_errors.push(GroupedMoveError::MovesFromValue {
219222
span: match_span,
220223
move_from: mpi,
221224
original_path,
@@ -230,13 +233,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
230233
let (mut err, err_span) = {
231234
let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind) =
232235
match error {
233-
GroupedMoveError::MovesFromMatchPlace {
236+
GroupedMoveError::MovesFromPlace {
234237
span,
235238
ref original_path,
236239
ref kind,
237240
..
238241
} |
239-
GroupedMoveError::MovesFromPattern { span, ref original_path, ref kind, .. } |
242+
GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } |
240243
GroupedMoveError::OtherIllegalMove { span, ref original_path, ref kind } => {
241244
(span, original_path, kind)
242245
},
@@ -331,111 +334,119 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
331334
err: &mut DiagnosticBuilder<'a>,
332335
span: Span,
333336
) {
337+
let snippet = self.tcx.sess.codemap().span_to_snippet(span).unwrap();
334338
match error {
335-
GroupedMoveError::MovesFromMatchPlace {
339+
GroupedMoveError::MovesFromPlace {
336340
mut binds_to,
337341
move_from,
338342
..
339343
} => {
340-
// Ok to suggest a borrow, since the target can't be moved from
341-
// anyway.
342-
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
343-
match move_from {
344-
Place::Projection(ref proj)
345-
if self.suitable_to_remove_deref(proj, &snippet) =>
346-
{
344+
let mut suggest_change_head_expr = false;
345+
match move_from {
346+
Place::Projection(box PlaceProjection {
347+
elem: ProjectionElem::Deref,
348+
..
349+
}) => {
350+
// This is false for (e.g.) index expressions `a[b]`,
351+
// which roughly desugar to `*Index::index(&a, b)` or
352+
// `*IndexMut::index_mut(&mut a, b)`.
353+
if snippet.starts_with('*') {
347354
err.span_suggestion(
348355
span,
349356
"consider removing this dereference operator",
350357
(&snippet[1..]).to_owned(),
351358
);
352-
}
353-
_ => {
354-
err.span_suggestion(
355-
span,
356-
"consider using a reference instead",
357-
format!("&{}", snippet),
358-
);
359+
suggest_change_head_expr = true;
359360
}
360361
}
361-
362-
binds_to.sort();
363-
binds_to.dedup();
364-
for local in binds_to {
365-
let bind_to = &self.mir.local_decls[local];
366-
let binding_span = bind_to.source_info.span;
367-
err.span_label(
368-
binding_span,
369-
format!(
370-
"move occurs because {} has type `{}`, \
371-
which does not implement the `Copy` trait",
372-
bind_to.name.unwrap(),
373-
bind_to.ty
374-
),
362+
_ => {
363+
err.span_suggestion(
364+
span,
365+
"consider using a reference instead",
366+
format!("&{}", snippet),
375367
);
368+
suggest_change_head_expr = true;
376369
}
377370
}
371+
binds_to.sort();
372+
binds_to.dedup();
373+
if !suggest_change_head_expr {
374+
self.add_move_error_suggestions(err, &binds_to);
375+
}
376+
self.add_move_error_labels(err, &binds_to);
378377
}
379-
GroupedMoveError::MovesFromPattern { mut binds_to, .. } => {
380-
// Suggest ref, since there might be a move in
381-
// another match arm
378+
GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
382379
binds_to.sort();
383380
binds_to.dedup();
384-
let mut multipart_suggestion = Vec::with_capacity(binds_to.len());
385-
for (j, local) in binds_to.into_iter().enumerate() {
386-
let bind_to = &self.mir.local_decls[local];
387-
let binding_span = bind_to.source_info.span;
381+
self.add_move_error_suggestions(err, &binds_to);
382+
self.add_move_error_labels(err, &binds_to);
383+
}
384+
// No binding. Nothing to suggest.
385+
GroupedMoveError::OtherIllegalMove { .. } => (),
386+
}
387+
}
388388

389-
// Suggest ref mut when the user has already written mut.
390-
let ref_kind = match bind_to.mutability {
391-
Mutability::Not => "ref",
392-
Mutability::Mut => "ref mut",
393-
};
394-
if j == 0 {
395-
err.span_label(binding_span, format!("data moved here"));
389+
fn add_move_error_suggestions(
390+
&self,
391+
err: &mut DiagnosticBuilder<'a>,
392+
binds_to: &[Local],
393+
) {
394+
for local in binds_to {
395+
let bind_to = &self.mir.local_decls[*local];
396+
if let Some(
397+
ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
398+
pat_span,
399+
..
400+
}))
401+
) = bind_to.is_user_variable {
402+
let pat_snippet = self
403+
.tcx.sess.codemap()
404+
.span_to_snippet(pat_span)
405+
.unwrap();
406+
if pat_snippet.starts_with('&') {
407+
let pat_snippet = &pat_snippet[1..];
408+
let suggestion;
409+
if pat_snippet.starts_with("mut")
410+
&& pat_snippet["mut".len()..].starts_with(Pattern_White_Space)
411+
{
412+
suggestion = pat_snippet["mut".len()..].trim_left();
396413
} else {
397-
err.span_label(binding_span, format!("... and here"));
398-
}
399-
match bind_to.name {
400-
Some(name) => {
401-
multipart_suggestion.push((binding_span,
402-
format!("{} {}", ref_kind, name)));
403-
}
404-
None => {
405-
err.span_label(
406-
span,
407-
format!("Local {:?} is not suitable for ref", bind_to),
408-
);
409-
}
414+
suggestion = pat_snippet;
410415
}
416+
err.span_suggestion(
417+
pat_span,
418+
"consider removing this borrow operator",
419+
suggestion.to_owned(),
420+
);
411421
}
412-
err.multipart_suggestion("to prevent move, use ref or ref mut",
413-
multipart_suggestion);
414422
}
415-
// Nothing to suggest.
416-
GroupedMoveError::OtherIllegalMove { .. } => (),
417423
}
418424
}
419425

420-
fn suitable_to_remove_deref(&self, proj: &PlaceProjection<'tcx>, snippet: &str) -> bool {
421-
let is_shared_ref = |ty: ty::Ty| match ty.sty {
422-
ty::TypeVariants::TyRef(.., hir::Mutability::MutImmutable) => true,
423-
_ => false,
424-
};
426+
fn add_move_error_labels(
427+
&self,
428+
err: &mut DiagnosticBuilder<'a>,
429+
binds_to: &[Local],
430+
) {
431+
for (j, local) in binds_to.into_iter().enumerate() {
432+
let bind_to = &self.mir.local_decls[*local];
433+
let binding_span = bind_to.source_info.span;
425434

426-
proj.elem == ProjectionElem::Deref && snippet.starts_with('*') && match proj.base {
427-
Place::Local(local) => {
428-
let local_decl = &self.mir.local_decls[local];
429-
// If this is a temporary, then this could be from an
430-
// overloaded * operator.
431-
local_decl.is_user_variable.is_some() && is_shared_ref(local_decl.ty)
435+
if j == 0 {
436+
err.span_label(binding_span, format!("data moved here"));
437+
} else {
438+
err.span_label(binding_span, format!("... and here"));
432439
}
433-
Place::Promoted(_) => true,
434-
Place::Static(ref st) => is_shared_ref(st.ty),
435-
Place::Projection(ref proj) => match proj.elem {
436-
ProjectionElem::Field(_, ty) => is_shared_ref(ty),
437-
_ => false,
438-
},
440+
441+
err.span_note(
442+
binding_span,
443+
&format!(
444+
"move occurs because {} has type `{}`, \
445+
which does not implement the `Copy` trait",
446+
bind_to.name.unwrap(),
447+
bind_to.ty
448+
),
449+
);
439450
}
440451
}
441452
}

src/librustc_mir/borrow_check/mutability_errors.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
329329
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
330330
binding_mode: ty::BindingMode::BindByReference(_),
331331
..
332-
})) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
332+
})) => {
333+
let pattern_span = local_decl.source_info.span;
334+
suggest_ref_mut(self.tcx, pattern_span)
335+
.map(|replacement| (pattern_span, replacement))
336+
}
333337

334338
//
335339
ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),

src/librustc_mir/build/matches/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
357357
let visibility_scope = visibility_scope.unwrap();
358358
this.declare_binding(source_info, visibility_scope, mutability, name, mode,
359359
num_patterns, var, ty, has_guard,
360-
opt_match_place.map(|(x, y)| (x.cloned(), y)));
360+
opt_match_place.map(|(x, y)| (x.cloned(), y)),
361+
patterns[0].span);
361362
});
362363
visibility_scope
363364
}
@@ -1182,7 +1183,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11821183
var_id: NodeId,
11831184
var_ty: Ty<'tcx>,
11841185
has_guard: ArmHasGuard,
1185-
opt_match_place: Option<(Option<Place<'tcx>>, Span)>)
1186+
opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
1187+
pat_span: Span)
11861188
{
11871189
debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
11881190
visibility_scope={:?}, source_info={:?})",
@@ -1208,6 +1210,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12081210
// Instead, just abandon providing diagnostic info.
12091211
opt_ty_info: None,
12101212
opt_match_place,
1213+
pat_span,
12111214
}))),
12121215
};
12131216
let for_arm_body = self.local_decls.push(local.clone());

src/librustc_mir/build/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
763763
binding_mode,
764764
opt_ty_info,
765765
opt_match_place: Some((Some(place.clone()), span)),
766+
pat_span: span,
766767
})))
767768
};
768769
self.var_indices.insert(var, LocalsForNode::One(local));

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
3535
#![feature(try_trait)]
3636
#![feature(unicode_internals)]
3737
#![feature(step_trait)]
38+
#![feature(slice_concat_ext)]
3839

3940
#![recursion_limit="256"]
4041

0 commit comments

Comments
 (0)