11//! Borrow checker diagnostics.
22
3+ use std:: collections:: BTreeMap ;
4+
35use rustc_abi:: { FieldIdx , VariantIdx } ;
6+ use rustc_data_structures:: fx:: FxIndexMap ;
47use rustc_errors:: { Applicability , Diag , MultiSpan } ;
58use rustc_hir:: def:: { CtorKind , Namespace } ;
69use rustc_hir:: { self as hir, CoroutineKind , LangItem } ;
@@ -17,10 +20,10 @@ use rustc_middle::mir::{
1720use rustc_middle:: ty:: print:: Print ;
1821use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt } ;
1922use 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 } ;
2124use rustc_span:: def_id:: LocalDefId ;
2225use rustc_span:: source_map:: Spanned ;
23- use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
26+ use rustc_span:: { DUMMY_SP , ErrorGuaranteed , Span , Symbol , sym} ;
2427use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
2528use rustc_trait_selection:: infer:: InferCtxtExt ;
2629use rustc_trait_selection:: traits:: {
@@ -68,6 +71,126 @@ pub(super) struct DescribePlaceOpt {
6871
6972pub ( super ) struct IncludingTupleField ( pub ( super ) bool ) ;
7073
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+
71194impl < ' infcx , ' tcx > MirBorrowckCtxt < ' _ , ' infcx , ' tcx > {
72195 /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
73196 /// is moved after being invoked.
0 commit comments