@@ -234,6 +234,38 @@ struct BufEntry {
234
234
size : isize ,
235
235
}
236
236
237
+ // Boxes opened with methods like `Printer::{cbox,ibox}` must be closed with
238
+ // `Printer::end`. Failure to do so can result in bad indenting, or in extreme
239
+ // cases, cause no output to be produced at all.
240
+ //
241
+ // Box opening and closing used to be entirely implicit, which was hard to
242
+ // understand and easy to get wrong. This marker type is now returned from the
243
+ // box opening methods and forgotten by `Printer::end`. Any marker that isn't
244
+ // forgotten will trigger a panic in `drop`. (Closing a box more than once
245
+ // isn't possible because `BoxMarker` doesn't implement `Copy` or `Clone`.)
246
+ //
247
+ // FIXME(nnethercote): the panic in `drop` is currently disabled because a few
248
+ // places fail to close their boxes. It can be enabled once they are fixed.
249
+ //
250
+ // Note: it would be better to make open/close mismatching impossible and avoid
251
+ // the need for this marker type altogether by having functions like
252
+ // `with_ibox` that open a box, call a closure, and then close the box. That
253
+ // would work for simple cases, but box lifetimes sometimes interact with
254
+ // complex control flow and across function boundaries in ways that are
255
+ // difficult to handle with such a technique.
256
+ #[ must_use]
257
+ pub struct BoxMarker ;
258
+
259
+ impl !Clone for BoxMarker { }
260
+ impl !Copy for BoxMarker { }
261
+
262
+ impl Drop for BoxMarker {
263
+ fn drop ( & mut self ) {
264
+ // FIXME(nnethercote): enable once the bad cases are fixed
265
+ //panic!("BoxMarker not ended with `Printer::end()`");
266
+ }
267
+ }
268
+
237
269
impl Printer {
238
270
pub fn new ( ) -> Self {
239
271
Printer {
@@ -270,23 +302,27 @@ impl Printer {
270
302
}
271
303
}
272
304
273
- fn scan_begin ( & mut self , token : BeginToken ) {
305
+ // This is is where `BoxMarker`s are produced.
306
+ fn scan_begin ( & mut self , token : BeginToken ) -> BoxMarker {
274
307
if self . scan_stack . is_empty ( ) {
275
308
self . left_total = 1 ;
276
309
self . right_total = 1 ;
277
310
self . buf . clear ( ) ;
278
311
}
279
312
let right = self . buf . push ( BufEntry { token : Token :: Begin ( token) , size : -self . right_total } ) ;
280
313
self . scan_stack . push_back ( right) ;
314
+ BoxMarker
281
315
}
282
316
283
- fn scan_end ( & mut self ) {
317
+ // This is is where `BoxMarker`s are consumed.
318
+ fn scan_end ( & mut self , b : BoxMarker ) {
284
319
if self . scan_stack . is_empty ( ) {
285
320
self . print_end ( ) ;
286
321
} else {
287
322
let right = self . buf . push ( BufEntry { token : Token :: End , size : -1 } ) ;
288
323
self . scan_stack . push_back ( right) ;
289
324
}
325
+ std:: mem:: forget ( b)
290
326
}
291
327
292
328
fn scan_break ( & mut self , token : BreakToken ) {
0 commit comments