@@ -35,19 +35,28 @@ where
3535}
3636
3737/// Used for emitting structured error messages and other diagnostic information.
38+ /// Each constructed `DiagnosticBuilder` must be consumed by a function such as
39+ /// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic. A panic occurrs if a
40+ /// `DiagnosticBuilder` is dropped without being consumed by one of these
41+ /// functions.
3842///
3943/// If there is some state in a downstream crate you would like to
4044/// access in the methods of `DiagnosticBuilder` here, consider
4145/// extending `DiagCtxtFlags`.
4246#[ must_use]
4347pub struct DiagnosticBuilder < ' a , G : EmissionGuarantee = ErrorGuaranteed > {
44- state : DiagnosticBuilderState < ' a > ,
48+ pub dcx : & ' a DiagCtxt ,
4549
46- /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
47- /// return value, especially within the frequently-used `PResult` type.
48- /// In theory, return value optimization (RVO) should avoid unnecessary
49- /// copying. In practice, it does not (at the time of writing).
50- diagnostic : Box < Diagnostic > ,
50+ /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is
51+ /// consumed via `emit`, `cancel`, etc. At that point it is consumed and
52+ /// replaced with `None`. Then `drop` checks that it is `None`; if not, it
53+ /// panics because a diagnostic was built but not used.
54+ ///
55+ /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is
56+ /// often used as a return value, especially within the frequently-used
57+ /// `PResult` type. In theory, return value optimization (RVO) should avoid
58+ /// unnecessary copying. In practice, it does not (at the time of writing).
59+ diag : Option < Box < Diagnostic > > ,
5160
5261 _marker : PhantomData < G > ,
5362}
@@ -56,32 +65,9 @@ pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
5665// twice, which would be bad.
5766impl < G > !Clone for DiagnosticBuilder < ' _ , G > { }
5867
59- #[ derive( Clone ) ]
60- enum DiagnosticBuilderState < ' a > {
61- /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
62- ///
63- /// The `Diagnostic` will be emitted through this `DiagCtxt`.
64- Emittable ( & ' a DiagCtxt ) ,
65-
66- /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
67- ///
68- /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be
69- /// assumed that `.emit()` was previously called, to end up in this state.
70- ///
71- /// While this is also used by `.cancel()`, this state is only observed by
72- /// the `Drop` `impl` of `DiagnosticBuilder`, because `.cancel()` takes
73- /// `self` by-value specifically to prevent any attempts to `.emit()`.
74- ///
75- // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
76- // despite that being potentially lossy, if important information is added
77- // *after* the original `.emit()` call.
78- AlreadyEmittedOrDuringCancellation ,
79- }
80-
81- // `DiagnosticBuilderState` should be pointer-sized.
8268rustc_data_structures:: static_assert_size!(
83- DiagnosticBuilderState <' _>,
84- std:: mem:: size_of:: <& DiagCtxt >( )
69+ DiagnosticBuilder <' _, ( ) >,
70+ 2 * std:: mem:: size_of:: <usize >( )
8571) ;
8672
8773/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
@@ -99,62 +85,44 @@ pub trait EmissionGuarantee: Sized {
9985}
10086
10187impl < ' a , G : EmissionGuarantee > DiagnosticBuilder < ' a , G > {
88+ /// Takes the diagnostic. For use by methods that consume the
89+ /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the
90+ /// only code that will be run on `self`.
91+ fn take_diag ( & mut self ) -> Diagnostic {
92+ Box :: into_inner ( self . diag . take ( ) . unwrap ( ) )
93+ }
94+
10295 /// Most `emit_producing_guarantee` functions use this as a starting point.
10396 fn emit_producing_nothing ( mut self ) {
104- match self . state {
105- // First `.emit()` call, the `&DiagCtxt` is still available.
106- DiagnosticBuilderState :: Emittable ( dcx) => {
107- self . state = DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation ;
108- dcx. emit_diagnostic_without_consuming ( & mut self . diagnostic ) ;
109- }
110- // `.emit()` was previously called, disallowed from repeating it.
111- DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation => { }
112- }
97+ let diag = self . take_diag ( ) ;
98+ self . dcx . emit_diagnostic ( diag) ;
99+ }
100+
101+ /// `ErrorGuaranteed::emit_producing_guarantee` uses this.
102+ // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
103+ fn emit_producing_error_guaranteed ( mut self ) -> ErrorGuaranteed {
104+ let diag = self . take_diag ( ) ;
105+
106+ // Only allow a guarantee if the `level` wasn't switched to a
107+ // non-error. The field isn't `pub`, but the whole `Diagnostic` can be
108+ // overwritten with a new one, thanks to `DerefMut`.
109+ assert ! (
110+ diag. is_error( ) ,
111+ "emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`" ,
112+ diag. level,
113+ ) ;
114+
115+ let guar = self . dcx . emit_diagnostic ( diag) ;
116+ guar. unwrap ( )
113117 }
114118}
115119
116- // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
117120impl EmissionGuarantee for ErrorGuaranteed {
118- fn emit_producing_guarantee ( mut db : DiagnosticBuilder < ' _ , Self > ) -> Self :: EmitResult {
119- // Contrast this with `emit_producing_nothing`.
120- match db. state {
121- // First `.emit()` call, the `&DiagCtxt` is still available.
122- DiagnosticBuilderState :: Emittable ( dcx) => {
123- db. state = DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation ;
124- let guar = dcx. emit_diagnostic_without_consuming ( & mut db. diagnostic ) ;
125-
126- // Only allow a guarantee if the `level` wasn't switched to a
127- // non-error - the field isn't `pub`, but the whole `Diagnostic`
128- // can be overwritten with a new one, thanks to `DerefMut`.
129- assert ! (
130- db. diagnostic. is_error( ) ,
131- "emitted non-error ({:?}) diagnostic \
132- from `DiagnosticBuilder<ErrorGuaranteed>`",
133- db. diagnostic. level,
134- ) ;
135- guar. unwrap ( )
136- }
137- // `.emit()` was previously called, disallowed from repeating it,
138- // but can take advantage of the previous `.emit()`'s guarantee
139- // still being applicable (i.e. as a form of idempotency).
140- DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation => {
141- // Only allow a guarantee if the `level` wasn't switched to a
142- // non-error - the field isn't `pub`, but the whole `Diagnostic`
143- // can be overwritten with a new one, thanks to `DerefMut`.
144- assert ! (
145- db. diagnostic. is_error( ) ,
146- "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \
147- became non-error ({:?}), after original `.emit()`",
148- db. diagnostic. level,
149- ) ;
150- #[ allow( deprecated) ]
151- ErrorGuaranteed :: unchecked_claim_error_was_emitted ( )
152- }
153- }
121+ fn emit_producing_guarantee ( db : DiagnosticBuilder < ' _ , Self > ) -> Self :: EmitResult {
122+ db. emit_producing_error_guaranteed ( )
154123 }
155124}
156125
157- // FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well?
158126impl EmissionGuarantee for ( ) {
159127 fn emit_producing_guarantee ( db : DiagnosticBuilder < ' _ , Self > ) -> Self :: EmitResult {
160128 db. emit_producing_nothing ( ) ;
@@ -206,7 +174,7 @@ macro_rules! forward0 {
206174 $( #[ $attrs] ) *
207175 #[ doc = concat!( "See [`Diagnostic::" , stringify!( $n) , "()`]." ) ]
208176 pub fn $n( & mut self $( , $name: $ty) * ) -> & mut Self {
209- self . diagnostic . $n( $( $name) ,* ) ;
177+ self . diag . as_mut ( ) . unwrap ( ) . $n( $( $name) ,* ) ;
210178 self
211179 }
212180 } ;
@@ -227,12 +195,12 @@ macro_rules! forward {
227195 ) => {
228196 #[ doc = concat!( "See [`Diagnostic::" , stringify!( $n) , "()`]." ) ]
229197 pub fn $n( & mut self , $( $name: $ty) ,* ) -> & mut Self {
230- self . diagnostic . $n( $( $name) ,* ) ;
198+ self . diag . as_mut ( ) . unwrap ( ) . $n( $( $name) ,* ) ;
231199 self
232200 }
233201 #[ doc = concat!( "See [`Diagnostic::" , stringify!( $n_mv) , "()`]." ) ]
234202 pub fn $n_mv( mut self , $( $name: $ty) ,* ) -> Self {
235- self . diagnostic . $n( $( $name) ,* ) ;
203+ self . diag . as_mut ( ) . unwrap ( ) . $n( $( $name) ,* ) ;
236204 self
237205 }
238206 } ;
@@ -242,13 +210,13 @@ impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
242210 type Target = Diagnostic ;
243211
244212 fn deref ( & self ) -> & Diagnostic {
245- & self . diagnostic
213+ self . diag . as_ref ( ) . unwrap ( )
246214 }
247215}
248216
249217impl < G : EmissionGuarantee > DerefMut for DiagnosticBuilder < ' _ , G > {
250218 fn deref_mut ( & mut self ) -> & mut Diagnostic {
251- & mut self . diagnostic
219+ self . diag . as_mut ( ) . unwrap ( )
252220 }
253221}
254222
@@ -262,13 +230,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
262230 /// Creates a new `DiagnosticBuilder` with an already constructed
263231 /// diagnostic.
264232 #[ track_caller]
265- pub ( crate ) fn new_diagnostic ( dcx : & ' a DiagCtxt , diagnostic : Diagnostic ) -> Self {
233+ pub ( crate ) fn new_diagnostic ( dcx : & ' a DiagCtxt , diag : Diagnostic ) -> Self {
266234 debug ! ( "Created new diagnostic" ) ;
267- Self {
268- state : DiagnosticBuilderState :: Emittable ( dcx) ,
269- diagnostic : Box :: new ( diagnostic) ,
270- _marker : PhantomData ,
271- }
235+ Self { dcx, diag : Some ( Box :: new ( diag) ) , _marker : PhantomData }
272236 }
273237
274238 /// Emit and consume the diagnostic.
@@ -289,14 +253,10 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
289253 self . emit ( )
290254 }
291255
292- /// Cancel the diagnostic (a structured diagnostic must either be emitted or
256+ /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or
293257 /// cancelled or it will panic when dropped).
294- ///
295- /// This method takes `self` by-value to disallow calling `.emit()` on it,
296- /// which may be expected to *guarantee* the emission of an error, either
297- /// at the time of the call, or through a prior `.emit()` call.
298258 pub fn cancel ( mut self ) {
299- self . state = DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation ;
259+ self . diag = None ;
300260 drop ( self ) ;
301261 }
302262
@@ -312,44 +272,21 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
312272 }
313273
314274 /// Converts the builder to a `Diagnostic` for later emission,
315- /// unless dcx has disabled such buffering, or `.emit()` was called .
275+ /// unless dcx has disabled such buffering.
316276 pub fn into_diagnostic ( mut self ) -> Option < ( Diagnostic , & ' a DiagCtxt ) > {
317- let dcx = match self . state {
318- // No `.emit()` calls, the `&DiagCtxt` is still available.
319- DiagnosticBuilderState :: Emittable ( dcx) => dcx,
320- // `.emit()` was previously called, nothing we can do.
321- DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation => {
322- return None ;
323- }
324- } ;
325-
326- if dcx. inner . lock ( ) . flags . dont_buffer_diagnostics
327- || dcx. inner . lock ( ) . flags . treat_err_as_bug . is_some ( )
328- {
277+ let flags = self . dcx . inner . lock ( ) . flags ;
278+ if flags. dont_buffer_diagnostics || flags. treat_err_as_bug . is_some ( ) {
329279 self . emit ( ) ;
330280 return None ;
331281 }
332282
333- // Take the `Diagnostic` by replacing it with a dummy.
334- let dummy = Diagnostic :: new ( Level :: Allow , DiagnosticMessage :: from ( "" ) ) ;
335- let diagnostic = std:: mem:: replace ( & mut * self . diagnostic , dummy) ;
336-
337- // Disable the ICE on `Drop`.
338- self . cancel ( ) ;
283+ let diag = self . take_diag ( ) ;
339284
340285 // Logging here is useful to help track down where in logs an error was
341286 // actually emitted.
342- debug ! ( "buffer: diagnostic ={:?}" , diagnostic ) ;
287+ debug ! ( "buffer: diag ={:?}" , diag ) ;
343288
344- Some ( ( diagnostic, dcx) )
345- }
346-
347- /// Retrieves the [`DiagCtxt`] if available
348- pub fn dcx ( & self ) -> Option < & DiagCtxt > {
349- match self . state {
350- DiagnosticBuilderState :: Emittable ( dcx) => Some ( dcx) ,
351- DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation => None ,
352- }
289+ Some ( ( diag, self . dcx ) )
353290 }
354291
355292 /// Buffers the diagnostic for later emission,
@@ -488,30 +425,24 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
488425
489426impl < G : EmissionGuarantee > Debug for DiagnosticBuilder < ' _ , G > {
490427 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
491- self . diagnostic . fmt ( f)
428+ self . diag . fmt ( f)
492429 }
493430}
494431
495- /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
496- /// or we emit a bug.
432+ /// Destructor bomb: a `DiagnosticBuilder` must be consumed ( emitted,
433+ /// cancelled, etc.) or we emit a bug.
497434impl < G : EmissionGuarantee > Drop for DiagnosticBuilder < ' _ , G > {
498435 fn drop ( & mut self ) {
499- match self . state {
500- // No `.emit()` or `.cancel()` calls.
501- DiagnosticBuilderState :: Emittable ( dcx) => {
502- if !panicking ( ) {
503- dcx. emit_diagnostic ( Diagnostic :: new (
504- Level :: Bug ,
505- DiagnosticMessage :: from (
506- "the following error was constructed but not emitted" ,
507- ) ,
508- ) ) ;
509- dcx. emit_diagnostic_without_consuming ( & mut self . diagnostic ) ;
510- panic ! ( "error was constructed but not emitted" ) ;
511- }
436+ match self . diag . take ( ) {
437+ Some ( diag) if !panicking ( ) => {
438+ self . dcx . emit_diagnostic ( Diagnostic :: new (
439+ Level :: Bug ,
440+ DiagnosticMessage :: from ( "the following error was constructed but not emitted" ) ,
441+ ) ) ;
442+ self . dcx . emit_diagnostic ( * diag) ;
443+ panic ! ( "error was constructed but not emitted" ) ;
512444 }
513- // `.emit()` was previously called, or maybe we're during `.cancel()`.
514- DiagnosticBuilderState :: AlreadyEmittedOrDuringCancellation => { }
445+ _ => { }
515446 }
516447 }
517448}
0 commit comments