]>
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; | |
85aaf69f | 13 | use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; |
1a4d82fc JJ |
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); | |
85aaf69f | 71 | for error in &grouped_errors { |
1a4d82fc JJ |
72 | report_cannot_move_out_of(bccx, error.move_from.clone()); |
73 | let mut is_first_note = true; | |
85aaf69f | 74 | for move_to in &error.move_to_places { |
1a4d82fc JJ |
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(); | |
85aaf69f | 85 | for error in errors { |
1a4d82fc JJ |
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 | }; | |
85aaf69f | 99 | for ge in &mut *grouped_errors { |
1a4d82fc JJ |
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 | ||
85aaf69f | 114 | // (keep in sync with gather_moves::check_and_get_illegal_move_origin ) |
1a4d82fc JJ |
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(..)) | | |
85aaf69f | 120 | mc::cat_deref(_, _, mc::UnsafePtr(..)) | |
1a4d82fc JJ |
121 | mc::cat_static_item => { |
122 | bccx.span_err(move_from.span, | |
123 | &format!("cannot move out of {}", | |
c34b1796 | 124 | move_from.descriptive_string(bccx.tcx))); |
1a4d82fc JJ |
125 | } |
126 | ||
85aaf69f SL |
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", | |
c34b1796 | 133 | b.ty.user_string(bccx.tcx))); |
85aaf69f SL |
134 | } |
135 | } | |
136 | ||
1a4d82fc | 137 | mc::cat_downcast(ref b, _) | |
85aaf69f | 138 | mc::cat_interior(ref b, mc::InteriorField(_)) => { |
1a4d82fc JJ |
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", | |
c34b1796 | 146 | b.ty.user_string(bccx.tcx))); |
1a4d82fc JJ |
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"); | |
c34b1796 | 168 | bccx.fileline_help( |
1a4d82fc JJ |
169 | move_to_span, |
170 | &format!("to prevent the move, \ | |
171 | use `ref {0}` or `ref mut {0}` to capture value by \ | |
172 | reference", | |
c34b1796 | 173 | pat_name)); |
1a4d82fc JJ |
174 | } else { |
175 | bccx.span_note(move_to_span, | |
176 | &format!("and here (use `ref {0}` or `ref mut {0}`)", | |
c34b1796 | 177 | pat_name)); |
1a4d82fc JJ |
178 | } |
179 | } |