]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
Auto merge of #30641 - tsion:match-range, r=eddyb
[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::InteriorOffsetKind as Kind;
15 use rustc::middle::ty;
16 use syntax::ast;
17 use syntax::codemap;
18 use syntax::errors::DiagnosticBuilder;
19 use rustc_front::hir;
20
21 pub struct MoveErrorCollector<'tcx> {
22     errors: Vec<MoveError<'tcx>>
23 }
24
25 impl<'tcx> MoveErrorCollector<'tcx> {
26     pub fn new() -> MoveErrorCollector<'tcx> {
27         MoveErrorCollector {
28             errors: Vec::new()
29         }
30     }
31
32     pub fn add_error(&mut self, error: MoveError<'tcx>) {
33         self.errors.push(error);
34     }
35
36     pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
37         report_move_errors(bccx, &self.errors)
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 name: ast::Name,
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         let mut err = 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(&mut err, move_to.span,
76                                   move_to.name, is_first_note);
77             is_first_note = false;
78         }
79         err.emit();
80     }
81 }
82
83 fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
84                                        -> Vec<GroupedMoveErrors<'tcx>> {
85     let mut grouped_errors = Vec::new();
86     for error in errors {
87         append_to_grouped_errors(&mut grouped_errors, error)
88     }
89     return grouped_errors;
90
91     fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
92                                       error: &MoveError<'tcx>) {
93         let move_from_id = error.move_from.id;
94         debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
95         let move_to = if error.move_to.is_some() {
96             vec!(error.move_to.clone().unwrap())
97         } else {
98             Vec::new()
99         };
100         for ge in &mut *grouped_errors {
101             if move_from_id == ge.move_from.id && error.move_to.is_some() {
102                 debug!("appending move_to to list");
103                 ge.move_to_places.extend(move_to);
104                 return
105             }
106         }
107         debug!("found a new move from location");
108         grouped_errors.push(GroupedMoveErrors {
109             move_from: error.move_from.clone(),
110             move_to_places: move_to
111         })
112     }
113 }
114
115 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
116 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
117                                        move_from: mc::cmt<'tcx>)
118                                        -> DiagnosticBuilder<'a> {
119     match move_from.cat {
120         Categorization::Deref(_, _, mc::BorrowedPtr(..)) |
121         Categorization::Deref(_, _, mc::Implicit(..)) |
122         Categorization::Deref(_, _, mc::UnsafePtr(..)) |
123         Categorization::StaticItem => {
124             struct_span_err!(bccx, move_from.span, E0507,
125                              "cannot move out of {}",
126                              move_from.descriptive_string(bccx.tcx))
127         }
128
129         Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => {
130             let expr = bccx.tcx.map.expect_expr(move_from.id);
131             if let hir::ExprIndex(..) = expr.node {
132                 struct_span_err!(bccx, move_from.span, E0508,
133                                  "cannot move out of type `{}`, \
134                                   a non-copy fixed-size array",
135                                  b.ty)
136             } else {
137                 bccx.span_bug(move_from.span, "this path should not cause illegal move");
138                 unreachable!();
139             }
140         }
141
142         Categorization::Downcast(ref b, _) |
143         Categorization::Interior(ref b, mc::InteriorField(_)) => {
144             match b.ty.sty {
145                 ty::TyStruct(def, _) |
146                 ty::TyEnum(def, _) if def.has_dtor() => {
147                     struct_span_err!(bccx, move_from.span, E0509,
148                                      "cannot move out of type `{}`, \
149                                       which defines the `Drop` trait",
150                                      b.ty)
151                 },
152                 _ => {
153                     bccx.span_bug(move_from.span, "this path should not cause illegal move");
154                     unreachable!();
155                 }
156             }
157         }
158         _ => {
159             bccx.span_bug(move_from.span, "this path should not cause illegal move");
160             unreachable!();
161         }
162     }
163 }
164
165 fn note_move_destination(err: &mut DiagnosticBuilder,
166                          move_to_span: codemap::Span,
167                          pat_name: ast::Name,
168                          is_first_note: bool) {
169     if is_first_note {
170         err.span_note(
171             move_to_span,
172             "attempting to move value to here");
173         err.fileline_help(
174             move_to_span,
175             &format!("to prevent the move, \
176                       use `ref {0}` or `ref mut {0}` to capture value by \
177                       reference",
178                      pat_name));
179     } else {
180         err.span_note(move_to_span,
181                       &format!("and here (use `ref {0}` or `ref mut {0}`)",
182                                pat_name));
183     }
184 }