]> git.proxmox.com Git - rustc.git/blame - src/librustc_borrowck/borrowck/gather_loans/move_error.rs
New upstream version 1.21.0+dfsg1
[rustc.git] / src / librustc_borrowck / borrowck / gather_loans / move_error.rs
CommitLineData
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
11use borrowck::BorrowckCtxt;
12use rustc::middle::mem_categorization as mc;
92a42be0 13use rustc::middle::mem_categorization::Categorization;
7cac9316 14use rustc::middle::mem_categorization::NoteClosureEnv;
85aaf69f 15use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
54a0048b 16use rustc::ty;
1a4d82fc 17use syntax::ast;
3157f602
XL
18use syntax_pos;
19use errors::DiagnosticBuilder;
7cac9316 20use borrowck::gather_loans::gather_moves::PatternSource;
1a4d82fc
JJ
21
22pub struct MoveErrorCollector<'tcx> {
92a42be0 23 errors: Vec<MoveError<'tcx>>
1a4d82fc
JJ
24}
25
26impl<'tcx> MoveErrorCollector<'tcx> {
27 pub fn new() -> MoveErrorCollector<'tcx> {
28 MoveErrorCollector {
92a42be0 29 errors: Vec::new()
1a4d82fc
JJ
30 }
31 }
32
92a42be0
SL
33 pub fn add_error(&mut self, error: MoveError<'tcx>) {
34 self.errors.push(error);
1a4d82fc
JJ
35 }
36
37 pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
92a42be0 38 report_move_errors(bccx, &self.errors)
1a4d82fc
JJ
39 }
40}
41
42pub struct MoveError<'tcx> {
43 move_from: mc::cmt<'tcx>,
7cac9316 44 move_to: Option<MovePlace<'tcx>>
1a4d82fc
JJ
45}
46
47impl<'tcx> MoveError<'tcx> {
48 pub fn with_move_info(move_from: mc::cmt<'tcx>,
7cac9316 49 move_to: Option<MovePlace<'tcx>>)
1a4d82fc
JJ
50 -> MoveError<'tcx> {
51 MoveError {
3b2f2976
XL
52 move_from,
53 move_to,
1a4d82fc
JJ
54 }
55 }
56}
57
58#[derive(Clone)]
7cac9316 59pub struct MovePlace<'tcx> {
3157f602 60 pub span: syntax_pos::Span,
b039eaaf 61 pub name: ast::Name,
7cac9316 62 pub pat_source: PatternSource<'tcx>,
1a4d82fc
JJ
63}
64
65pub struct GroupedMoveErrors<'tcx> {
66 move_from: mc::cmt<'tcx>,
7cac9316 67 move_to_places: Vec<MovePlace<'tcx>>
1a4d82fc
JJ
68}
69
7cac9316 70fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec<MoveError<'tcx>>) {
1a4d82fc 71 let grouped_errors = group_errors_with_same_origin(errors);
85aaf69f 72 for error in &grouped_errors {
9cc50fc6 73 let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
1a4d82fc 74 let mut is_first_note = true;
7cac9316
XL
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 {
3b2f2976
XL
96 let var_node_id = bccx.tcx.hir.def_index_to_node_id(upvar_id.var_id);
97 err.span_label(bccx.tcx.hir.span(var_node_id),
7cac9316 98 "captured outer variable");
1a4d82fc 99 }
9cc50fc6 100 err.emit();
1a4d82fc
JJ
101 }
102}
103
104fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
105 -> Vec<GroupedMoveErrors<'tcx>> {
106 let mut grouped_errors = Vec::new();
85aaf69f 107 for error in errors {
1a4d82fc
JJ
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() {
c30ab7b3 117 vec![error.move_to.clone().unwrap()]
1a4d82fc
JJ
118 } else {
119 Vec::new()
120 };
85aaf69f 121 for ge in &mut *grouped_errors {
1a4d82fc
JJ
122 if move_from_id == ge.move_from.id && error.move_to.is_some() {
123 debug!("appending move_to to list");
62682a34 124 ge.move_to_places.extend(move_to);
1a4d82fc
JJ
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
85aaf69f 136// (keep in sync with gather_moves::check_and_get_illegal_move_origin )
1a4d82fc 137fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
9cc50fc6
SL
138 move_from: mc::cmt<'tcx>)
139 -> DiagnosticBuilder<'a> {
1a4d82fc 140 match move_from.cat {
7cac9316
XL
141 Categorization::Deref(_, mc::BorrowedPtr(..)) |
142 Categorization::Deref(_, mc::Implicit(..)) |
143 Categorization::Deref(_, mc::UnsafePtr(..)) |
92a42be0 144 Categorization::StaticItem => {
a7813a04 145 let mut err = struct_span_err!(bccx, move_from.span, E0507,
9cc50fc6 146 "cannot move out of {}",
a7813a04
XL
147 move_from.descriptive_string(bccx.tcx));
148 err.span_label(
149 move_from.span,
7cac9316 150 format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx))
a7813a04
XL
151 );
152 err
1a4d82fc
JJ
153 }
154
7cac9316 155 Categorization::Interior(ref b, mc::InteriorElement(ik)) => {
3b2f2976
XL
156 let type_name = match (&b.ty.sty, ik) {
157 (&ty::TyArray(_, _), Kind::Index) => "array",
158 (&ty::TySlice(_), _) => "slice",
159 _ => {
9e0c209e 160 span_bug!(move_from.span, "this path should not cause illegal move");
3b2f2976
XL
161 },
162 };
163 let mut err = struct_span_err!(bccx, move_from.span, E0508,
164 "cannot move out of type `{}`, \
165 a non-copy {}",
166 b.ty, type_name);
167 err.span_label(move_from.span, "cannot move out of here");
168 err
85aaf69f
SL
169 }
170
92a42be0
SL
171 Categorization::Downcast(ref b, _) |
172 Categorization::Interior(ref b, mc::InteriorField(_)) => {
1a4d82fc 173 match b.ty.sty {
8bb4bdeb 174 ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => {
a7813a04
XL
175 let mut err = struct_span_err!(bccx, move_from.span, E0509,
176 "cannot move out of type `{}`, \
177 which implements the `Drop` trait",
178 b.ty);
7cac9316 179 err.span_label(move_from.span, "cannot move out of here");
a7813a04 180 err
1a4d82fc
JJ
181 },
182 _ => {
54a0048b 183 span_bug!(move_from.span, "this path should not cause illegal move");
1a4d82fc
JJ
184 }
185 }
186 }
187 _ => {
54a0048b 188 span_bug!(move_from.span, "this path should not cause illegal move");
1a4d82fc
JJ
189 }
190 }
191}
192
a7813a04 193fn note_move_destination(mut err: DiagnosticBuilder,
3157f602 194 move_to_span: syntax_pos::Span,
b039eaaf 195 pat_name: ast::Name,
a7813a04 196 is_first_note: bool) -> DiagnosticBuilder {
1a4d82fc 197 if is_first_note {
a7813a04 198 err.span_label(
1a4d82fc 199 move_to_span,
7cac9316 200 format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
9cc50fc6 201 pat_name));
a7813a04 202 err
1a4d82fc 203 } else {
a7813a04 204 err.span_label(move_to_span,
7cac9316 205 format!("...and here (use `ref {0}` or `ref mut {0}`)",
c34b1796 206 pat_name));
a7813a04 207 err
1a4d82fc
JJ
208 }
209}