]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
rustdoc: Hide `self: Box<Self>` in list of deref methods
[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::mem_categorization::Categorization;
14 use rustc::middle::mem_categorization::NoteClosureEnv;
15 use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
16 use rustc::ty;
17 use syntax::ast;
18 use syntax_pos;
19 use errors::DiagnosticBuilder;
20 use borrowck::gather_loans::gather_moves::PatternSource;
21
22 pub struct MoveErrorCollector<'tcx> {
23     errors: Vec<MoveError<'tcx>>
24 }
25
26 impl<'tcx> MoveErrorCollector<'tcx> {
27     pub fn new() -> MoveErrorCollector<'tcx> {
28         MoveErrorCollector {
29             errors: Vec::new()
30         }
31     }
32
33     pub fn add_error(&mut self, error: MoveError<'tcx>) {
34         self.errors.push(error);
35     }
36
37     pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
38         report_move_errors(bccx, &self.errors)
39     }
40 }
41
42 pub struct MoveError<'tcx> {
43     move_from: mc::cmt<'tcx>,
44     move_to: Option<MovePlace<'tcx>>
45 }
46
47 impl<'tcx> MoveError<'tcx> {
48     pub fn with_move_info(move_from: mc::cmt<'tcx>,
49                           move_to: Option<MovePlace<'tcx>>)
50                           -> MoveError<'tcx> {
51         MoveError {
52             move_from: move_from,
53             move_to: move_to,
54         }
55     }
56 }
57
58 #[derive(Clone)]
59 pub struct MovePlace<'tcx> {
60     pub span: syntax_pos::Span,
61     pub name: ast::Name,
62     pub pat_source: PatternSource<'tcx>,
63 }
64
65 pub struct GroupedMoveErrors<'tcx> {
66     move_from: mc::cmt<'tcx>,
67     move_to_places: Vec<MovePlace<'tcx>>
68 }
69
70 fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec<MoveError<'tcx>>) {
71     let grouped_errors = group_errors_with_same_origin(errors);
72     for error in &grouped_errors {
73         let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
74         let mut is_first_note = true;
75         match error.move_to_places.get(0) {
76             Some(&MovePlace { pat_source: PatternSource::LetDecl(ref e), .. }) => {
77                 // ignore patterns that are found at the top-level of a `let`;
78                 // see `get_pattern_source()` for details
79                 let initializer =
80                     e.init.as_ref().expect("should have an initializer to get an error");
81                 if let Ok(snippet) = bccx.tcx.sess.codemap().span_to_snippet(initializer.span) {
82                     err.span_suggestion(initializer.span,
83                                         "consider using a reference instead",
84                                         format!("&{}", snippet));
85                 }
86             }
87             _ => {
88                 for move_to in &error.move_to_places {
89
90                     err = note_move_destination(err, move_to.span, move_to.name, is_first_note);
91                     is_first_note = false;
92                 }
93             }
94         }
95         if let NoteClosureEnv(upvar_id) = error.move_from.note {
96             err.span_label(bccx.tcx.hir.span(upvar_id.var_id),
97                            "captured outer variable");
98         }
99         err.emit();
100
101     }
102 }
103
104 fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
105                                        -> Vec<GroupedMoveErrors<'tcx>> {
106     let mut grouped_errors = Vec::new();
107     for error in errors {
108         append_to_grouped_errors(&mut grouped_errors, error)
109     }
110     return grouped_errors;
111
112     fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
113                                       error: &MoveError<'tcx>) {
114         let move_from_id = error.move_from.id;
115         debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
116         let move_to = if error.move_to.is_some() {
117             vec![error.move_to.clone().unwrap()]
118         } else {
119             Vec::new()
120         };
121         for ge in &mut *grouped_errors {
122             if move_from_id == ge.move_from.id && error.move_to.is_some() {
123                 debug!("appending move_to to list");
124                 ge.move_to_places.extend(move_to);
125                 return
126             }
127         }
128         debug!("found a new move from location");
129         grouped_errors.push(GroupedMoveErrors {
130             move_from: error.move_from.clone(),
131             move_to_places: move_to
132         })
133     }
134 }
135
136 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
137 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
138                                        move_from: mc::cmt<'tcx>)
139                                        -> DiagnosticBuilder<'a> {
140     match move_from.cat {
141         Categorization::Deref(.., mc::BorrowedPtr(..)) |
142         Categorization::Deref(.., mc::Implicit(..)) |
143         Categorization::Deref(.., mc::UnsafePtr(..)) |
144         Categorization::StaticItem => {
145             let mut err = struct_span_err!(bccx, move_from.span, E0507,
146                              "cannot move out of {}",
147                              move_from.descriptive_string(bccx.tcx));
148             err.span_label(
149                 move_from.span,
150                 format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx))
151                 );
152             err
153         }
154
155         Categorization::Interior(ref b, mc::InteriorElement(ik, _)) => {
156             match (&b.ty.sty, ik) {
157                 (&ty::TySlice(..), _) |
158                 (_, Kind::Index) => {
159                     let mut err = struct_span_err!(bccx, move_from.span, E0508,
160                                                    "cannot move out of type `{}`, \
161                                                     a non-copy array",
162                                                    b.ty);
163                     err.span_label(move_from.span, "cannot move out of here");
164                     err
165                 }
166                 (_, Kind::Pattern) => {
167                     span_bug!(move_from.span, "this path should not cause illegal move");
168                 }
169             }
170         }
171
172         Categorization::Downcast(ref b, _) |
173         Categorization::Interior(ref b, mc::InteriorField(_)) => {
174             match b.ty.sty {
175                 ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => {
176                     let mut err = struct_span_err!(bccx, move_from.span, E0509,
177                                                    "cannot move out of type `{}`, \
178                                                    which implements the `Drop` trait",
179                                                    b.ty);
180                     err.span_label(move_from.span, "cannot move out of here");
181                     err
182                 },
183                 _ => {
184                     span_bug!(move_from.span, "this path should not cause illegal move");
185                 }
186             }
187         }
188         _ => {
189             span_bug!(move_from.span, "this path should not cause illegal move");
190         }
191     }
192 }
193
194 fn note_move_destination(mut err: DiagnosticBuilder,
195                          move_to_span: syntax_pos::Span,
196                          pat_name: ast::Name,
197                          is_first_note: bool) -> DiagnosticBuilder {
198     if is_first_note {
199         err.span_label(
200             move_to_span,
201             format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
202                      pat_name));
203         err
204     } else {
205         err.span_label(move_to_span,
206                       format!("...and here (use `ref {0}` or `ref mut {0}`)",
207                                pat_name));
208         err
209     }
210 }