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