]> git.proxmox.com Git - rustc.git/blob - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / librustc_borrowck / borrowck / gather_loans / move_error.rs
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;
13 use rustc::middle::mem_categorization::Categorization;
14 use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
15 use rustc::middle::ty;
16 use syntax::ast;
17 use syntax::codemap;
18 use rustc_front::hir;
19
20 pub struct MoveErrorCollector<'tcx> {
21 errors: Vec<MoveError<'tcx>>
22 }
23
24 impl<'tcx> MoveErrorCollector<'tcx> {
25 pub fn new() -> MoveErrorCollector<'tcx> {
26 MoveErrorCollector {
27 errors: Vec::new()
28 }
29 }
30
31 pub fn add_error(&mut self, error: MoveError<'tcx>) {
32 self.errors.push(error);
33 }
34
35 pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
36 report_move_errors(bccx, &self.errors)
37 }
38 }
39
40 pub struct MoveError<'tcx> {
41 move_from: mc::cmt<'tcx>,
42 move_to: Option<MoveSpanAndPath>
43 }
44
45 impl<'tcx> MoveError<'tcx> {
46 pub fn with_move_info(move_from: mc::cmt<'tcx>,
47 move_to: Option<MoveSpanAndPath>)
48 -> MoveError<'tcx> {
49 MoveError {
50 move_from: move_from,
51 move_to: move_to,
52 }
53 }
54 }
55
56 #[derive(Clone)]
57 pub struct MoveSpanAndPath {
58 pub span: codemap::Span,
59 pub name: ast::Name,
60 }
61
62 pub struct GroupedMoveErrors<'tcx> {
63 move_from: mc::cmt<'tcx>,
64 move_to_places: Vec<MoveSpanAndPath>
65 }
66
67 fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
68 errors: &Vec<MoveError<'tcx>>) {
69 let grouped_errors = group_errors_with_same_origin(errors);
70 for error in &grouped_errors {
71 report_cannot_move_out_of(bccx, error.move_from.clone());
72 let mut is_first_note = true;
73 for move_to in &error.move_to_places {
74 note_move_destination(bccx, move_to.span,
75 move_to.name, is_first_note);
76 is_first_note = false;
77 }
78 }
79 }
80
81 fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
82 -> Vec<GroupedMoveErrors<'tcx>> {
83 let mut grouped_errors = Vec::new();
84 for error in errors {
85 append_to_grouped_errors(&mut grouped_errors, error)
86 }
87 return grouped_errors;
88
89 fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
90 error: &MoveError<'tcx>) {
91 let move_from_id = error.move_from.id;
92 debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
93 let move_to = if error.move_to.is_some() {
94 vec!(error.move_to.clone().unwrap())
95 } else {
96 Vec::new()
97 };
98 for ge in &mut *grouped_errors {
99 if move_from_id == ge.move_from.id && error.move_to.is_some() {
100 debug!("appending move_to to list");
101 ge.move_to_places.extend(move_to);
102 return
103 }
104 }
105 debug!("found a new move from location");
106 grouped_errors.push(GroupedMoveErrors {
107 move_from: error.move_from.clone(),
108 move_to_places: move_to
109 })
110 }
111 }
112
113 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
114 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
115 move_from: mc::cmt<'tcx>) {
116 match move_from.cat {
117 Categorization::Deref(_, _, mc::BorrowedPtr(..)) |
118 Categorization::Deref(_, _, mc::Implicit(..)) |
119 Categorization::Deref(_, _, mc::UnsafePtr(..)) |
120 Categorization::StaticItem => {
121 span_err!(bccx, move_from.span, E0507,
122 "cannot move out of {}",
123 move_from.descriptive_string(bccx.tcx));
124 }
125
126 Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => {
127 let expr = bccx.tcx.map.expect_expr(move_from.id);
128 if let hir::ExprIndex(..) = expr.node {
129 span_err!(bccx, move_from.span, E0508,
130 "cannot move out of type `{}`, \
131 a non-copy fixed-size array",
132 b.ty);
133 }
134 }
135
136 Categorization::Downcast(ref b, _) |
137 Categorization::Interior(ref b, mc::InteriorField(_)) => {
138 match b.ty.sty {
139 ty::TyStruct(def, _) |
140 ty::TyEnum(def, _) if def.has_dtor() => {
141 span_err!(bccx, move_from.span, E0509,
142 "cannot move out of type `{}`, \
143 which defines the `Drop` trait",
144 b.ty);
145 },
146 _ => {
147 bccx.span_bug(move_from.span, "this path should not cause illegal move")
148 }
149 }
150 }
151 _ => {
152 bccx.span_bug(move_from.span, "this path should not cause illegal move")
153 }
154 }
155 }
156
157 fn note_move_destination(bccx: &BorrowckCtxt,
158 move_to_span: codemap::Span,
159 pat_name: ast::Name,
160 is_first_note: bool) {
161 if is_first_note {
162 bccx.span_note(
163 move_to_span,
164 "attempting to move value to here");
165 bccx.fileline_help(
166 move_to_span,
167 &format!("to prevent the move, \
168 use `ref {0}` or `ref mut {0}` to capture value by \
169 reference",
170 pat_name));
171 } else {
172 bccx.span_note(move_to_span,
173 &format!("and here (use `ref {0}` or `ref mut {0}`)",
174 pat_name));
175 }
176 }