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.
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.
11 use middle::mem_categorization as mc;
12 use middle::borrowck::BorrowckCtxt;
15 use std::cell::RefCell;
18 use syntax::print::pprust;
19 use util::ppaux::UserString;
21 pub struct MoveErrorCollector {
22 errors: RefCell<Vec<MoveError>>
25 impl MoveErrorCollector {
26 pub fn new() -> MoveErrorCollector {
28 errors: RefCell::new(Vec::new())
32 pub fn add_error(&self, error: MoveError) {
33 self.errors.borrow_mut().push(error);
36 pub fn report_potential_errors(&self, bccx: &BorrowckCtxt) {
37 report_move_errors(bccx, self.errors.borrow().deref())
41 pub struct MoveError {
43 move_to: Option<MoveSpanAndPath>
47 pub fn with_move_info(move_from: mc::cmt,
48 move_to: Option<MoveSpanAndPath>)
58 pub struct MoveSpanAndPath {
59 pub span: codemap::Span,
63 pub struct GroupedMoveErrors {
65 move_to_places: Vec<MoveSpanAndPath>
68 fn report_move_errors(bccx: &BorrowckCtxt, errors: &Vec<MoveError>) {
69 let grouped_errors = group_errors_with_same_origin(errors);
70 for error in grouped_errors.iter() {
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.iter() {
74 note_move_destination(bccx, move_to.span,
75 &move_to.ident, is_first_note);
76 is_first_note = false;
81 fn group_errors_with_same_origin(errors: &Vec<MoveError>)
82 -> Vec<GroupedMoveErrors> {
83 let mut grouped_errors = Vec::new();
84 for error in errors.iter() {
85 append_to_grouped_errors(&mut grouped_errors, error)
87 return grouped_errors;
89 fn append_to_grouped_errors(grouped_errors: &mut Vec<GroupedMoveErrors>,
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())
98 for ge in grouped_errors.iter_mut() {
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.push_all_move(move_to);
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
113 fn report_cannot_move_out_of(bccx: &BorrowckCtxt, move_from: mc::cmt) {
114 match move_from.cat {
115 mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
116 mc::cat_deref(_, _, mc::Implicit(..)) |
117 mc::cat_deref(_, _, mc::GcPtr) |
118 mc::cat_deref(_, _, mc::UnsafePtr(..)) |
119 mc::cat_upvar(..) | mc::cat_static_item |
120 mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => {
123 format!("cannot move out of {}",
124 bccx.cmt_to_string(&*move_from)).as_slice());
127 mc::cat_downcast(ref b) |
128 mc::cat_interior(ref b, _) => {
129 match ty::get(b.ty).sty {
130 ty::ty_struct(did, _)
131 | ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => {
134 format!("cannot move out of type `{}`, \
135 which defines the `Drop` trait",
136 b.ty.user_string(bccx.tcx)).as_slice());
138 _ => fail!("this path should not cause illegal move")
141 _ => fail!("this path should not cause illegal move")
145 fn note_move_destination(bccx: &BorrowckCtxt,
146 move_to_span: codemap::Span,
147 pat_ident: &ast::Ident,
148 is_first_note: bool) {
149 let pat_name = pprust::ident_to_string(pat_ident);
153 format!("attempting to move value to here (to prevent the move, \
154 use `ref {0}` or `ref mut {0}` to capture value by \
156 pat_name).as_slice());
158 bccx.span_note(move_to_span,
159 format!("and here (use `ref {0}` or `ref mut {0}`)",
160 pat_name).as_slice());