1
1
//! Borrow checker diagnostics.
2
2
3
+ use std:: collections:: BTreeMap ;
4
+
3
5
use rustc_abi:: { FieldIdx , VariantIdx } ;
6
+ use rustc_data_structures:: fx:: FxIndexMap ;
4
7
use rustc_errors:: { Applicability , Diag , MultiSpan } ;
5
8
use rustc_hir:: def:: { CtorKind , Namespace } ;
6
9
use rustc_hir:: { self as hir, CoroutineKind , LangItem } ;
@@ -17,10 +20,10 @@ use rustc_middle::mir::{
17
20
use rustc_middle:: ty:: print:: Print ;
18
21
use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt } ;
19
22
use rustc_middle:: util:: { CallDesugaringKind , call_kind} ;
20
- use rustc_mir_dataflow:: move_paths:: { InitLocation , LookupResult } ;
23
+ use rustc_mir_dataflow:: move_paths:: { InitLocation , LookupResult , MoveOutIndex } ;
21
24
use rustc_span:: def_id:: LocalDefId ;
22
25
use rustc_span:: source_map:: Spanned ;
23
- use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
26
+ use rustc_span:: { DUMMY_SP , ErrorGuaranteed , Span , Symbol , sym} ;
24
27
use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
25
28
use rustc_trait_selection:: infer:: InferCtxtExt ;
26
29
use rustc_trait_selection:: traits:: {
@@ -68,6 +71,126 @@ pub(super) struct DescribePlaceOpt {
68
71
69
72
pub ( super ) struct IncludingTupleField ( pub ( super ) bool ) ;
70
73
74
+ enum BufferedDiag < ' infcx > {
75
+ Error ( Diag < ' infcx > ) ,
76
+ NonError ( Diag < ' infcx , ( ) > ) ,
77
+ }
78
+
79
+ impl < ' infcx > BufferedDiag < ' infcx > {
80
+ fn sort_span ( & self ) -> Span {
81
+ match self {
82
+ BufferedDiag :: Error ( diag) => diag. sort_span ,
83
+ BufferedDiag :: NonError ( diag) => diag. sort_span ,
84
+ }
85
+ }
86
+ }
87
+
88
+ #[ derive( Default ) ]
89
+ pub ( crate ) struct BorrowckDiagnosticsBuffer < ' infcx , ' tcx > {
90
+ /// This field keeps track of move errors that are to be reported for given move indices.
91
+ ///
92
+ /// There are situations where many errors can be reported for a single move out (see
93
+ /// #53807) and we want only the best of those errors.
94
+ ///
95
+ /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
96
+ /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of
97
+ /// the `Place` of the previous most diagnostic. This happens instead of buffering the
98
+ /// error. Once all move errors have been reported, any diagnostics in this map are added
99
+ /// to the buffer to be emitted.
100
+ ///
101
+ /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
102
+ /// when errors in the map are being re-added to the error buffer so that errors with the
103
+ /// same primary span come out in a consistent order.
104
+ buffered_move_errors : BTreeMap < Vec < MoveOutIndex > , ( PlaceRef < ' tcx > , Diag < ' infcx > ) > ,
105
+
106
+ buffered_mut_errors : FxIndexMap < Span , ( Diag < ' infcx > , usize ) > ,
107
+
108
+ /// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
109
+ buffered_diags : Vec < BufferedDiag < ' infcx > > ,
110
+ }
111
+
112
+ impl < ' infcx , ' tcx > BorrowckDiagnosticsBuffer < ' infcx , ' tcx > {
113
+ pub ( crate ) fn buffer_non_error ( & mut self , diag : Diag < ' infcx , ( ) > ) {
114
+ self . buffered_diags . push ( BufferedDiag :: NonError ( diag) ) ;
115
+ }
116
+ }
117
+
118
+ impl < ' infcx , ' tcx > MirBorrowckCtxt < ' _ , ' infcx , ' tcx > {
119
+ pub ( crate ) fn buffer_error ( & mut self , diag : Diag < ' infcx > ) {
120
+ self . diags_buffer . buffered_diags . push ( BufferedDiag :: Error ( diag) ) ;
121
+ }
122
+
123
+ pub ( crate ) fn buffer_non_error ( & mut self , diag : Diag < ' infcx , ( ) > ) {
124
+ self . diags_buffer . buffer_non_error ( diag) ;
125
+ }
126
+
127
+ pub ( crate ) fn buffer_move_error (
128
+ & mut self ,
129
+ move_out_indices : Vec < MoveOutIndex > ,
130
+ place_and_err : ( PlaceRef < ' tcx > , Diag < ' infcx > ) ,
131
+ ) -> bool {
132
+ if let Some ( ( _, diag) ) =
133
+ self . diags_buffer . buffered_move_errors . insert ( move_out_indices, place_and_err)
134
+ {
135
+ // Cancel the old diagnostic so we don't ICE
136
+ diag. cancel ( ) ;
137
+ false
138
+ } else {
139
+ true
140
+ }
141
+ }
142
+
143
+ pub ( crate ) fn get_buffered_mut_error ( & mut self , span : Span ) -> Option < ( Diag < ' infcx > , usize ) > {
144
+ // FIXME(#120456) - is `swap_remove` correct?
145
+ self . diags_buffer . buffered_mut_errors . swap_remove ( & span)
146
+ }
147
+
148
+ pub ( crate ) fn buffer_mut_error ( & mut self , span : Span , diag : Diag < ' infcx > , count : usize ) {
149
+ self . diags_buffer . buffered_mut_errors . insert ( span, ( diag, count) ) ;
150
+ }
151
+
152
+ pub ( crate ) fn emit_errors ( & mut self ) -> Option < ErrorGuaranteed > {
153
+ let mut res = self . infcx . tainted_by_errors ( ) ;
154
+
155
+ // Buffer any move errors that we collected and de-duplicated.
156
+ for ( _, ( _, diag) ) in std:: mem:: take ( & mut self . diags_buffer . buffered_move_errors ) {
157
+ // We have already set tainted for this error, so just buffer it.
158
+ self . buffer_error ( diag) ;
159
+ }
160
+ for ( _, ( mut diag, count) ) in std:: mem:: take ( & mut self . diags_buffer . buffered_mut_errors ) {
161
+ if count > 10 {
162
+ #[ allow( rustc:: diagnostic_outside_of_impl) ]
163
+ #[ allow( rustc:: untranslatable_diagnostic) ]
164
+ diag. note ( format ! ( "...and {} other attempted mutable borrows" , count - 10 ) ) ;
165
+ }
166
+ self . buffer_error ( diag) ;
167
+ }
168
+
169
+ if !self . diags_buffer . buffered_diags . is_empty ( ) {
170
+ self . diags_buffer . buffered_diags . sort_by_key ( |buffered_diag| buffered_diag. sort_span ( ) ) ;
171
+ for buffered_diag in self . diags_buffer . buffered_diags . drain ( ..) {
172
+ match buffered_diag {
173
+ BufferedDiag :: Error ( diag) => res = Some ( diag. emit ( ) ) ,
174
+ BufferedDiag :: NonError ( diag) => diag. emit ( ) ,
175
+ }
176
+ }
177
+ }
178
+
179
+ res
180
+ }
181
+
182
+ pub ( crate ) fn has_buffered_diags ( & self ) -> bool {
183
+ self . diags_buffer . buffered_diags . is_empty ( )
184
+ }
185
+
186
+ pub ( crate ) fn has_move_error (
187
+ & self ,
188
+ move_out_indices : & [ MoveOutIndex ] ,
189
+ ) -> Option < & ( PlaceRef < ' tcx > , Diag < ' infcx > ) > {
190
+ self . diags_buffer . buffered_move_errors . get ( move_out_indices)
191
+ }
192
+ }
193
+
71
194
impl < ' infcx , ' tcx > MirBorrowckCtxt < ' _ , ' infcx , ' tcx > {
72
195
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
73
196
/// is moved after being invoked.
0 commit comments