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