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.
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.
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
;
19 use errors
::DiagnosticBuilder
;
20 use borrowck
::gather_loans
::gather_moves
::PatternSource
;
22 pub struct MoveErrorCollector
<'tcx
> {
23 errors
: Vec
<MoveError
<'tcx
>>
26 impl<'tcx
> MoveErrorCollector
<'tcx
> {
27 pub fn new() -> MoveErrorCollector
<'tcx
> {
33 pub fn add_error(&mut self, error
: MoveError
<'tcx
>) {
34 self.errors
.push(error
);
37 pub fn report_potential_errors
<'a
>(&self, bccx
: &BorrowckCtxt
<'a
, 'tcx
>) {
38 report_move_errors(bccx
, &self.errors
)
42 pub struct MoveError
<'tcx
> {
43 move_from
: mc
::cmt
<'tcx
>,
44 move_to
: Option
<MovePlace
<'tcx
>>
47 impl<'tcx
> MoveError
<'tcx
> {
48 pub fn with_move_info(move_from
: mc
::cmt
<'tcx
>,
49 move_to
: Option
<MovePlace
<'tcx
>>)
59 pub struct MovePlace
<'tcx
> {
60 pub span
: syntax_pos
::Span
,
62 pub pat_source
: PatternSource
<'tcx
>,
65 pub struct GroupedMoveErrors
<'tcx
> {
66 move_from
: mc
::cmt
<'tcx
>,
67 move_to_places
: Vec
<MovePlace
<'tcx
>>
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
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
));
88 for move_to
in &error
.move_to_places
{
90 err
= note_move_destination(err
, move_to
.span
, move_to
.name
, is_first_note
);
91 is_first_note
= false;
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");
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
)
110 return grouped_errors
;
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()]
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
);
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
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
));
150 format
!("cannot move out of {}", move_from
.descriptive_string(bccx
.tcx
))
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 `{}`, \
163 err
.span_label(move_from
.span
, "cannot move out of here");
166 (_
, Kind
::Pattern
) => {
167 span_bug
!(move_from
.span
, "this path should not cause illegal move");
172 Categorization
::Downcast(ref b
, _
) |
173 Categorization
::Interior(ref b
, mc
::InteriorField(_
)) => {
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",
180 err
.span_label(move_from
.span
, "cannot move out of here");
184 span_bug
!(move_from
.span
, "this path should not cause illegal move");
189 span_bug
!(move_from
.span
, "this path should not cause illegal move");
194 fn note_move_destination(mut err
: DiagnosticBuilder
,
195 move_to_span
: syntax_pos
::Span
,
197 is_first_note
: bool
) -> DiagnosticBuilder
{
201 format
!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
205 err
.span_label(move_to_span
,
206 format
!("...and here (use `ref {0}` or `ref mut {0}`)",