]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use borrowck::BorrowckCtxt; | |
12 | use rustc::middle::mem_categorization as mc; | |
92a42be0 | 13 | use rustc::middle::mem_categorization::Categorization; |
7cac9316 | 14 | use rustc::middle::mem_categorization::NoteClosureEnv; |
85aaf69f | 15 | use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; |
54a0048b | 16 | use rustc::ty; |
1a4d82fc | 17 | use syntax::ast; |
3157f602 XL |
18 | use syntax_pos; |
19 | use errors::DiagnosticBuilder; | |
7cac9316 | 20 | use borrowck::gather_loans::gather_moves::PatternSource; |
1a4d82fc JJ |
21 | |
22 | pub struct MoveErrorCollector<'tcx> { | |
92a42be0 | 23 | errors: Vec<MoveError<'tcx>> |
1a4d82fc JJ |
24 | } |
25 | ||
26 | impl<'tcx> MoveErrorCollector<'tcx> { | |
27 | pub fn new() -> MoveErrorCollector<'tcx> { | |
28 | MoveErrorCollector { | |
92a42be0 | 29 | errors: Vec::new() |
1a4d82fc JJ |
30 | } |
31 | } | |
32 | ||
92a42be0 SL |
33 | pub fn add_error(&mut self, error: MoveError<'tcx>) { |
34 | self.errors.push(error); | |
1a4d82fc JJ |
35 | } |
36 | ||
37 | pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) { | |
92a42be0 | 38 | report_move_errors(bccx, &self.errors) |
1a4d82fc JJ |
39 | } |
40 | } | |
41 | ||
42 | pub struct MoveError<'tcx> { | |
43 | move_from: mc::cmt<'tcx>, | |
7cac9316 | 44 | move_to: Option<MovePlace<'tcx>> |
1a4d82fc JJ |
45 | } |
46 | ||
47 | impl<'tcx> MoveError<'tcx> { | |
48 | pub fn with_move_info(move_from: mc::cmt<'tcx>, | |
7cac9316 | 49 | move_to: Option<MovePlace<'tcx>>) |
1a4d82fc JJ |
50 | -> MoveError<'tcx> { |
51 | MoveError { | |
3b2f2976 XL |
52 | move_from, |
53 | move_to, | |
1a4d82fc JJ |
54 | } |
55 | } | |
56 | } | |
57 | ||
58 | #[derive(Clone)] | |
7cac9316 | 59 | pub struct MovePlace<'tcx> { |
3157f602 | 60 | pub span: syntax_pos::Span, |
b039eaaf | 61 | pub name: ast::Name, |
7cac9316 | 62 | pub pat_source: PatternSource<'tcx>, |
1a4d82fc JJ |
63 | } |
64 | ||
65 | pub struct GroupedMoveErrors<'tcx> { | |
66 | move_from: mc::cmt<'tcx>, | |
7cac9316 | 67 | move_to_places: Vec<MovePlace<'tcx>> |
1a4d82fc JJ |
68 | } |
69 | ||
7cac9316 | 70 | fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec<MoveError<'tcx>>) { |
1a4d82fc | 71 | let grouped_errors = group_errors_with_same_origin(errors); |
85aaf69f | 72 | for error in &grouped_errors { |
9cc50fc6 | 73 | let mut err = report_cannot_move_out_of(bccx, error.move_from.clone()); |
1a4d82fc | 74 | let mut is_first_note = true; |
7cac9316 XL |
75 | match error.move_to_places.get(0) { |
76 | Some(&MovePlace { pat_source: PatternSource::LetDecl(ref e), .. }) => { | |
77 | // ignore patterns that are found at the top-level of a `let`; | |
78 | // see `get_pattern_source()` for details | |
79 | let initializer = | |
80 | e.init.as_ref().expect("should have an initializer to get an error"); | |
81 | if let Ok(snippet) = bccx.tcx.sess.codemap().span_to_snippet(initializer.span) { | |
82 | err.span_suggestion(initializer.span, | |
83 | "consider using a reference instead", | |
84 | format!("&{}", snippet)); | |
85 | } | |
86 | } | |
87 | _ => { | |
88 | for move_to in &error.move_to_places { | |
89 | ||
90 | err = note_move_destination(err, move_to.span, move_to.name, is_first_note); | |
91 | is_first_note = false; | |
92 | } | |
93 | } | |
94 | } | |
95 | if let NoteClosureEnv(upvar_id) = error.move_from.note { | |
3b2f2976 XL |
96 | let var_node_id = bccx.tcx.hir.def_index_to_node_id(upvar_id.var_id); |
97 | err.span_label(bccx.tcx.hir.span(var_node_id), | |
7cac9316 | 98 | "captured outer variable"); |
1a4d82fc | 99 | } |
9cc50fc6 | 100 | err.emit(); |
1a4d82fc JJ |
101 | } |
102 | } | |
103 | ||
104 | fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>) | |
105 | -> Vec<GroupedMoveErrors<'tcx>> { | |
106 | let mut grouped_errors = Vec::new(); | |
85aaf69f | 107 | for error in errors { |
1a4d82fc JJ |
108 | append_to_grouped_errors(&mut grouped_errors, error) |
109 | } | |
110 | return grouped_errors; | |
111 | ||
112 | fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>, | |
113 | error: &MoveError<'tcx>) { | |
114 | let move_from_id = error.move_from.id; | |
115 | debug!("append_to_grouped_errors(move_from_id={})", move_from_id); | |
116 | let move_to = if error.move_to.is_some() { | |
c30ab7b3 | 117 | vec![error.move_to.clone().unwrap()] |
1a4d82fc JJ |
118 | } else { |
119 | Vec::new() | |
120 | }; | |
85aaf69f | 121 | for ge in &mut *grouped_errors { |
1a4d82fc JJ |
122 | if move_from_id == ge.move_from.id && error.move_to.is_some() { |
123 | debug!("appending move_to to list"); | |
62682a34 | 124 | ge.move_to_places.extend(move_to); |
1a4d82fc JJ |
125 | return |
126 | } | |
127 | } | |
128 | debug!("found a new move from location"); | |
129 | grouped_errors.push(GroupedMoveErrors { | |
130 | move_from: error.move_from.clone(), | |
131 | move_to_places: move_to | |
132 | }) | |
133 | } | |
134 | } | |
135 | ||
85aaf69f | 136 | // (keep in sync with gather_moves::check_and_get_illegal_move_origin ) |
1a4d82fc | 137 | fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, |
9cc50fc6 SL |
138 | move_from: mc::cmt<'tcx>) |
139 | -> DiagnosticBuilder<'a> { | |
1a4d82fc | 140 | match move_from.cat { |
7cac9316 XL |
141 | Categorization::Deref(_, mc::BorrowedPtr(..)) | |
142 | Categorization::Deref(_, mc::Implicit(..)) | | |
143 | Categorization::Deref(_, mc::UnsafePtr(..)) | | |
92a42be0 | 144 | Categorization::StaticItem => { |
a7813a04 | 145 | let mut err = struct_span_err!(bccx, move_from.span, E0507, |
9cc50fc6 | 146 | "cannot move out of {}", |
a7813a04 XL |
147 | move_from.descriptive_string(bccx.tcx)); |
148 | err.span_label( | |
149 | move_from.span, | |
7cac9316 | 150 | format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx)) |
a7813a04 XL |
151 | ); |
152 | err | |
1a4d82fc JJ |
153 | } |
154 | ||
7cac9316 | 155 | Categorization::Interior(ref b, mc::InteriorElement(ik)) => { |
3b2f2976 XL |
156 | let type_name = match (&b.ty.sty, ik) { |
157 | (&ty::TyArray(_, _), Kind::Index) => "array", | |
158 | (&ty::TySlice(_), _) => "slice", | |
159 | _ => { | |
9e0c209e | 160 | span_bug!(move_from.span, "this path should not cause illegal move"); |
3b2f2976 XL |
161 | }, |
162 | }; | |
163 | let mut err = struct_span_err!(bccx, move_from.span, E0508, | |
164 | "cannot move out of type `{}`, \ | |
165 | a non-copy {}", | |
166 | b.ty, type_name); | |
167 | err.span_label(move_from.span, "cannot move out of here"); | |
168 | err | |
85aaf69f SL |
169 | } |
170 | ||
92a42be0 SL |
171 | Categorization::Downcast(ref b, _) | |
172 | Categorization::Interior(ref b, mc::InteriorField(_)) => { | |
1a4d82fc | 173 | match b.ty.sty { |
8bb4bdeb | 174 | ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => { |
a7813a04 XL |
175 | let mut err = struct_span_err!(bccx, move_from.span, E0509, |
176 | "cannot move out of type `{}`, \ | |
177 | which implements the `Drop` trait", | |
178 | b.ty); | |
7cac9316 | 179 | err.span_label(move_from.span, "cannot move out of here"); |
a7813a04 | 180 | err |
1a4d82fc JJ |
181 | }, |
182 | _ => { | |
54a0048b | 183 | span_bug!(move_from.span, "this path should not cause illegal move"); |
1a4d82fc JJ |
184 | } |
185 | } | |
186 | } | |
187 | _ => { | |
54a0048b | 188 | span_bug!(move_from.span, "this path should not cause illegal move"); |
1a4d82fc JJ |
189 | } |
190 | } | |
191 | } | |
192 | ||
a7813a04 | 193 | fn note_move_destination(mut err: DiagnosticBuilder, |
3157f602 | 194 | move_to_span: syntax_pos::Span, |
b039eaaf | 195 | pat_name: ast::Name, |
a7813a04 | 196 | is_first_note: bool) -> DiagnosticBuilder { |
1a4d82fc | 197 | if is_first_note { |
a7813a04 | 198 | err.span_label( |
1a4d82fc | 199 | move_to_span, |
7cac9316 | 200 | format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`", |
9cc50fc6 | 201 | pat_name)); |
a7813a04 | 202 | err |
1a4d82fc | 203 | } else { |
a7813a04 | 204 | err.span_label(move_to_span, |
7cac9316 | 205 | format!("...and here (use `ref {0}` or `ref mut {0}`)", |
c34b1796 | 206 | pat_name)); |
a7813a04 | 207 | err |
1a4d82fc JJ |
208 | } |
209 | } |