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