1 use borrowck::BorrowckCtxt;
2 use rustc::middle::mem_categorization as mc;
3 use rustc::middle::mem_categorization::Categorization;
4 use rustc::middle::mem_categorization::NoteClosureEnv;
5 use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
7 use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
10 use errors::{DiagnosticBuilder, Applicability};
11 use borrowck::gather_loans::gather_moves::PatternSource;
13 pub struct MoveErrorCollector<'tcx> {
14 errors: Vec<MoveError<'tcx>>
17 impl<'tcx> MoveErrorCollector<'tcx> {
18 pub fn new() -> MoveErrorCollector<'tcx> {
24 pub fn add_error(&mut self, error: MoveError<'tcx>) {
25 self.errors.push(error);
28 pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
29 report_move_errors(bccx, &self.errors)
33 pub struct MoveError<'tcx> {
34 move_from: mc::cmt<'tcx>,
35 move_to: Option<MovePlace<'tcx>>
38 impl<'tcx> MoveError<'tcx> {
39 pub fn with_move_info(move_from: mc::cmt<'tcx>,
40 move_to: Option<MovePlace<'tcx>>)
50 pub struct MovePlace<'tcx> {
51 pub span: syntax_pos::Span,
53 pub pat_source: PatternSource<'tcx>,
56 pub struct GroupedMoveErrors<'tcx> {
57 move_from: mc::cmt<'tcx>,
58 move_to_places: Vec<MovePlace<'tcx>>
61 fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &[MoveError<'tcx>]) {
62 let grouped_errors = group_errors_with_same_origin(errors);
63 for error in &grouped_errors {
64 let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
65 let mut is_first_note = true;
66 match error.move_to_places.get(0) {
67 Some(&MovePlace { pat_source: PatternSource::LetDecl(ref e), .. }) => {
68 // ignore patterns that are found at the top-level of a `let`;
69 // see `get_pattern_source()` for details
71 e.init.as_ref().expect("should have an initializer to get an error");
72 if let Ok(snippet) = bccx.tcx.sess.source_map().span_to_snippet(initializer.span) {
75 "consider using a reference instead",
76 format!("&{}", snippet),
77 Applicability::MaybeIncorrect // using a reference may not be the right fix
82 for move_to in &error.move_to_places {
84 err = note_move_destination(err, move_to.span, move_to.name, is_first_note);
85 is_first_note = false;
89 if let NoteClosureEnv(upvar_id) = error.move_from.note {
90 let var_node_id = bccx.tcx.hir().hir_to_node_id(upvar_id.var_path.hir_id);
91 err.span_label(bccx.tcx.hir().span(var_node_id),
92 "captured outer variable");
99 fn group_errors_with_same_origin<'tcx>(errors: &[MoveError<'tcx>])
100 -> Vec<GroupedMoveErrors<'tcx>> {
101 let mut grouped_errors = Vec::new();
102 for error in errors {
103 append_to_grouped_errors(&mut grouped_errors, error)
105 return grouped_errors;
107 fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
108 error: &MoveError<'tcx>) {
109 let move_from_id = error.move_from.hir_id;
110 debug!("append_to_grouped_errors(move_from_id={:?})", move_from_id);
111 let move_to = if error.move_to.is_some() {
112 vec![error.move_to.clone().unwrap()]
116 for ge in &mut *grouped_errors {
117 if move_from_id == ge.move_from.hir_id && error.move_to.is_some() {
118 debug!("appending move_to to list");
119 ge.move_to_places.extend(move_to);
123 debug!("found a new move from location");
124 grouped_errors.push(GroupedMoveErrors {
125 move_from: error.move_from.clone(),
126 move_to_places: move_to
131 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
132 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>,
133 move_from: mc::cmt<'tcx>)
134 -> DiagnosticBuilder<'a> {
135 match move_from.cat {
136 Categorization::Deref(_, mc::BorrowedPtr(..)) |
137 Categorization::Deref(_, mc::UnsafePtr(..)) |
138 Categorization::Deref(_, mc::Unique) |
139 Categorization::ThreadLocal(..) |
140 Categorization::StaticItem => {
141 bccx.cannot_move_out_of(
142 move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast)
144 Categorization::Interior(ref b, mc::InteriorElement(ik)) => {
145 bccx.cannot_move_out_of_interior_noncopy(
146 move_from.span, b.ty, Some(ik == Kind::Index), Origin::Ast)
149 Categorization::Downcast(ref b, _) |
150 Categorization::Interior(ref b, mc::InteriorField(_)) => {
152 ty::Adt(def, _) if def.has_dtor(bccx.tcx) => {
153 bccx.cannot_move_out_of_interior_of_drop(
154 move_from.span, b.ty, Origin::Ast)
157 span_bug!(move_from.span, "this path should not cause illegal move");
162 Categorization::Rvalue(..) |
163 Categorization::Local(..) |
164 Categorization::Upvar(..) => {
165 span_bug!(move_from.span, "this path should not cause illegal move");
170 fn note_move_destination(mut err: DiagnosticBuilder,
171 move_to_span: syntax_pos::Span,
173 is_first_note: bool) -> DiagnosticBuilder {
177 format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
181 err.span_label(move_to_span,
182 format!("...and here (use `ref {0}` or `ref mut {0}`)",