about summary refs log tree commit diff
path: root/compiler/rustc_ast_pretty/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_ast_pretty/src')
-rw-r--r--compiler/rustc_ast_pretty/src/lib.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pp.rs40
-rw-r--r--compiler/rustc_ast_pretty/src/pp/convenience.rs18
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs137
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs128
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs168
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/tests.rs6
7 files changed, 265 insertions, 233 deletions
diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs
index 84d9ce278a2..1079ccccb03 100644
--- a/compiler/rustc_ast_pretty/src/lib.rs
+++ b/compiler/rustc_ast_pretty/src/lib.rs
@@ -2,6 +2,7 @@
 #![allow(internal_features)]
 #![doc(rust_logo)]
 #![feature(box_patterns)]
+#![feature(negative_impls)]
 #![feature(rustdoc_internals)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs
index e4fd7e94fde..142c80b8e39 100644
--- a/compiler/rustc_ast_pretty/src/pp.rs
+++ b/compiler/rustc_ast_pretty/src/pp.rs
@@ -234,6 +234,38 @@ struct BufEntry {
     size: isize,
 }
 
+// Boxes opened with methods like `Printer::{cbox,ibox}` must be closed with
+// `Printer::end`. Failure to do so can result in bad indenting, or in extreme
+// cases, cause no output to be produced at all.
+//
+// Box opening and closing used to be entirely implicit, which was hard to
+// understand and easy to get wrong. This marker type is now returned from the
+// box opening methods and forgotten by `Printer::end`. Any marker that isn't
+// forgotten will trigger a panic in `drop`. (Closing a box more than once
+// isn't possible because `BoxMarker` doesn't implement `Copy` or `Clone`.)
+//
+// FIXME(nnethercote): the panic in `drop` is currently disabled because a few
+// places fail to close their boxes. It can be enabled once they are fixed.
+//
+// Note: it would be better to make open/close mismatching impossible and avoid
+// the need for this marker type altogether by having functions like
+// `with_ibox` that open a box, call a closure, and then close the box. That
+// would work for simple cases, but box lifetimes sometimes interact with
+// complex control flow and across function boundaries in ways that are
+// difficult to handle with such a technique.
+#[must_use]
+pub struct BoxMarker;
+
+impl !Clone for BoxMarker {}
+impl !Copy for BoxMarker {}
+
+impl Drop for BoxMarker {
+    fn drop(&mut self) {
+        // FIXME(nnethercote): enable once the bad cases are fixed
+        //panic!("BoxMarker not ended with `Printer::end()`");
+    }
+}
+
 impl Printer {
     pub fn new() -> Self {
         Printer {
@@ -270,7 +302,8 @@ impl Printer {
         }
     }
 
-    fn scan_begin(&mut self, token: BeginToken) {
+    // This is is where `BoxMarker`s are produced.
+    fn scan_begin(&mut self, token: BeginToken) -> BoxMarker {
         if self.scan_stack.is_empty() {
             self.left_total = 1;
             self.right_total = 1;
@@ -278,15 +311,18 @@ impl Printer {
         }
         let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
         self.scan_stack.push_back(right);
+        BoxMarker
     }
 
-    fn scan_end(&mut self) {
+    // This is is where `BoxMarker`s are consumed.
+    fn scan_end(&mut self, b: BoxMarker) {
         if self.scan_stack.is_empty() {
             self.print_end();
         } else {
             let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
             self.scan_stack.push_back(right);
         }
+        std::mem::forget(b)
     }
 
     fn scan_break(&mut self, token: BreakToken) {
diff --git a/compiler/rustc_ast_pretty/src/pp/convenience.rs b/compiler/rustc_ast_pretty/src/pp/convenience.rs
index a1c07bb07e4..9b902b38122 100644
--- a/compiler/rustc_ast_pretty/src/pp/convenience.rs
+++ b/compiler/rustc_ast_pretty/src/pp/convenience.rs
@@ -1,25 +1,27 @@
 use std::borrow::Cow;
 
-use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token};
+use crate::pp::{
+    BeginToken, BoxMarker, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token,
+};
 
 impl Printer {
     /// "raw box"
-    pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
+    pub fn rbox(&mut self, indent: isize, breaks: Breaks) -> BoxMarker {
         self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks })
     }
 
     /// Inconsistent breaking box
-    pub fn ibox(&mut self, indent: isize) {
+    pub fn ibox(&mut self, indent: isize) -> BoxMarker {
         self.rbox(indent, Breaks::Inconsistent)
     }
 
     /// Consistent breaking box
-    pub fn cbox(&mut self, indent: isize) {
+    pub fn cbox(&mut self, indent: isize) -> BoxMarker {
         self.rbox(indent, Breaks::Consistent)
     }
 
-    pub fn visual_align(&mut self) {
-        self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
+    pub fn visual_align(&mut self) -> BoxMarker {
+        self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent })
     }
 
     pub fn break_offset(&mut self, n: usize, off: isize) {
@@ -30,8 +32,8 @@ impl Printer {
         });
     }
 
-    pub fn end(&mut self) {
-        self.scan_end()
+    pub fn end(&mut self, b: BoxMarker) {
+        self.scan_end(b)
     }
 
     pub fn eof(mut self) -> String {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 62a50c73855..985359e1234 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -26,7 +26,7 @@ use rustc_span::symbol::IdentPrinter;
 use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
 
 use crate::pp::Breaks::{Consistent, Inconsistent};
-use crate::pp::{self, Breaks};
+use crate::pp::{self, BoxMarker, Breaks};
 use crate::pprust::state::fixup::FixupContext;
 
 pub enum MacHeader<'a> {
@@ -419,7 +419,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     {
         let mut it = elts.into_iter();
 
-        self.rbox(0, b);
+        let rb = self.rbox(0, b);
         if let Some(first) = it.next() {
             op(self, first);
             for elt in it {
@@ -430,7 +430,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 op(self, elt);
             }
         }
-        self.end();
+        self.end(rb);
     }
 
     fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F)
@@ -461,7 +461,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.zerobreak();
                 }
                 if let Some((last, lines)) = cmnt.lines.split_last() {
-                    self.ibox(0);
+                    let ib = self.ibox(0);
 
                     for line in lines {
                         self.word(line.clone());
@@ -471,7 +471,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.word(last.clone());
                     self.space();
 
-                    self.end();
+                    self.end(ib);
                 }
                 self.zerobreak()
             }
@@ -494,14 +494,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.word(line.clone());
                     self.hardbreak()
                 } else {
-                    self.visual_align();
+                    let vb = self.visual_align();
                     for line in &cmnt.lines {
                         if !line.is_empty() {
                             self.word(line.clone());
                         }
                         self.hardbreak();
                     }
-                    self.end();
+                    self.end(vb);
                 }
             }
             CommentStyle::BlankLine => {
@@ -620,7 +620,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     }
 
     fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
-        self.ibox(0);
+        let ib = self.ibox(0);
         match item.unsafety {
             ast::Safety::Unsafe(_) => {
                 self.word("unsafe");
@@ -653,7 +653,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             ast::Safety::Unsafe(_) => self.pclose(),
             ast::Safety::Default | ast::Safety::Safe(_) => {}
         }
-        self.end();
+        self.end(ib);
     }
 
     /// This doesn't deserve to be called "pretty" printing, but it should be
@@ -739,9 +739,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         convert_dollar_crate: bool,
         span: Span,
     ) {
-        if delim == Delimiter::Brace {
-            self.cbox(INDENT_UNIT);
-        }
+        let cb = (delim == Delimiter::Brace).then(|| self.cbox(INDENT_UNIT));
         match header {
             Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
             Some(MacHeader::Keyword(kw)) => self.word(kw),
@@ -763,18 +761,18 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 if !tts.is_empty() {
                     self.space();
                 }
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.print_tts(tts, convert_dollar_crate);
-                self.end();
+                self.end(ib);
                 let empty = tts.is_empty();
-                self.bclose(span, empty);
+                self.bclose(span, empty, cb.unwrap());
             }
             delim => {
                 let token_str = self.token_kind_to_string(&delim.as_open_token_kind());
                 self.word(token_str);
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.print_tts(tts, convert_dollar_crate);
-                self.end();
+                self.end(ib);
                 let token_str = self.token_kind_to_string(&delim.as_close_token_kind());
                 self.word(token_str);
             }
@@ -828,37 +826,38 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+    fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) -> (BoxMarker, BoxMarker) {
         let w = w.into();
         // Outer-box is consistent.
-        self.cbox(INDENT_UNIT);
+        let cb = self.cbox(INDENT_UNIT);
         // Head-box is inconsistent.
-        self.ibox(0);
+        let ib = self.ibox(0);
         // Keyword that starts the head.
         if !w.is_empty() {
             self.word_nbsp(w);
         }
+        (cb, ib)
     }
 
-    fn bopen(&mut self) {
+    fn bopen(&mut self, ib: BoxMarker) {
         self.word("{");
-        self.end(); // Close the head-box.
+        self.end(ib);
     }
 
-    fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
+    fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, cb: Option<BoxMarker>) {
         let has_comment = self.maybe_print_comment(span.hi());
         if !empty || has_comment {
             self.break_offset_if_not_bol(1, -INDENT_UNIT);
         }
         self.word("}");
-        if close_box {
-            self.end(); // Close the outer-box.
+        if let Some(cb) = cb {
+            self.end(cb);
         }
     }
 
-    fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
-        let close_box = true;
-        self.bclose_maybe_open(span, empty, close_box)
+    fn bclose(&mut self, span: rustc_span::Span, empty: bool, cb: BoxMarker) {
+        let cb = Some(cb);
+        self.bclose_maybe_open(span, empty, cb)
     }
 
     fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
@@ -1014,11 +1013,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
     fn block_to_string(&self, blk: &ast::Block) -> String {
         Self::to_string(|s| {
-            // Containing cbox, will be closed by `print_block` at `}`.
-            s.cbox(INDENT_UNIT);
-            // Head-ibox, will be closed by `print_block` after `{`.
-            s.ibox(0);
-            s.print_block(blk)
+            let (cb, ib) = s.head("");
+            s.print_block(blk, cb, ib)
         })
     }
 
@@ -1090,7 +1086,7 @@ impl<'a> State<'a> {
         F: FnMut(&mut State<'_>, &T),
         G: FnMut(&T) -> rustc_span::Span,
     {
-        self.rbox(0, b);
+        let rb = self.rbox(0, b);
         let len = elts.len();
         let mut i = 0;
         for elt in elts {
@@ -1103,7 +1099,7 @@ impl<'a> State<'a> {
                 self.space_if_not_bol();
             }
         }
-        self.end();
+        self.end(rb);
     }
 
     fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
@@ -1172,7 +1168,7 @@ impl<'a> State<'a> {
 
     pub fn print_type(&mut self, ty: &ast::Ty) {
         self.maybe_print_comment(ty.span.lo());
-        self.ibox(0);
+        let ib = self.ibox(0);
         match &ty.kind {
             ast::TyKind::Slice(ty) => {
                 self.word("[");
@@ -1214,12 +1210,12 @@ impl<'a> State<'a> {
                 self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params);
             }
             ast::TyKind::UnsafeBinder(f) => {
-                self.ibox(INDENT_UNIT);
+                let ib = self.ibox(INDENT_UNIT);
                 self.word("unsafe");
                 self.print_generic_params(&f.generic_params);
                 self.nbsp();
                 self.print_type(&f.inner_ty);
-                self.end();
+                self.end(ib);
             }
             ast::TyKind::Path(None, path) => {
                 self.print_path(path, false, 0);
@@ -1277,7 +1273,7 @@ impl<'a> State<'a> {
                 self.print_ty_pat(pat);
             }
         }
-        self.end();
+        self.end(ib);
     }
 
     fn print_trait_ref(&mut self, t: &ast::TraitRef) {
@@ -1324,15 +1320,15 @@ impl<'a> State<'a> {
             ast::StmtKind::Let(loc) => {
                 self.print_outer_attributes(&loc.attrs);
                 self.space_if_not_bol();
-                self.ibox(INDENT_UNIT);
+                let ib1 = self.ibox(INDENT_UNIT);
                 if loc.super_.is_some() {
                     self.word_nbsp("super");
                 }
                 self.word_nbsp("let");
 
-                self.ibox(INDENT_UNIT);
+                let ib2 = self.ibox(INDENT_UNIT);
                 self.print_local_decl(loc);
-                self.end();
+                self.end(ib2);
                 if let Some((init, els)) = loc.kind.init_else_opt() {
                     self.nbsp();
                     self.word_space("=");
@@ -1342,14 +1338,14 @@ impl<'a> State<'a> {
                         FixupContext::default(),
                     );
                     if let Some(els) = els {
-                        self.cbox(INDENT_UNIT);
-                        self.ibox(INDENT_UNIT);
+                        let cb = self.cbox(INDENT_UNIT);
+                        let ib = self.ibox(INDENT_UNIT);
                         self.word(" else ");
-                        self.print_block(els);
+                        self.print_block(els, cb, ib);
                     }
                 }
                 self.word(";");
-                self.end(); // `let` ibox
+                self.end(ib1);
             }
             ast::StmtKind::Item(item) => self.print_item(item),
             ast::StmtKind::Expr(expr) => {
@@ -1380,23 +1376,30 @@ impl<'a> State<'a> {
         self.maybe_print_trailing_comment(st.span, None)
     }
 
-    fn print_block(&mut self, blk: &ast::Block) {
-        self.print_block_with_attrs(blk, &[])
+    fn print_block(&mut self, blk: &ast::Block, cb: BoxMarker, ib: BoxMarker) {
+        self.print_block_with_attrs(blk, &[], cb, ib)
     }
 
-    fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
-        self.print_block_maybe_unclosed(blk, &[], false)
+    fn print_block_unclosed_indent(&mut self, blk: &ast::Block, ib: BoxMarker) {
+        self.print_block_maybe_unclosed(blk, &[], None, ib)
     }
 
-    fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
-        self.print_block_maybe_unclosed(blk, attrs, true)
+    fn print_block_with_attrs(
+        &mut self,
+        blk: &ast::Block,
+        attrs: &[ast::Attribute],
+        cb: BoxMarker,
+        ib: BoxMarker,
+    ) {
+        self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib)
     }
 
     fn print_block_maybe_unclosed(
         &mut self,
         blk: &ast::Block,
         attrs: &[ast::Attribute],
-        close_box: bool,
+        cb: Option<BoxMarker>,
+        ib: BoxMarker,
     ) {
         match blk.rules {
             BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
@@ -1404,7 +1407,7 @@ impl<'a> State<'a> {
         }
         self.maybe_print_comment(blk.span.lo());
         self.ann.pre(self, AnnNode::Block(blk));
-        self.bopen();
+        self.bopen(ib);
 
         let has_attrs = self.print_inner_attributes(attrs);
 
@@ -1421,7 +1424,7 @@ impl<'a> State<'a> {
         }
 
         let empty = !has_attrs && blk.stmts.is_empty();
-        self.bclose_maybe_open(blk.span, empty, close_box);
+        self.bclose_maybe_open(blk.span, empty, cb);
         self.ann.post(self, AnnNode::Block(blk))
     }
 
@@ -1556,8 +1559,8 @@ impl<'a> State<'a> {
                         }
                     }
                     InlineAsmOperand::Label { block } => {
-                        s.head("label");
-                        s.print_block(block);
+                        let (cb, ib) = s.head("label");
+                        s.print_block(block, cb, ib);
                     }
                 }
             }
@@ -1671,13 +1674,13 @@ impl<'a> State<'a> {
                     Consistent,
                     fields,
                     |s, f| {
-                        s.cbox(INDENT_UNIT);
+                        let cb = s.cbox(INDENT_UNIT);
                         if !f.is_shorthand {
                             s.print_ident(f.ident);
                             s.word_nbsp(":");
                         }
                         s.print_pat(&f.pat);
-                        s.end();
+                        s.end(cb);
                     },
                     |f| f.pat.span,
                 );
@@ -1928,7 +1931,7 @@ impl<'a> State<'a> {
     }
 
     fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
 
         self.print_outer_attributes_inline(&input.attrs);
 
@@ -1947,16 +1950,16 @@ impl<'a> State<'a> {
                 }
             }
         }
-        self.end();
+        self.end(ib);
     }
 
     fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
         if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
             self.space_if_not_bol();
-            self.ibox(INDENT_UNIT);
+            let ib = self.ibox(INDENT_UNIT);
             self.word_space("->");
             self.print_type(ty);
-            self.end();
+            self.end(ib);
             self.maybe_print_comment(ty.span.lo());
         }
     }
@@ -1969,12 +1972,12 @@ impl<'a> State<'a> {
         name: Option<Ident>,
         generic_params: &[ast::GenericParam],
     ) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
         self.print_formal_generic_params(generic_params);
         let generics = ast::Generics::default();
         let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() };
         self.print_fn(decl, header, name, &generics);
-        self.end();
+        self.end(ib);
     }
 
     fn print_fn_header_info(&mut self, header: ast::FnHeader) {
@@ -2052,7 +2055,7 @@ impl<'a> State<'a> {
     }
 
     fn print_meta_item(&mut self, item: &ast::MetaItem) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
         match &item.kind {
             ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
             ast::MetaItemKind::NameValue(value) => {
@@ -2068,7 +2071,7 @@ impl<'a> State<'a> {
                 self.pclose();
             }
         }
-        self.end();
+        self.end(ib);
     }
 
     pub(crate) fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 48da9fc63b8..38cadc77b77 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -13,7 +13,7 @@ use rustc_ast::{
 
 use crate::pp::Breaks::Inconsistent;
 use crate::pprust::state::fixup::FixupContext;
-use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State};
+use crate::pprust::state::{AnnNode, BoxMarker, INDENT_UNIT, PrintState, State};
 
 impl<'a> State<'a> {
     fn print_else(&mut self, els: Option<&ast::Expr>) {
@@ -21,20 +21,20 @@ impl<'a> State<'a> {
             match &_else.kind {
                 // Another `else if` block.
                 ast::ExprKind::If(i, then, e) => {
-                    self.cbox(0);
-                    self.ibox(0);
+                    let cb = self.cbox(0);
+                    let ib = self.ibox(0);
                     self.word(" else if ");
                     self.print_expr_as_cond(i);
                     self.space();
-                    self.print_block(then);
+                    self.print_block(then, cb, ib);
                     self.print_else(e.as_deref())
                 }
                 // Final `else` block.
                 ast::ExprKind::Block(b, None) => {
-                    self.cbox(0);
-                    self.ibox(0);
+                    let cb = self.cbox(0);
+                    let ib = self.ibox(0);
                     self.word(" else ");
-                    self.print_block(b)
+                    self.print_block(b, cb, ib)
                 }
                 // Constraints would be great here!
                 _ => {
@@ -45,12 +45,12 @@ impl<'a> State<'a> {
     }
 
     fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
-        self.cbox(0);
-        self.ibox(0);
+        let cb = self.cbox(0);
+        let ib = self.ibox(0);
         self.word_nbsp("if");
         self.print_expr_as_cond(test);
         self.space();
-        self.print_block(blk);
+        self.print_block(blk, cb, ib);
         self.print_else(elseopt)
     }
 
@@ -112,11 +112,11 @@ impl<'a> State<'a> {
     }
 
     fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
         self.word("[");
         self.commasep_exprs(Inconsistent, exprs);
         self.word("]");
-        self.end();
+        self.end(ib);
     }
 
     pub(super) fn print_expr_anon_const(
@@ -124,27 +124,27 @@ impl<'a> State<'a> {
         expr: &ast::AnonConst,
         attrs: &[ast::Attribute],
     ) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
         self.word("const");
         self.nbsp();
         if let ast::ExprKind::Block(block, None) = &expr.value.kind {
-            self.cbox(0);
-            self.ibox(0);
-            self.print_block_with_attrs(block, attrs);
+            let cb = self.cbox(0);
+            let ib = self.ibox(0);
+            self.print_block_with_attrs(block, attrs, cb, ib);
         } else {
             self.print_expr(&expr.value, FixupContext::default());
         }
-        self.end();
+        self.end(ib);
     }
 
     fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) {
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
         self.word("[");
         self.print_expr(element, FixupContext::default());
         self.word_space(";");
         self.print_expr(&count.value, FixupContext::default());
         self.word("]");
-        self.end();
+        self.end(ib);
     }
 
     fn print_expr_struct(
@@ -169,7 +169,7 @@ impl<'a> State<'a> {
             self.word("}");
             return;
         }
-        self.cbox(0);
+        let cb = self.cbox(0);
         for (pos, field) in fields.iter().with_position() {
             let is_first = matches!(pos, Position::First | Position::Only);
             let is_last = matches!(pos, Position::Last | Position::Only);
@@ -200,7 +200,7 @@ impl<'a> State<'a> {
             self.space();
         }
         self.offset(-INDENT_UNIT);
-        self.end();
+        self.end(cb);
         self.word("}");
     }
 
@@ -368,7 +368,7 @@ impl<'a> State<'a> {
             self.print_outer_attributes(attrs);
         }
 
-        self.ibox(INDENT_UNIT);
+        let ib = self.ibox(INDENT_UNIT);
 
         // The Match subexpression in `match x {} - 1` must be parenthesized if
         // it is the leftmost subexpression in a statement:
@@ -440,14 +440,14 @@ impl<'a> State<'a> {
             ast::ExprKind::Type(expr, ty) => {
                 self.word("builtin # type_ascribe");
                 self.popen();
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.print_expr(expr, FixupContext::default());
 
                 self.word(",");
                 self.space_if_not_bol();
                 self.print_type(ty);
 
-                self.end();
+                self.end(ib);
                 self.pclose();
             }
             ast::ExprKind::Let(pat, scrutinee, _, _) => {
@@ -459,20 +459,20 @@ impl<'a> State<'a> {
                     self.print_ident(label.ident);
                     self.word_space(":");
                 }
-                self.cbox(0);
-                self.ibox(0);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
                 self.word_nbsp("while");
                 self.print_expr_as_cond(test);
                 self.space();
-                self.print_block_with_attrs(blk, attrs);
+                self.print_block_with_attrs(blk, attrs, cb, ib);
             }
             ast::ExprKind::ForLoop { pat, iter, body, label, kind } => {
                 if let Some(label) = label {
                     self.print_ident(label.ident);
                     self.word_space(":");
                 }
-                self.cbox(0);
-                self.ibox(0);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
                 self.word_nbsp("for");
                 if kind == &ForLoopKind::ForAwait {
                     self.word_nbsp("await");
@@ -482,21 +482,21 @@ impl<'a> State<'a> {
                 self.word_space("in");
                 self.print_expr_as_cond(iter);
                 self.space();
-                self.print_block_with_attrs(body, attrs);
+                self.print_block_with_attrs(body, attrs, cb, ib);
             }
             ast::ExprKind::Loop(blk, opt_label, _) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
                 }
-                self.cbox(0);
-                self.ibox(0);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
                 self.word_nbsp("loop");
-                self.print_block_with_attrs(blk, attrs);
+                self.print_block_with_attrs(blk, attrs, cb, ib);
             }
             ast::ExprKind::Match(expr, arms, match_kind) => {
-                self.cbox(0);
-                self.ibox(0);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
 
                 match match_kind {
                     MatchKind::Prefix => {
@@ -514,13 +514,13 @@ impl<'a> State<'a> {
                     }
                 }
 
-                self.bopen();
+                self.bopen(ib);
                 self.print_inner_attributes_no_trailing_hardbreak(attrs);
                 for arm in arms {
                     self.print_arm(arm);
                 }
                 let empty = attrs.is_empty() && arms.is_empty();
-                self.bclose(expr.span, empty);
+                self.bclose(expr.span, empty, cb);
             }
             ast::ExprKind::Closure(box ast::Closure {
                 binder,
@@ -542,12 +542,15 @@ impl<'a> State<'a> {
                 self.print_fn_params_and_ret(fn_decl, true);
                 self.space();
                 self.print_expr(body, FixupContext::default());
-                self.end(); // need to close a box
+                // FIXME(nnethercote): Bogus. Reduce visibility of `ended` once it's fixed.
+                let fake_ib = BoxMarker;
+                self.end(fake_ib);
 
-                // a box will be closed by print_expr, but we didn't want an overall
+                // A box will be closed by print_expr, but we didn't want an overall
                 // wrapper so we closed the corresponding opening. so create an
                 // empty box to satisfy the close.
-                self.ibox(0);
+                // FIXME(nnethercote): Bogus.
+                let _ib = self.ibox(0);
             }
             ast::ExprKind::Block(blk, opt_label) => {
                 if let Some(label) = opt_label {
@@ -555,18 +558,18 @@ impl<'a> State<'a> {
                     self.word_space(":");
                 }
                 // containing cbox, will be closed by print-block at }
-                self.cbox(0);
+                let cb = self.cbox(0);
                 // head-box, will be closed by print-block after {
-                self.ibox(0);
-                self.print_block_with_attrs(blk, attrs);
+                let ib = self.ibox(0);
+                self.print_block_with_attrs(blk, attrs, cb, ib);
             }
             ast::ExprKind::Gen(capture_clause, blk, kind, _decl_span) => {
                 self.word_nbsp(kind.modifier());
                 self.print_capture_clause(*capture_clause);
                 // cbox/ibox in analogy to the `ExprKind::Block` arm above
-                self.cbox(0);
-                self.ibox(0);
-                self.print_block_with_attrs(blk, attrs);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
+                self.print_block_with_attrs(blk, attrs, cb, ib);
             }
             ast::ExprKind::Await(expr, _) => {
                 self.print_expr_cond_paren(
@@ -728,19 +731,19 @@ impl<'a> State<'a> {
                 // FIXME: Print `builtin # format_args` once macro `format_args` uses `builtin_syntax`.
                 self.word("format_args!");
                 self.popen();
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.word(reconstruct_format_args_template_string(&fmt.template));
                 for arg in fmt.arguments.all_args() {
                     self.word_space(",");
                     self.print_expr(&arg.expr, FixupContext::default());
                 }
-                self.end();
+                self.end(ib);
                 self.pclose();
             }
             ast::ExprKind::OffsetOf(container, fields) => {
                 self.word("builtin # offset_of");
                 self.popen();
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.print_type(container);
                 self.word(",");
                 self.space();
@@ -753,7 +756,7 @@ impl<'a> State<'a> {
                         self.print_ident(field);
                     }
                 }
-                self.end();
+                self.end(ib);
                 self.pclose();
             }
             ast::ExprKind::MacCall(m) => self.print_mac(m),
@@ -791,10 +794,10 @@ impl<'a> State<'a> {
                 self.word("?")
             }
             ast::ExprKind::TryBlock(blk) => {
-                self.cbox(0);
-                self.ibox(0);
+                let cb = self.cbox(0);
+                let ib = self.ibox(0);
                 self.word_nbsp("try");
-                self.print_block_with_attrs(blk, attrs)
+                self.print_block_with_attrs(blk, attrs, cb, ib)
             }
             ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
                 self.word("builtin # ");
@@ -803,7 +806,7 @@ impl<'a> State<'a> {
                     ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"),
                 }
                 self.popen();
-                self.ibox(0);
+                let ib = self.ibox(0);
                 self.print_expr(expr, FixupContext::default());
 
                 if let Some(ty) = ty {
@@ -812,7 +815,7 @@ impl<'a> State<'a> {
                     self.print_type(ty);
                 }
 
-                self.end();
+                self.end(ib);
                 self.pclose();
             }
             ast::ExprKind::Err(_) => {
@@ -833,7 +836,7 @@ impl<'a> State<'a> {
             self.pclose();
         }
 
-        self.end();
+        self.end(ib);
     }
 
     fn print_arm(&mut self, arm: &ast::Arm) {
@@ -841,8 +844,8 @@ impl<'a> State<'a> {
         if arm.attrs.is_empty() {
             self.space();
         }
-        self.cbox(INDENT_UNIT);
-        self.ibox(0);
+        let cb = self.cbox(INDENT_UNIT);
+        let ib = self.ibox(0);
         self.maybe_print_comment(arm.pat.span.lo());
         self.print_outer_attributes(&arm.attrs);
         self.print_pat(&arm.pat);
@@ -863,8 +866,7 @@ impl<'a> State<'a> {
                         self.word_space(":");
                     }
 
-                    // The block will close the pattern's ibox.
-                    self.print_block_unclosed_indent(blk);
+                    self.print_block_unclosed_indent(blk, ib);
 
                     // If it is a user-provided unsafe block, print a comma after it.
                     if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
@@ -872,16 +874,16 @@ impl<'a> State<'a> {
                     }
                 }
                 _ => {
-                    self.end(); // Close the ibox for the pattern.
+                    self.end(ib);
                     self.print_expr(body, FixupContext::new_match_arm());
                     self.word(",");
                 }
             }
         } else {
-            self.end(); // Close the ibox for the pattern.
+            self.end(ib);
             self.word(",");
         }
-        self.end(); // Close enclosing cbox.
+        self.end(cb);
     }
 
     fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 653bd77cc4d..1e02ac8fd5d 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -5,6 +5,7 @@ use rustc_ast::ModKind;
 use rustc_ast::ptr::P;
 use rustc_span::Ident;
 
+use crate::pp::BoxMarker;
 use crate::pp::Breaks::Inconsistent;
 use crate::pprust::state::fixup::FixupContext;
 use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State};
@@ -96,7 +97,7 @@ impl<'a> State<'a> {
         define_opaque: Option<&[(ast::NodeId, ast::Path)]>,
     ) {
         self.print_define_opaques(define_opaque);
-        self.head("");
+        let (cb, ib) = self.head("");
         self.print_visibility(vis);
         self.print_safety(safety);
         self.print_defaultness(defaultness);
@@ -113,14 +114,14 @@ impl<'a> State<'a> {
         if body.is_some() {
             self.space();
         }
-        self.end(); // end the head-ibox
+        self.end(ib);
         if let Some(body) = body {
             self.word_space("=");
             self.print_expr(body, FixupContext::default());
         }
         self.print_where_clause(&generics.where_clause);
         self.word(";");
-        self.end(); // end the outer cbox
+        self.end(cb);
     }
 
     fn print_associated_type(
@@ -135,7 +136,7 @@ impl<'a> State<'a> {
     ) {
         let (before_predicates, after_predicates) =
             generics.where_clause.predicates.split_at(where_clauses.split);
-        self.head("");
+        let (cb, ib) = self.head("");
         self.print_visibility(vis);
         self.print_defaultness(defaultness);
         self.word_space("type");
@@ -153,8 +154,8 @@ impl<'a> State<'a> {
         }
         self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates);
         self.word(";");
-        self.end(); // end inner head-block
-        self.end(); // end outer head-block
+        self.end(ib);
+        self.end(cb);
     }
 
     /// Pretty-prints an item.
@@ -165,7 +166,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Item(item));
         match &item.kind {
             ast::ItemKind::ExternCrate(orig_name, ident) => {
-                self.head(visibility_qualified(&item.vis, "extern crate"));
+                let (cb, ib) = self.head(visibility_qualified(&item.vis, "extern crate"));
                 if let &Some(orig_name) = orig_name {
                     self.print_name(orig_name);
                     self.space();
@@ -174,8 +175,8 @@ impl<'a> State<'a> {
                 }
                 self.print_ident(*ident);
                 self.word(";");
-                self.end(); // end inner head-block
-                self.end(); // end outer head-block
+                self.end(ib);
+                self.end(cb);
             }
             ast::ItemKind::Use(tree) => {
                 self.print_visibility(&item.vis);
@@ -228,7 +229,7 @@ impl<'a> State<'a> {
                 self.print_fn_full(&item.vis, &item.attrs, &*func);
             }
             ast::ItemKind::Mod(safety, ident, mod_kind) => {
-                self.head(Self::to_string(|s| {
+                let (cb, ib) = self.head(Self::to_string(|s| {
                     s.print_visibility(&item.vis);
                     s.print_safety(*safety);
                     s.word("mod");
@@ -238,23 +239,23 @@ impl<'a> State<'a> {
                 match mod_kind {
                     ModKind::Loaded(items, ..) => {
                         self.nbsp();
-                        self.bopen();
+                        self.bopen(ib);
                         self.print_inner_attributes(&item.attrs);
                         for item in items {
                             self.print_item(item);
                         }
                         let empty = item.attrs.is_empty() && items.is_empty();
-                        self.bclose(item.span, empty);
+                        self.bclose(item.span, empty, cb);
                     }
                     ModKind::Unloaded => {
                         self.word(";");
-                        self.end(); // end inner head-block
-                        self.end(); // end outer head-block
+                        self.end(ib);
+                        self.end(cb);
                     }
                 }
             }
             ast::ItemKind::ForeignMod(nmod) => {
-                self.head(Self::to_string(|s| {
+                let (cb, ib) = self.head(Self::to_string(|s| {
                     s.print_safety(nmod.safety);
                     s.word("extern");
                 }));
@@ -262,18 +263,18 @@ impl<'a> State<'a> {
                     self.print_token_literal(abi.as_token_lit(), abi.span);
                     self.nbsp();
                 }
-                self.bopen();
+                self.bopen(ib);
                 self.print_foreign_mod(nmod, &item.attrs);
                 let empty = item.attrs.is_empty() && nmod.items.is_empty();
-                self.bclose(item.span, empty);
+                self.bclose(item.span, empty, cb);
             }
             ast::ItemKind::GlobalAsm(asm) => {
                 // FIXME: Print `builtin # global_asm` once macro `global_asm` uses `builtin_syntax`.
-                self.head(visibility_qualified(&item.vis, "global_asm!"));
+                let (cb, ib) = self.head(visibility_qualified(&item.vis, "global_asm!"));
                 self.print_inline_asm(asm);
                 self.word(";");
-                self.end();
-                self.end();
+                self.end(ib);
+                self.end(cb);
             }
             ast::ItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
@@ -297,12 +298,12 @@ impl<'a> State<'a> {
                 self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis);
             }
             ast::ItemKind::Struct(ident, struct_def, generics) => {
-                self.head(visibility_qualified(&item.vis, "struct"));
-                self.print_struct(struct_def, generics, *ident, item.span, true);
+                let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct"));
+                self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
             }
             ast::ItemKind::Union(ident, struct_def, generics) => {
-                self.head(visibility_qualified(&item.vis, "union"));
-                self.print_struct(struct_def, generics, *ident, item.span, true);
+                let (cb, ib) = self.head(visibility_qualified(&item.vis, "union"));
+                self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
             }
             ast::ItemKind::Impl(box ast::Impl {
                 safety,
@@ -314,7 +315,7 @@ impl<'a> State<'a> {
                 self_ty,
                 items,
             }) => {
-                self.head("");
+                let (cb, ib) = self.head("");
                 self.print_visibility(&item.vis);
                 self.print_defaultness(*defaultness);
                 self.print_safety(*safety);
@@ -343,13 +344,13 @@ impl<'a> State<'a> {
                 self.print_where_clause(&generics.where_clause);
 
                 self.space();
-                self.bopen();
+                self.bopen(ib);
                 self.print_inner_attributes(&item.attrs);
                 for impl_item in items {
                     self.print_assoc_item(impl_item);
                 }
                 let empty = item.attrs.is_empty() && items.is_empty();
-                self.bclose(item.span, empty);
+                self.bclose(item.span, empty, cb);
             }
             ast::ItemKind::Trait(box ast::Trait {
                 safety,
@@ -359,7 +360,7 @@ impl<'a> State<'a> {
                 bounds,
                 items,
             }) => {
-                self.head("");
+                let (cb, ib) = self.head("");
                 self.print_visibility(&item.vis);
                 self.print_safety(*safety);
                 self.print_is_auto(*is_auto);
@@ -372,16 +373,16 @@ impl<'a> State<'a> {
                 }
                 self.print_where_clause(&generics.where_clause);
                 self.word(" ");
-                self.bopen();
+                self.bopen(ib);
                 self.print_inner_attributes(&item.attrs);
                 for trait_item in items {
                     self.print_assoc_item(trait_item);
                 }
                 let empty = item.attrs.is_empty() && items.is_empty();
-                self.bclose(item.span, empty);
+                self.bclose(item.span, empty, cb);
             }
             ast::ItemKind::TraitAlias(ident, generics, bounds) => {
-                self.head(visibility_qualified(&item.vis, "trait"));
+                let (cb, ib) = self.head(visibility_qualified(&item.vis, "trait"));
                 self.print_ident(*ident);
                 self.print_generic_params(&generics.params);
                 self.nbsp();
@@ -391,8 +392,8 @@ impl<'a> State<'a> {
                 }
                 self.print_where_clause(&generics.where_clause);
                 self.word(";");
-                self.end(); // end inner head-block
-                self.end(); // end outer head-block
+                self.end(ib);
+                self.end(cb);
             }
             ast::ItemKind::MacCall(mac) => {
                 self.print_mac(mac);
@@ -433,28 +434,24 @@ impl<'a> State<'a> {
         span: rustc_span::Span,
         visibility: &ast::Visibility,
     ) {
-        self.head(visibility_qualified(visibility, "enum"));
+        let (cb, ib) = self.head(visibility_qualified(visibility, "enum"));
         self.print_ident(ident);
         self.print_generic_params(&generics.params);
         self.print_where_clause(&generics.where_clause);
         self.space();
-        self.print_variants(&enum_definition.variants, span)
-    }
-
-    fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
-        self.bopen();
-        for v in variants {
+        self.bopen(ib);
+        for v in enum_definition.variants.iter() {
             self.space_if_not_bol();
             self.maybe_print_comment(v.span.lo());
             self.print_outer_attributes(&v.attrs);
-            self.ibox(0);
+            let ib = self.ibox(0);
             self.print_variant(v);
             self.word(",");
-            self.end();
+            self.end(ib);
             self.maybe_print_trailing_comment(v.span, None);
         }
-        let empty = variants.is_empty();
-        self.bclose(span, empty)
+        let empty = enum_definition.variants.is_empty();
+        self.bclose(span, empty, cb)
     }
 
     pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) {
@@ -478,33 +475,6 @@ impl<'a> State<'a> {
         }
     }
 
-    pub(crate) fn print_record_struct_body(
-        &mut self,
-        fields: &[ast::FieldDef],
-        span: rustc_span::Span,
-    ) {
-        self.nbsp();
-        self.bopen();
-
-        let empty = fields.is_empty();
-        if !empty {
-            self.hardbreak_if_not_bol();
-
-            for field in fields {
-                self.hardbreak_if_not_bol();
-                self.maybe_print_comment(field.span.lo());
-                self.print_outer_attributes(&field.attrs);
-                self.print_visibility(&field.vis);
-                self.print_ident(field.ident.unwrap());
-                self.word_nbsp(":");
-                self.print_type(&field.ty);
-                self.word(",");
-            }
-        }
-
-        self.bclose(span, empty);
-    }
-
     fn print_struct(
         &mut self,
         struct_def: &ast::VariantData,
@@ -512,6 +482,8 @@ impl<'a> State<'a> {
         ident: Ident,
         span: rustc_span::Span,
         print_finalizer: bool,
+        cb: BoxMarker,
+        ib: BoxMarker,
     ) {
         self.print_ident(ident);
         self.print_generic_params(&generics.params);
@@ -531,21 +503,40 @@ impl<'a> State<'a> {
                 if print_finalizer {
                     self.word(";");
                 }
-                self.end();
-                self.end(); // Close the outer-box.
+                self.end(ib);
+                self.end(cb);
             }
             ast::VariantData::Struct { fields, .. } => {
                 self.print_where_clause(&generics.where_clause);
-                self.print_record_struct_body(fields, span);
+                self.nbsp();
+                self.bopen(ib);
+
+                let empty = fields.is_empty();
+                if !empty {
+                    self.hardbreak_if_not_bol();
+
+                    for field in fields {
+                        self.hardbreak_if_not_bol();
+                        self.maybe_print_comment(field.span.lo());
+                        self.print_outer_attributes(&field.attrs);
+                        self.print_visibility(&field.vis);
+                        self.print_ident(field.ident.unwrap());
+                        self.word_nbsp(":");
+                        self.print_type(&field.ty);
+                        self.word(",");
+                    }
+                }
+
+                self.bclose(span, empty, cb);
             }
         }
     }
 
     pub(crate) fn print_variant(&mut self, v: &ast::Variant) {
-        self.head("");
+        let (cb, ib) = self.head("");
         self.print_visibility(&v.vis);
         let generics = ast::Generics::default();
-        self.print_struct(&v.data, &generics, v.ident, v.span, false);
+        self.print_struct(&v.data, &generics, v.ident, v.span, false, cb, ib);
         if let Some(d) = &v.disr_expr {
             self.space();
             self.word_space("=");
@@ -636,9 +627,7 @@ impl<'a> State<'a> {
         kind: DelegationKind<'_>,
         body: &Option<P<ast::Block>>,
     ) {
-        if body.is_some() {
-            self.head("");
-        }
+        let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
         self.print_visibility(vis);
         self.word_nbsp("reuse");
 
@@ -670,9 +659,9 @@ impl<'a> State<'a> {
                 self.word("*");
             }
         }
-        if let Some(body) = body {
+        if let Some((body, (cb, ib))) = body_cb_ib {
             self.nbsp();
-            self.print_block_with_attrs(body, attrs);
+            self.print_block_with_attrs(body, attrs, cb, ib);
         } else {
             self.word(";");
         }
@@ -683,9 +672,8 @@ impl<'a> State<'a> {
 
         self.print_define_opaques(define_opaque.as_deref());
 
-        if body.is_some() {
-            self.head("");
-        }
+        let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
+
         self.print_visibility(vis);
         self.print_defaultness(*defaultness);
         self.print_fn(&sig.decl, sig.header, Some(*ident), generics);
@@ -693,9 +681,9 @@ impl<'a> State<'a> {
             self.nbsp();
             self.print_contract(contract);
         }
-        if let Some(body) = body {
+        if let Some((body, (cb, ib))) = body_cb_ib {
             self.nbsp();
-            self.print_block_with_attrs(body, attrs);
+            self.print_block_with_attrs(body, attrs, cb, ib);
         } else {
             self.word(";");
         }
@@ -851,10 +839,10 @@ impl<'a> State<'a> {
                 } else if let [(item, _)] = items.as_slice() {
                     self.print_use_tree(item);
                 } else {
-                    self.cbox(INDENT_UNIT);
+                    let cb = self.cbox(INDENT_UNIT);
                     self.word("{");
                     self.zerobreak();
-                    self.ibox(0);
+                    let ib = self.ibox(0);
                     for (pos, use_tree) in items.iter().with_position() {
                         let is_last = matches!(pos, Position::Last | Position::Only);
                         self.print_use_tree(&use_tree.0);
@@ -867,11 +855,11 @@ impl<'a> State<'a> {
                             }
                         }
                     }
-                    self.end();
+                    self.end(ib);
                     self.trailing_comma();
                     self.offset(-INDENT_UNIT);
                     self.word("}");
-                    self.end();
+                    self.end(cb);
                 }
             }
         }
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
index bc7f22766a5..786de529c5b 100644
--- a/compiler/rustc_ast_pretty/src/pprust/tests.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -11,10 +11,10 @@ fn fun_to_string(
     generics: &ast::Generics,
 ) -> String {
     to_string(|s| {
-        s.head("");
+        let (cb, ib) = s.head("");
         s.print_fn(decl, header, Some(ident), generics);
-        s.end(); // Close the head box.
-        s.end(); // Close the outer box.
+        s.end(ib);
+        s.end(cb);
     })
 }