]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
Merge pull request #20510 from tshepang/patch-6
[rust.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::ty;
14 use rustc::util::ppaux::UserString;
15 use std::cell::RefCell;
16 use syntax::ast;
17 use syntax::codemap;
18 use syntax::print::pprust;
19
20 pub struct MoveErrorCollector<'tcx> {
21     errors: RefCell<Vec<MoveError<'tcx>>>
22 }
23
24 impl<'tcx> MoveErrorCollector<'tcx> {
25     pub fn new() -> MoveErrorCollector<'tcx> {
26         MoveErrorCollector {
27             errors: RefCell::new(Vec::new())
28         }
29     }
30
31     pub fn add_error(&self, error: MoveError<'tcx>) {
32         self.errors.borrow_mut().push(error);
33     }
34
35     pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
36         report_move_errors(bccx, &*self.errors.borrow())
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 ident: ast::Ident
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.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;
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.iter() {
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 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.extend(move_to.into_iter());
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 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
114                                        move_from: mc::cmt<'tcx>) {
115     match move_from.cat {
116         mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
117         mc::cat_deref(_, _, mc::Implicit(..)) |
118         mc::cat_deref(_, _, mc::UnsafePtr(..)) |
119         mc::cat_static_item => {
120             bccx.span_err(
121                 move_from.span,
122                 format!("cannot move out of {}",
123                         bccx.cmt_to_string(&*move_from))[]);
124         }
125
126         mc::cat_downcast(ref b, _) |
127         mc::cat_interior(ref b, _) => {
128             match b.ty.sty {
129                 ty::ty_struct(did, _)
130                 | ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => {
131                     bccx.span_err(
132                         move_from.span,
133                         format!("cannot move out of type `{}`, \
134                                  which defines the `Drop` trait",
135                                 b.ty.user_string(bccx.tcx))[]);
136                 },
137                 _ => panic!("this path should not cause illegal move")
138             }
139         }
140         _ => panic!("this path should not cause illegal move")
141     }
142 }
143
144 fn note_move_destination(bccx: &BorrowckCtxt,
145                          move_to_span: codemap::Span,
146                          pat_ident: &ast::Ident,
147                          is_first_note: bool) {
148     let pat_name = pprust::ident_to_string(pat_ident);
149     if is_first_note {
150         bccx.span_note(
151             move_to_span,
152             "attempting to move value to here");
153         bccx.span_help(
154             move_to_span,
155             format!("to prevent the move, \
156                      use `ref {0}` or `ref mut {0}` to capture value by \
157                      reference",
158                     pat_name)[]);
159     } else {
160         bccx.span_note(move_to_span,
161                        format!("and here (use `ref {0}` or `ref mut {0}`)",
162                                pat_name)[]);
163     }
164 }