Skip to content

Commit 353db81

Browse files
committed
Introduce BoxMarker to pretty-printing.
The pretty-printers open and close "boxes" of text a lot. The open and close operations must be matched. The matching is currently all implicit and very easy to get wrong. (rust-lang#140280 and rust-lang#140246 are two recent pretty-printing fixes that both involved unclosed boxes.) This commit introduces `BoxMarker`, a marker type that represents an open box. It makes box opening/closing explicit, which makes it much easier to understand and harder to get wrong. The commit also removes many comments are on `end` calls saying things like "end outer head-block", "Close the outer-box". These demonstrate how confusing the implicit approach was, but aren't necessary any more.
1 parent cb31a00 commit 353db81

File tree

9 files changed

+465
-377
lines changed

9 files changed

+465
-377
lines changed

compiler/rustc_ast_pretty/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![allow(internal_features)]
33
#![doc(rust_logo)]
44
#![feature(box_patterns)]
5+
#![feature(negative_impls)]
56
#![feature(rustdoc_internals)]
67
// tidy-alphabetical-end
78

compiler/rustc_ast_pretty/src/pp.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,43 @@ 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 consumed by `Printer::end`. Any marker that isn't
244+
// closed, or closed more than once, will trigger a panic if debug assertions
245+
// are enabled.
246+
//
247+
// FIXME(nnethercote): the "isn't closed" assertion is currently disabled
248+
// because a few places fail to close their boxes. It can be enabled once they
249+
// are fixed.
250+
//
251+
// Note: it would be better to make open/close mismatching impossible and avoid
252+
// the need for this marker type altogether by having functions like
253+
// `with_ibox` that open a box, call a closure, and then close the box. That
254+
// would work for simple cases, but box lifetimes sometimes interact with
255+
// complex control flow and across function boundaries in ways that are
256+
// difficult to handle with such a technique.
257+
#[must_use]
258+
pub struct BoxMarker {
259+
pub ended: bool,
260+
}
261+
262+
impl !Clone for BoxMarker {}
263+
impl !Copy for BoxMarker {}
264+
265+
impl Drop for BoxMarker {
266+
fn drop(&mut self) {
267+
if cfg!(debug_assertions) && !self.ended {
268+
// FIXME(nnethercote): enable once the bad cases are fixed
269+
//panic!("BoxMarker not ended");
270+
}
271+
}
272+
}
273+
237274
impl Printer {
238275
pub fn new() -> Self {
239276
Printer {
@@ -270,23 +307,30 @@ impl Printer {
270307
}
271308
}
272309

273-
fn scan_begin(&mut self, token: BeginToken) {
310+
// This is is where `BoxMarker`s are produced.
311+
fn scan_begin(&mut self, token: BeginToken) -> BoxMarker {
274312
if self.scan_stack.is_empty() {
275313
self.left_total = 1;
276314
self.right_total = 1;
277315
self.buf.clear();
278316
}
279317
let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
280318
self.scan_stack.push_back(right);
319+
BoxMarker { ended: false }
281320
}
282321

283-
fn scan_end(&mut self) {
322+
// This is is where `BoxMarker`s are consumed.
323+
fn scan_end(&mut self, mut b: BoxMarker) {
284324
if self.scan_stack.is_empty() {
285325
self.print_end();
286326
} else {
287327
let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
288328
self.scan_stack.push_back(right);
289329
}
330+
if cfg!(debug_assertions) && b.ended {
331+
panic!("BoxMarker ended more than once");
332+
}
333+
b.ended = true;
290334
}
291335

292336
fn scan_break(&mut self, token: BreakToken) {

compiler/rustc_ast_pretty/src/pp/convenience.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
use std::borrow::Cow;
22

3-
use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token};
3+
use crate::pp::{
4+
BeginToken, BoxMarker, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token,
5+
};
46

57
impl Printer {
68
/// "raw box"
7-
pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
9+
pub fn rbox(&mut self, indent: isize, breaks: Breaks) -> BoxMarker {
810
self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks })
911
}
1012

1113
/// Inconsistent breaking box
12-
pub fn ibox(&mut self, indent: isize) {
14+
pub fn ibox(&mut self, indent: isize) -> BoxMarker {
1315
self.rbox(indent, Breaks::Inconsistent)
1416
}
1517

1618
/// Consistent breaking box
17-
pub fn cbox(&mut self, indent: isize) {
19+
pub fn cbox(&mut self, indent: isize) -> BoxMarker {
1820
self.rbox(indent, Breaks::Consistent)
1921
}
2022

21-
pub fn visual_align(&mut self) {
22-
self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
23+
pub fn visual_align(&mut self) -> BoxMarker {
24+
self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent })
2325
}
2426

2527
pub fn break_offset(&mut self, n: usize, off: isize) {
@@ -30,8 +32,8 @@ impl Printer {
3032
});
3133
}
3234

33-
pub fn end(&mut self) {
34-
self.scan_end()
35+
pub fn end(&mut self, b: BoxMarker) {
36+
self.scan_end(b)
3537
}
3638

3739
pub fn eof(mut self) -> String {

0 commit comments

Comments
 (0)