@@ -234,6 +234,38 @@ struct BufEntry {
234234 size : isize ,
235235}
236236
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+
237269impl Printer {
238270 pub fn new ( ) -> Self {
239271 Printer {
@@ -270,23 +302,27 @@ impl Printer {
270302 }
271303 }
272304
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 {
274307 if self . scan_stack . is_empty ( ) {
275308 self . left_total = 1 ;
276309 self . right_total = 1 ;
277310 self . buf . clear ( ) ;
278311 }
279312 let right = self . buf . push ( BufEntry { token : Token :: Begin ( token) , size : -self . right_total } ) ;
280313 self . scan_stack . push_back ( right) ;
314+ BoxMarker
281315 }
282316
283- fn scan_end ( & mut self ) {
317+ // This is is where `BoxMarker`s are consumed.
318+ fn scan_end ( & mut self , b : BoxMarker ) {
284319 if self . scan_stack . is_empty ( ) {
285320 self . print_end ( ) ;
286321 } else {
287322 let right = self . buf . push ( BufEntry { token : Token :: End , size : -1 } ) ;
288323 self . scan_stack . push_back ( right) ;
289324 }
325+ std:: mem:: forget ( b)
290326 }
291327
292328 fn scan_break ( & mut self , token : BreakToken ) {
0 commit comments