]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
00cbc250bd686cf19fe2235cd9fd321f68f5c455
[rust.git] / src / librustc_borrowck / borrowck / gather_loans / move_error.rs
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;
6 use rustc::ty;
7 use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
8 use syntax::ast;
9 use syntax_pos;
10 use errors::{DiagnosticBuilder, Applicability};
11 use borrowck::gather_loans::gather_moves::PatternSource;
12
13 pub struct MoveErrorCollector<'tcx> {
14     errors: Vec<MoveError<'tcx>>
15 }
16
17 impl<'tcx> MoveErrorCollector<'tcx> {
18     pub fn new() -> MoveErrorCollector<'tcx> {
19         MoveErrorCollector {
20             errors: Vec::new()
21         }
22     }
23
24     pub fn add_error(&mut self, error: MoveError<'tcx>) {
25         self.errors.push(error);
26     }
27
28     pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
29         report_move_errors(bccx, &self.errors)
30     }
31 }
32
33 pub struct MoveError<'tcx> {
34     move_from: mc::cmt<'tcx>,
35     move_to: Option<MovePlace<'tcx>>
36 }
37
38 impl<'tcx> MoveError<'tcx> {
39     pub fn with_move_info(move_from: mc::cmt<'tcx>,
40                           move_to: Option<MovePlace<'tcx>>)
41                           -> MoveError<'tcx> {
42         MoveError {
43             move_from,
44             move_to,
45         }
46     }
47 }
48
49 #[derive(Clone)]
50 pub struct MovePlace<'tcx> {
51     pub span: syntax_pos::Span,
52     pub name: ast::Name,
53     pub pat_source: PatternSource<'tcx>,
54 }
55
56 pub struct GroupedMoveErrors<'tcx> {
57     move_from: mc::cmt<'tcx>,
58     move_to_places: Vec<MovePlace<'tcx>>
59 }
60
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
70                 let initializer =
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) {
73                     err.span_suggestion(
74                         initializer.span,
75                         "consider using a reference instead",
76                         format!("&{}", snippet),
77                         Applicability::MaybeIncorrect // using a reference may not be the right fix
78                     );
79                 }
80             }
81             _ => {
82                 for move_to in &error.move_to_places {
83
84                     err = note_move_destination(err, move_to.span, move_to.name, is_first_note);
85                     is_first_note = false;
86                 }
87             }
88         }
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");
93         }
94         err.emit();
95         bccx.signal_error();
96     }
97 }
98
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)
104     }
105     return grouped_errors;
106
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()]
113         } else {
114             Vec::new()
115         };
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);
120                 return
121             }
122         }
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
127         })
128     }
129 }
130
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)
143         }
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)
147         }
148
149         Categorization::Downcast(ref b, _) |
150         Categorization::Interior(ref b, mc::InteriorField(_)) => {
151             match b.ty.sty {
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)
155                 }
156                 _ => {
157                     span_bug!(move_from.span, "this path should not cause illegal move");
158                 }
159             }
160         }
161
162         Categorization::Rvalue(..) |
163         Categorization::Local(..) |
164         Categorization::Upvar(..) => {
165             span_bug!(move_from.span, "this path should not cause illegal move");
166         }
167     }
168 }
169
170 fn note_move_destination(mut err: DiagnosticBuilder,
171                          move_to_span: syntax_pos::Span,
172                          pat_name: ast::Name,
173                          is_first_note: bool) -> DiagnosticBuilder {
174     if is_first_note {
175         err.span_label(
176             move_to_span,
177             format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
178                      pat_name));
179         err
180     } else {
181         err.span_label(move_to_span,
182                       format!("...and here (use `ref {0}` or `ref mut {0}`)",
183                                pat_name));
184         err
185     }
186 }