]> git.lizzy.rs Git - rust.git/commitdiff
syntax::print -> new crate rustc_ast_pretty
authorMazdak Farrokhzad <twingoow@gmail.com>
Sat, 11 Jan 2020 16:02:46 +0000 (17:02 +0100)
committerMazdak Farrokhzad <twingoow@gmail.com>
Sat, 1 Feb 2020 17:59:49 +0000 (18:59 +0100)
66 files changed:
Cargo.lock
src/librustc/Cargo.toml
src/librustc_ast_lowering/Cargo.toml
src/librustc_ast_lowering/lib.rs
src/librustc_ast_passes/Cargo.toml
src/librustc_ast_passes/ast_validation.rs
src/librustc_ast_pretty/Cargo.toml [new file with mode: 0644]
src/librustc_ast_pretty/helpers.rs [new file with mode: 0644]
src/librustc_ast_pretty/lib.rs [new file with mode: 0644]
src/librustc_ast_pretty/pp.rs [new file with mode: 0644]
src/librustc_ast_pretty/pprust.rs [new file with mode: 0644]
src/librustc_ast_pretty/pprust/tests.rs [new file with mode: 0644]
src/librustc_attr/Cargo.toml
src/librustc_attr/builtin.rs
src/librustc_builtin_macros/Cargo.toml
src/librustc_builtin_macros/assert.rs
src/librustc_builtin_macros/log_syntax.rs
src/librustc_builtin_macros/proc_macro_harness.rs
src/librustc_builtin_macros/source_util.rs
src/librustc_builtin_macros/test.rs
src/librustc_driver/Cargo.toml
src/librustc_driver/pretty.rs
src/librustc_expand/Cargo.toml
src/librustc_expand/expand.rs
src/librustc_expand/mbe/macro_parser.rs
src/librustc_expand/mbe/macro_rules.rs
src/librustc_expand/mbe/quoted.rs
src/librustc_expand/proc_macro_server.rs
src/librustc_hir/Cargo.toml
src/librustc_hir/print.rs
src/librustc_lint/Cargo.toml
src/librustc_lint/builtin.rs
src/librustc_lint/levels.rs
src/librustc_lint/unused.rs
src/librustc_metadata/Cargo.toml
src/librustc_metadata/rmeta/encoder.rs
src/librustc_mir/Cargo.toml
src/librustc_mir/dataflow/mod.rs
src/librustc_parse/Cargo.toml
src/librustc_parse/lexer/tokentrees.rs
src/librustc_parse/lib.rs
src/librustc_parse/parser/attr.rs
src/librustc_parse/parser/diagnostics.rs
src/librustc_parse/parser/expr.rs
src/librustc_parse/parser/item.rs
src/librustc_parse/parser/mod.rs
src/librustc_parse/parser/pat.rs
src/librustc_resolve/Cargo.toml
src/librustc_resolve/diagnostics.rs
src/librustc_resolve/lib.rs
src/librustc_resolve/macros.rs
src/librustc_save_analysis/Cargo.toml
src/librustc_save_analysis/dump_visitor.rs
src/librustc_save_analysis/lib.rs
src/librustc_save_analysis/sig.rs
src/librustdoc/html/render.rs
src/librustdoc/lib.rs
src/librustdoc/test.rs
src/libsyntax/ast.rs
src/libsyntax/lib.rs
src/libsyntax/print/helpers.rs [deleted file]
src/libsyntax/print/pp.rs [deleted file]
src/libsyntax/print/pprust.rs [deleted file]
src/libsyntax/print/pprust/tests.rs [deleted file]
src/libsyntax/util/comments.rs
src/libsyntax/util/parser.rs

index b6e9738f10ec4f347d74d2e060f78be9dfa823be..62fbba7a77041be78f6581c936b6c449dda8e138 100644 (file)
@@ -3093,6 +3093,7 @@ dependencies = [
  "rustc-rayon",
  "rustc-rayon-core",
  "rustc_apfloat",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3359,6 +3360,7 @@ version = "0.0.0"
 dependencies = [
  "log",
  "rustc",
+ "rustc_ast_pretty",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_hir",
@@ -3375,6 +3377,7 @@ name = "rustc_ast_passes"
 version = "0.0.0"
 dependencies = [
  "log",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3385,10 +3388,21 @@ dependencies = [
  "syntax",
 ]
 
+[[package]]
+name = "rustc_ast_pretty"
+version = "0.0.0"
+dependencies = [
+ "log",
+ "rustc_data_structures",
+ "rustc_span",
+ "syntax",
+]
+
 [[package]]
 name = "rustc_attr"
 version = "0.0.0"
 dependencies = [
+ "rustc_ast_pretty",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_feature",
@@ -3406,6 +3420,7 @@ version = "0.0.0"
 dependencies = [
  "fmt_macros",
  "log",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3526,6 +3541,7 @@ dependencies = [
  "lazy_static 1.4.0",
  "log",
  "rustc",
+ "rustc_ast_pretty",
  "rustc_codegen_utils",
  "rustc_data_structures",
  "rustc_error_codes",
@@ -3572,6 +3588,7 @@ version = "0.0.0"
 dependencies = [
  "log",
  "rustc_ast_passes",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3602,6 +3619,7 @@ version = "0.0.0"
 name = "rustc_hir"
 version = "0.0.0"
 dependencies = [
+ "rustc_ast_pretty",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_index",
@@ -3693,6 +3711,7 @@ version = "0.0.0"
 dependencies = [
  "log",
  "rustc",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3734,6 +3753,7 @@ dependencies = [
  "log",
  "memmap",
  "rustc",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3762,6 +3782,7 @@ dependencies = [
  "polonius-engine",
  "rustc",
  "rustc_apfloat",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3805,6 +3826,7 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "log",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3872,6 +3894,7 @@ dependencies = [
  "log",
  "rustc",
  "rustc_ast_lowering",
+ "rustc_ast_pretty",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -3893,6 +3916,7 @@ dependencies = [
  "rls-data",
  "rls-span",
  "rustc",
+ "rustc_ast_pretty",
  "rustc_codegen_utils",
  "rustc_data_structures",
  "rustc_hir",
index 782c6879ac58fd6945bd2e6c921d13c3f4ca16e5..7df5185d27c4066f5f208c5aec3f4338a9cbc4e0 100644 (file)
@@ -21,6 +21,8 @@ rustc-rayon = "0.3.0"
 rustc-rayon-core = "0.3.0"
 polonius-engine = "0.11.0"
 rustc_apfloat = { path = "../librustc_apfloat" }
+# FIXME(Centril): remove this dependency when stuff is moved to rustc_lint.
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_feature = { path = "../librustc_feature" }
 rustc_hir = { path = "../librustc_hir" }
index 4b786d6245fc4056d6ee77a77b54a24433024788..f6ab60e199f335f977d1f92d8a6d6aeb52d080f3 100644 (file)
@@ -12,6 +12,7 @@ doctest = false
 [dependencies]
 log = { version = "0.4", features = ["release_max_level_info", "std"] }
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_hir = { path = "../librustc_hir" }
 rustc_target = { path = "../librustc_target" }
 rustc_data_structures = { path = "../librustc_data_structures" }
index 932ca743b02c66513be0ba5b1fcc8377997d7c4c..c3e96a31e4001264a4fc890accf97497cbbad3b3 100644 (file)
@@ -38,6 +38,7 @@
 use rustc::hir::map::definitions::{DefKey, DefPathData, Definitions};
 use rustc::hir::map::Map;
 use rustc::{bug, span_bug};
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
@@ -60,7 +61,6 @@
 use syntax::ast::*;
 use syntax::attr;
 use syntax::node_id::NodeMap;
-use syntax::print::pprust;
 use syntax::token::{self, Nonterminal, Token};
 use syntax::tokenstream::{TokenStream, TokenTree};
 use syntax::visit::{self, Visitor};
index 2b25f04ce9acada2d4ce8e7d2c6fb46a4fbf149b..01d2ac449b590b034a054c494f5777ecee71eb09 100644 (file)
@@ -10,6 +10,7 @@ path = "lib.rs"
 
 [dependencies]
 log = "0.4"
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
index c6ea97be583ba8c0dbf8d37aa399488b02edc219..4bb55d6acddcd2038a9674927f19ce265e2d0d18 100644 (file)
@@ -6,6 +6,7 @@
 // This pass is supposed to perform only simple checks not requiring name resolution
 // or type checking or some other kind of complex analysis.
 
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{struct_span_err, Applicability, FatalError};
 use rustc_parse::validate_attr;
@@ -19,7 +20,6 @@
 use syntax::ast::*;
 use syntax::attr;
 use syntax::expand::is_proc_macro_attr;
-use syntax::print::pprust;
 use syntax::visit::{self, Visitor};
 use syntax::walk_list;
 
diff --git a/src/librustc_ast_pretty/Cargo.toml b/src/librustc_ast_pretty/Cargo.toml
new file mode 100644 (file)
index 0000000..2f7f804
--- /dev/null
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_ast_pretty"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+name = "rustc_ast_pretty"
+path = "lib.rs"
+doctest = false
+
+[dependencies]
+log = "0.4"
+rustc_span = { path = "../librustc_span" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+syntax = { path = "../libsyntax" }
diff --git a/src/librustc_ast_pretty/helpers.rs b/src/librustc_ast_pretty/helpers.rs
new file mode 100644 (file)
index 0000000..dce856d
--- /dev/null
@@ -0,0 +1,38 @@
+use crate::pp::Printer;
+use std::borrow::Cow;
+
+impl Printer {
+    pub fn word_space<W: Into<Cow<'static, str>>>(&mut self, w: W) {
+        self.word(w);
+        self.space();
+    }
+
+    pub fn popen(&mut self) {
+        self.word("(");
+    }
+
+    pub fn pclose(&mut self) {
+        self.word(")");
+    }
+
+    pub fn hardbreak_if_not_bol(&mut self) {
+        if !self.is_beginning_of_line() {
+            self.hardbreak()
+        }
+    }
+
+    pub fn space_if_not_bol(&mut self) {
+        if !self.is_beginning_of_line() {
+            self.space();
+        }
+    }
+
+    pub fn nbsp(&mut self) {
+        self.word(" ")
+    }
+
+    pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+        self.word(w);
+        self.nbsp()
+    }
+}
diff --git a/src/librustc_ast_pretty/lib.rs b/src/librustc_ast_pretty/lib.rs
new file mode 100644 (file)
index 0000000..4c3a836
--- /dev/null
@@ -0,0 +1,6 @@
+#![feature(bool_to_option)]
+#![feature(crate_visibility_modifier)]
+
+mod helpers;
+pub mod pp;
+pub mod pprust;
diff --git a/src/librustc_ast_pretty/pp.rs b/src/librustc_ast_pretty/pp.rs
new file mode 100644 (file)
index 0000000..e6090db
--- /dev/null
@@ -0,0 +1,640 @@
+//! This pretty-printer is a direct reimplementation of Philip Karlton's
+//! Mesa pretty-printer, as described in appendix A of
+//!
+//! ```text
+//! STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen.
+//! Stanford Department of Computer Science, 1979.
+//! ```
+//!
+//! The algorithm's aim is to break a stream into as few lines as possible
+//! while respecting the indentation-consistency requirements of the enclosing
+//! block, and avoiding breaking at silly places on block boundaries, for
+//! example, between "x" and ")" in "x)".
+//!
+//! I am implementing this algorithm because it comes with 20 pages of
+//! documentation explaining its theory, and because it addresses the set of
+//! concerns I've seen other pretty-printers fall down on. Weirdly. Even though
+//! it's 32 years old. What can I say?
+//!
+//! Despite some redundancies and quirks in the way it's implemented in that
+//! paper, I've opted to keep the implementation here as similar as I can,
+//! changing only what was blatantly wrong, a typo, or sufficiently
+//! non-idiomatic rust that it really stuck out.
+//!
+//! In particular you'll see a certain amount of churn related to INTEGER vs.
+//! CARDINAL in the Mesa implementation. Mesa apparently interconverts the two
+//! somewhat readily? In any case, I've used usize for indices-in-buffers and
+//! ints for character-sizes-and-indentation-offsets. This respects the need
+//! for ints to "go negative" while carrying a pending-calculation balance, and
+//! helps differentiate all the numbers flying around internally (slightly).
+//!
+//! I also inverted the indentation arithmetic used in the print stack, since
+//! the Mesa implementation (somewhat randomly) stores the offset on the print
+//! stack in terms of margin-col rather than col itself. I store col.
+//!
+//! I also implemented a small change in the String token, in that I store an
+//! explicit length for the string. For most tokens this is just the length of
+//! the accompanying string. But it's necessary to permit it to differ, for
+//! encoding things that are supposed to "go on their own line" -- certain
+//! classes of comment and blank-line -- where relying on adjacent
+//! hardbreak-like Break tokens with long blankness indication doesn't actually
+//! work. To see why, consider when there is a "thing that should be on its own
+//! line" between two long blocks, say functions. If you put a hardbreak after
+//! each function (or before each) and the breaking algorithm decides to break
+//! there anyways (because the functions themselves are long) you wind up with
+//! extra blank lines. If you don't put hardbreaks you can wind up with the
+//! "thing which should be on its own line" not getting its own line in the
+//! rare case of "really small functions" or such. This re-occurs with comments
+//! and explicit blank lines. So in those cases we use a string with a payload
+//! we want isolated to a line and an explicit length that's huge, surrounded
+//! by two zero-length breaks. The algorithm will try its best to fit it on a
+//! line (which it can't) and so naturally place the content on its own line to
+//! avoid combining it with other lines and making matters even worse.
+//!
+//! # Explanation
+//!
+//! In case you do not have the paper, here is an explanation of what's going
+//! on.
+//!
+//! There is a stream of input tokens flowing through this printer.
+//!
+//! The printer buffers up to 3N tokens inside itself, where N is linewidth.
+//! Yes, linewidth is chars and tokens are multi-char, but in the worst
+//! case every token worth buffering is 1 char long, so it's ok.
+//!
+//! Tokens are String, Break, and Begin/End to delimit blocks.
+//!
+//! Begin tokens can carry an offset, saying "how far to indent when you break
+//! inside here", as well as a flag indicating "consistent" or "inconsistent"
+//! breaking. Consistent breaking means that after the first break, no attempt
+//! will be made to flow subsequent breaks together onto lines. Inconsistent
+//! is the opposite. Inconsistent breaking example would be, say:
+//!
+//! ```
+//! foo(hello, there, good, friends)
+//! ```
+//!
+//! breaking inconsistently to become
+//!
+//! ```
+//! foo(hello, there
+//!     good, friends);
+//! ```
+//!
+//! whereas a consistent breaking would yield:
+//!
+//! ```
+//! foo(hello,
+//!     there
+//!     good,
+//!     friends);
+//! ```
+//!
+//! That is, in the consistent-break blocks we value vertical alignment
+//! more than the ability to cram stuff onto a line. But in all cases if it
+//! can make a block a one-liner, it'll do so.
+//!
+//! Carrying on with high-level logic:
+//!
+//! The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and
+//! 'right' indices denote the active portion of the ring buffer as well as
+//! describing hypothetical points-in-the-infinite-stream at most 3N tokens
+//! apart (i.e., "not wrapped to ring-buffer boundaries"). The paper will switch
+//! between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer
+//! and point-in-infinite-stream senses freely.
+//!
+//! There is a parallel ring buffer, `size`, that holds the calculated size of
+//! each token. Why calculated? Because for Begin/End pairs, the "size"
+//! includes everything between the pair. That is, the "size" of Begin is
+//! actually the sum of the sizes of everything between Begin and the paired
+//! End that follows. Since that is arbitrarily far in the future, `size` is
+//! being rewritten regularly while the printer runs; in fact most of the
+//! machinery is here to work out `size` entries on the fly (and give up when
+//! they're so obviously over-long that "infinity" is a good enough
+//! approximation for purposes of line breaking).
+//!
+//! The "input side" of the printer is managed as an abstract process called
+//! SCAN, which uses `scan_stack`, to manage calculating `size`. SCAN is, in
+//! other words, the process of calculating 'size' entries.
+//!
+//! The "output side" of the printer is managed by an abstract process called
+//! PRINT, which uses `print_stack`, `margin` and `space` to figure out what to
+//! do with each token/size pair it consumes as it goes. It's trying to consume
+//! the entire buffered window, but can't output anything until the size is >=
+//! 0 (sizes are set to negative while they're pending calculation).
+//!
+//! So SCAN takes input and buffers tokens and pending calculations, while
+//! PRINT gobbles up completed calculations and tokens from the buffer. The
+//! theory is that the two can never get more than 3N tokens apart, because
+//! once there's "obviously" too much data to fit on a line, in a size
+//! calculation, SCAN will write "infinity" to the size and let PRINT consume
+//! it.
+//!
+//! In this implementation (following the paper, again) the SCAN process is the
+//! methods called `Printer::scan_*`, and the 'PRINT' process is the
+//! method called `Printer::print`.
+
+use log::debug;
+use std::borrow::Cow;
+use std::collections::VecDeque;
+use std::fmt;
+
+/// How to break. Described in more detail in the module docs.
+#[derive(Clone, Copy, PartialEq)]
+pub enum Breaks {
+    Consistent,
+    Inconsistent,
+}
+
+#[derive(Clone, Copy)]
+pub struct BreakToken {
+    offset: isize,
+    blank_space: isize,
+}
+
+#[derive(Clone, Copy)]
+pub struct BeginToken {
+    offset: isize,
+    breaks: Breaks,
+}
+
+#[derive(Clone)]
+pub enum Token {
+    // In practice a string token contains either a `&'static str` or a
+    // `String`. `Cow` is overkill for this because we never modify the data,
+    // but it's more convenient than rolling our own more specialized type.
+    String(Cow<'static, str>),
+    Break(BreakToken),
+    Begin(BeginToken),
+    End,
+    Eof,
+}
+
+impl Token {
+    crate fn is_eof(&self) -> bool {
+        match *self {
+            Token::Eof => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_hardbreak_tok(&self) -> bool {
+        match *self {
+            Token::Break(BreakToken { offset: 0, blank_space: bs }) if bs == SIZE_INFINITY => true,
+            _ => false,
+        }
+    }
+}
+
+impl fmt::Display for Token {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            Token::String(ref s) => write!(f, "STR({},{})", s, s.len()),
+            Token::Break(_) => f.write_str("BREAK"),
+            Token::Begin(_) => f.write_str("BEGIN"),
+            Token::End => f.write_str("END"),
+            Token::Eof => f.write_str("EOF"),
+        }
+    }
+}
+
+fn buf_str(buf: &[BufEntry], left: usize, right: usize, lim: usize) -> String {
+    let n = buf.len();
+    let mut i = left;
+    let mut l = lim;
+    let mut s = String::from("[");
+    while i != right && l != 0 {
+        l -= 1;
+        if i != left {
+            s.push_str(", ");
+        }
+        s.push_str(&format!("{}={}", buf[i].size, &buf[i].token));
+        i += 1;
+        i %= n;
+    }
+    s.push(']');
+    s
+}
+
+#[derive(Copy, Clone)]
+enum PrintStackBreak {
+    Fits,
+    Broken(Breaks),
+}
+
+#[derive(Copy, Clone)]
+struct PrintStackElem {
+    offset: isize,
+    pbreak: PrintStackBreak,
+}
+
+const SIZE_INFINITY: isize = 0xffff;
+
+pub fn mk_printer() -> Printer {
+    let linewidth = 78;
+    // Yes 55, it makes the ring buffers big enough to never fall behind.
+    let n: usize = 55 * linewidth;
+    debug!("mk_printer {}", linewidth);
+    Printer {
+        out: String::new(),
+        buf_max_len: n,
+        margin: linewidth as isize,
+        space: linewidth as isize,
+        left: 0,
+        right: 0,
+        // Initialize a single entry; advance_right() will extend it on demand
+        // up to `buf_max_len` elements.
+        buf: vec![BufEntry::default()],
+        left_total: 0,
+        right_total: 0,
+        scan_stack: VecDeque::new(),
+        print_stack: Vec::new(),
+        pending_indentation: 0,
+    }
+}
+
+pub struct Printer {
+    out: String,
+    buf_max_len: usize,
+    /// Width of lines we're constrained to
+    margin: isize,
+    /// Number of spaces left on line
+    space: isize,
+    /// Index of left side of input stream
+    left: usize,
+    /// Index of right side of input stream
+    right: usize,
+    /// Ring-buffer of tokens and calculated sizes
+    buf: Vec<BufEntry>,
+    /// Running size of stream "...left"
+    left_total: isize,
+    /// Running size of stream "...right"
+    right_total: isize,
+    /// Pseudo-stack, really a ring too. Holds the
+    /// primary-ring-buffers index of the Begin that started the
+    /// current block, possibly with the most recent Break after that
+    /// Begin (if there is any) on top of it. Stuff is flushed off the
+    /// bottom as it becomes irrelevant due to the primary ring-buffer
+    /// advancing.
+    scan_stack: VecDeque<usize>,
+    /// Stack of blocks-in-progress being flushed by print
+    print_stack: Vec<PrintStackElem>,
+    /// Buffered indentation to avoid writing trailing whitespace
+    pending_indentation: isize,
+}
+
+#[derive(Clone)]
+struct BufEntry {
+    token: Token,
+    size: isize,
+}
+
+impl Default for BufEntry {
+    fn default() -> Self {
+        BufEntry { token: Token::Eof, size: 0 }
+    }
+}
+
+impl Printer {
+    pub fn last_token(&self) -> Token {
+        self.buf[self.right].token.clone()
+    }
+
+    /// Be very careful with this!
+    pub fn replace_last_token(&mut self, t: Token) {
+        self.buf[self.right].token = t;
+    }
+
+    fn scan_eof(&mut self) {
+        if !self.scan_stack.is_empty() {
+            self.check_stack(0);
+            self.advance_left();
+        }
+    }
+
+    fn scan_begin(&mut self, b: BeginToken) {
+        if self.scan_stack.is_empty() {
+            self.left_total = 1;
+            self.right_total = 1;
+            self.left = 0;
+            self.right = 0;
+        } else {
+            self.advance_right();
+        }
+        debug!("pp Begin({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
+        self.scan_push(BufEntry { token: Token::Begin(b), size: -self.right_total });
+    }
+
+    fn scan_end(&mut self) {
+        if self.scan_stack.is_empty() {
+            debug!("pp End/print Vec<{},{}>", self.left, self.right);
+            self.print_end();
+        } else {
+            debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
+            self.advance_right();
+            self.scan_push(BufEntry { token: Token::End, size: -1 });
+        }
+    }
+
+    fn scan_break(&mut self, b: BreakToken) {
+        if self.scan_stack.is_empty() {
+            self.left_total = 1;
+            self.right_total = 1;
+            self.left = 0;
+            self.right = 0;
+        } else {
+            self.advance_right();
+        }
+        debug!("pp Break({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
+        self.check_stack(0);
+        self.scan_push(BufEntry { token: Token::Break(b), size: -self.right_total });
+        self.right_total += b.blank_space;
+    }
+
+    fn scan_string(&mut self, s: Cow<'static, str>) {
+        if self.scan_stack.is_empty() {
+            debug!("pp String('{}')/print Vec<{},{}>", s, self.left, self.right);
+            self.print_string(s);
+        } else {
+            debug!("pp String('{}')/buffer Vec<{},{}>", s, self.left, self.right);
+            self.advance_right();
+            let len = s.len() as isize;
+            self.buf[self.right] = BufEntry { token: Token::String(s), size: len };
+            self.right_total += len;
+            self.check_stream();
+        }
+    }
+
+    fn check_stream(&mut self) {
+        debug!(
+            "check_stream Vec<{}, {}> with left_total={}, right_total={}",
+            self.left, self.right, self.left_total, self.right_total
+        );
+        if self.right_total - self.left_total > self.space {
+            debug!(
+                "scan window is {}, longer than space on line ({})",
+                self.right_total - self.left_total,
+                self.space
+            );
+            if Some(&self.left) == self.scan_stack.back() {
+                debug!("setting {} to infinity and popping", self.left);
+                let scanned = self.scan_pop_bottom();
+                self.buf[scanned].size = SIZE_INFINITY;
+            }
+            self.advance_left();
+            if self.left != self.right {
+                self.check_stream();
+            }
+        }
+    }
+
+    fn scan_push(&mut self, entry: BufEntry) {
+        debug!("scan_push {}", self.right);
+        self.buf[self.right] = entry;
+        self.scan_stack.push_front(self.right);
+    }
+
+    fn scan_pop(&mut self) -> usize {
+        self.scan_stack.pop_front().unwrap()
+    }
+
+    fn scan_top(&mut self) -> usize {
+        *self.scan_stack.front().unwrap()
+    }
+
+    fn scan_pop_bottom(&mut self) -> usize {
+        self.scan_stack.pop_back().unwrap()
+    }
+
+    fn advance_right(&mut self) {
+        self.right += 1;
+        self.right %= self.buf_max_len;
+        // Extend the buf if necessary.
+        if self.right == self.buf.len() {
+            self.buf.push(BufEntry::default());
+        }
+        assert_ne!(self.right, self.left);
+    }
+
+    fn advance_left(&mut self) {
+        debug!(
+            "advance_left Vec<{},{}>, sizeof({})={}",
+            self.left, self.right, self.left, self.buf[self.left].size
+        );
+
+        let mut left_size = self.buf[self.left].size;
+
+        while left_size >= 0 {
+            let left = self.buf[self.left].token.clone();
+
+            let len = match left {
+                Token::Break(b) => b.blank_space,
+                Token::String(ref s) => {
+                    let len = s.len() as isize;
+                    assert_eq!(len, left_size);
+                    len
+                }
+                _ => 0,
+            };
+
+            self.print(left, left_size);
+
+            self.left_total += len;
+
+            if self.left == self.right {
+                break;
+            }
+
+            self.left += 1;
+            self.left %= self.buf_max_len;
+
+            left_size = self.buf[self.left].size;
+        }
+    }
+
+    fn check_stack(&mut self, k: usize) {
+        if !self.scan_stack.is_empty() {
+            let x = self.scan_top();
+            match self.buf[x].token {
+                Token::Begin(_) => {
+                    if k > 0 {
+                        self.scan_pop();
+                        self.buf[x].size += self.right_total;
+                        self.check_stack(k - 1);
+                    }
+                }
+                Token::End => {
+                    // paper says + not =, but that makes no sense.
+                    self.scan_pop();
+                    self.buf[x].size = 1;
+                    self.check_stack(k + 1);
+                }
+                _ => {
+                    self.scan_pop();
+                    self.buf[x].size += self.right_total;
+                    if k > 0 {
+                        self.check_stack(k);
+                    }
+                }
+            }
+        }
+    }
+
+    fn print_newline(&mut self, amount: isize) {
+        debug!("NEWLINE {}", amount);
+        self.out.push('\n');
+        self.pending_indentation = 0;
+        self.indent(amount);
+    }
+
+    fn indent(&mut self, amount: isize) {
+        debug!("INDENT {}", amount);
+        self.pending_indentation += amount;
+    }
+
+    fn get_top(&mut self) -> PrintStackElem {
+        match self.print_stack.last() {
+            Some(el) => *el,
+            None => {
+                PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
+            }
+        }
+    }
+
+    fn print_begin(&mut self, b: BeginToken, l: isize) {
+        if l > self.space {
+            let col = self.margin - self.space + b.offset;
+            debug!("print Begin -> push broken block at col {}", col);
+            self.print_stack
+                .push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) });
+        } else {
+            debug!("print Begin -> push fitting block");
+            self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits });
+        }
+    }
+
+    fn print_end(&mut self) {
+        debug!("print End -> pop End");
+        self.print_stack.pop().unwrap();
+    }
+
+    fn print_break(&mut self, b: BreakToken, l: isize) {
+        let top = self.get_top();
+        match top.pbreak {
+            PrintStackBreak::Fits => {
+                debug!("print Break({}) in fitting block", b.blank_space);
+                self.space -= b.blank_space;
+                self.indent(b.blank_space);
+            }
+            PrintStackBreak::Broken(Breaks::Consistent) => {
+                debug!("print Break({}+{}) in consistent block", top.offset, b.offset);
+                self.print_newline(top.offset + b.offset);
+                self.space = self.margin - (top.offset + b.offset);
+            }
+            PrintStackBreak::Broken(Breaks::Inconsistent) => {
+                if l > self.space {
+                    debug!("print Break({}+{}) w/ newline in inconsistent", top.offset, b.offset);
+                    self.print_newline(top.offset + b.offset);
+                    self.space = self.margin - (top.offset + b.offset);
+                } else {
+                    debug!("print Break({}) w/o newline in inconsistent", b.blank_space);
+                    self.indent(b.blank_space);
+                    self.space -= b.blank_space;
+                }
+            }
+        }
+    }
+
+    fn print_string(&mut self, s: Cow<'static, str>) {
+        let len = s.len() as isize;
+        debug!("print String({})", s);
+        // assert!(len <= space);
+        self.space -= len;
+
+        // Write the pending indent. A more concise way of doing this would be:
+        //
+        //   write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
+        //
+        // But that is significantly slower. This code is sufficiently hot, and indents can get
+        // sufficiently large, that the difference is significant on some workloads.
+        self.out.reserve(self.pending_indentation as usize);
+        self.out.extend(std::iter::repeat(' ').take(self.pending_indentation as usize));
+        self.pending_indentation = 0;
+        self.out.push_str(&s);
+    }
+
+    fn print(&mut self, token: Token, l: isize) {
+        debug!("print {} {} (remaining line space={})", token, l, self.space);
+        debug!("{}", buf_str(&self.buf, self.left, self.right, 6));
+        match token {
+            Token::Begin(b) => self.print_begin(b, l),
+            Token::End => self.print_end(),
+            Token::Break(b) => self.print_break(b, l),
+            Token::String(s) => {
+                let len = s.len() as isize;
+                assert_eq!(len, l);
+                self.print_string(s);
+            }
+            Token::Eof => panic!(), // Eof should never get here.
+        }
+    }
+
+    // Convenience functions to talk to the printer.
+
+    /// "raw box"
+    pub fn rbox(&mut self, indent: usize, b: Breaks) {
+        self.scan_begin(BeginToken { offset: indent as isize, breaks: b })
+    }
+
+    /// Inconsistent breaking box
+    pub fn ibox(&mut self, indent: usize) {
+        self.rbox(indent, Breaks::Inconsistent)
+    }
+
+    /// Consistent breaking box
+    pub fn cbox(&mut self, indent: usize) {
+        self.rbox(indent, Breaks::Consistent)
+    }
+
+    pub fn break_offset(&mut self, n: usize, off: isize) {
+        self.scan_break(BreakToken { offset: off, blank_space: n as isize })
+    }
+
+    pub fn end(&mut self) {
+        self.scan_end()
+    }
+
+    pub fn eof(mut self) -> String {
+        self.scan_eof();
+        self.out
+    }
+
+    pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
+        let s = wrd.into();
+        self.scan_string(s)
+    }
+
+    fn spaces(&mut self, n: usize) {
+        self.break_offset(n, 0)
+    }
+
+    crate fn zerobreak(&mut self) {
+        self.spaces(0)
+    }
+
+    pub fn space(&mut self) {
+        self.spaces(1)
+    }
+
+    pub fn hardbreak(&mut self) {
+        self.spaces(SIZE_INFINITY as usize)
+    }
+
+    pub fn is_beginning_of_line(&self) -> bool {
+        self.last_token().is_eof() || self.last_token().is_hardbreak_tok()
+    }
+
+    pub fn hardbreak_tok_offset(off: isize) -> Token {
+        Token::Break(BreakToken { offset: off, blank_space: SIZE_INFINITY })
+    }
+}
diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs
new file mode 100644 (file)
index 0000000..2257710
--- /dev/null
@@ -0,0 +1,2780 @@
+use crate::pp::Breaks::{Consistent, Inconsistent};
+use crate::pp::{self, Breaks};
+
+use rustc_data_structures::sync::Once;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{dummy_spanned, SourceMap, Spanned};
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{BytePos, FileName, Span};
+use syntax::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
+use syntax::ast::{Attribute, GenericArg, MacArgs};
+use syntax::ast::{GenericBound, SelfKind, TraitBoundModifier};
+use syntax::attr;
+use syntax::ptr::P;
+use syntax::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
+use syntax::tokenstream::{self, TokenStream, TokenTree};
+use syntax::util::parser::{self, AssocOp, Fixity};
+use syntax::util::{classify, comments};
+
+use std::borrow::Cow;
+
+#[cfg(test)]
+mod tests;
+
+pub enum MacHeader<'a> {
+    Path(&'a ast::Path),
+    Keyword(&'static str),
+}
+
+pub enum AnnNode<'a> {
+    Ident(&'a ast::Ident),
+    Name(&'a ast::Name),
+    Block(&'a ast::Block),
+    Item(&'a ast::Item),
+    SubItem(ast::NodeId),
+    Expr(&'a ast::Expr),
+    Pat(&'a ast::Pat),
+    Crate(&'a ast::Crate),
+}
+
+pub trait PpAnn {
+    fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+    fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+}
+
+#[derive(Copy, Clone)]
+pub struct NoAnn;
+
+impl PpAnn for NoAnn {}
+
+pub struct Comments<'a> {
+    cm: &'a SourceMap,
+    comments: Vec<comments::Comment>,
+    current: usize,
+}
+
+impl<'a> Comments<'a> {
+    pub fn new(cm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
+        let comments = comments::gather_comments(cm, filename, input);
+        Comments { cm, comments, current: 0 }
+    }
+
+    pub fn next(&self) -> Option<comments::Comment> {
+        self.comments.get(self.current).cloned()
+    }
+
+    pub fn trailing_comment(
+        &mut self,
+        span: rustc_span::Span,
+        next_pos: Option<BytePos>,
+    ) -> Option<comments::Comment> {
+        if let Some(cmnt) = self.next() {
+            if cmnt.style != comments::Trailing {
+                return None;
+            }
+            let span_line = self.cm.lookup_char_pos(span.hi());
+            let comment_line = self.cm.lookup_char_pos(cmnt.pos);
+            let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
+            if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
+                return Some(cmnt);
+            }
+        }
+
+        None
+    }
+}
+
+pub struct State<'a> {
+    pub s: pp::Printer,
+    comments: Option<Comments<'a>>,
+    ann: &'a (dyn PpAnn + 'a),
+    is_expanded: bool,
+}
+
+crate const INDENT_UNIT: usize = 4;
+
+/// Requires you to pass an input filename and reader so that
+/// it can scan the input text for comments to copy forward.
+pub fn print_crate<'a>(
+    cm: &'a SourceMap,
+    krate: &ast::Crate,
+    filename: FileName,
+    input: String,
+    ann: &'a dyn PpAnn,
+    is_expanded: bool,
+    edition: Edition,
+    injected_crate_name: &Once<Symbol>,
+) -> String {
+    let mut s = State {
+        s: pp::mk_printer(),
+        comments: Some(Comments::new(cm, filename, input)),
+        ann,
+        is_expanded,
+    };
+
+    if is_expanded && injected_crate_name.try_get().is_some() {
+        // We need to print `#![no_std]` (and its feature gate) so that
+        // compiling pretty-printed source won't inject libstd again.
+        // However, we don't want these attributes in the AST because
+        // of the feature gate, so we fake them up here.
+
+        // `#![feature(prelude_import)]`
+        let pi_nested = attr::mk_nested_word_item(ast::Ident::with_dummy_span(sym::prelude_import));
+        let list = attr::mk_list_item(ast::Ident::with_dummy_span(sym::feature), vec![pi_nested]);
+        let fake_attr = attr::mk_attr_inner(list);
+        s.print_attribute(&fake_attr);
+
+        // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
+        // root, so this is not needed, and actually breaks things.
+        if edition == Edition::Edition2015 {
+            // `#![no_std]`
+            let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
+            let fake_attr = attr::mk_attr_inner(no_std_meta);
+            s.print_attribute(&fake_attr);
+        }
+    }
+
+    s.print_mod(&krate.module, &krate.attrs);
+    s.print_remaining_comments();
+    s.ann.post(&mut s, AnnNode::Crate(krate));
+    s.s.eof()
+}
+
+pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
+    let mut printer =
+        State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false };
+    f(&mut printer);
+    printer.s.eof()
+}
+
+// This makes comma-separated lists look slightly nicer,
+// and also addresses a specific regression described in issue #63896.
+fn tt_prepend_space(tt: &TokenTree) -> bool {
+    match tt {
+        TokenTree::Token(token) => match token.kind {
+            token::Comma => false,
+            _ => true,
+        },
+        _ => true,
+    }
+}
+
+fn binop_to_string(op: BinOpToken) -> &'static str {
+    match op {
+        token::Plus => "+",
+        token::Minus => "-",
+        token::Star => "*",
+        token::Slash => "/",
+        token::Percent => "%",
+        token::Caret => "^",
+        token::And => "&",
+        token::Or => "|",
+        token::Shl => "<<",
+        token::Shr => ">>",
+    }
+}
+
+pub fn literal_to_string(lit: token::Lit) -> String {
+    let token::Lit { kind, symbol, suffix } = lit;
+    let mut out = match kind {
+        token::Byte => format!("b'{}'", symbol),
+        token::Char => format!("'{}'", symbol),
+        token::Str => format!("\"{}\"", symbol),
+        token::StrRaw(n) => {
+            format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+        }
+        token::ByteStr => format!("b\"{}\"", symbol),
+        token::ByteStrRaw(n) => {
+            format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+        }
+        token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
+    };
+
+    if let Some(suffix) = suffix {
+        out.push_str(&suffix.as_str())
+    }
+
+    out
+}
+
+/// Print an ident from AST, `$crate` is converted into its respective crate name.
+pub fn ast_ident_to_string(ident: ast::Ident, is_raw: bool) -> String {
+    ident_to_string(ident.name, is_raw, Some(ident.span))
+}
+
+// AST pretty-printer is used as a fallback for turning AST structures into token streams for
+// proc macros. Additionally, proc macros may stringify their input and expect it survive the
+// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
+// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
+// hygiene data, most importantly name of the crate it refers to.
+// As a result we print `$crate` as `crate` if it refers to the local crate
+// and as `::other_crate_name` if it refers to some other crate.
+// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
+// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
+// so we should not perform this lossy conversion if the top level call to the pretty-printer was
+// done for a token stream or a single token.
+fn ident_to_string(name: ast::Name, is_raw: bool, convert_dollar_crate: Option<Span>) -> String {
+    if is_raw {
+        format!("r#{}", name)
+    } else {
+        if name == kw::DollarCrate {
+            if let Some(span) = convert_dollar_crate {
+                let converted = span.ctxt().dollar_crate_name();
+                return if converted.is_path_segment_keyword() {
+                    converted.to_string()
+                } else {
+                    format!("::{}", converted)
+                };
+            }
+        }
+        name.to_string()
+    }
+}
+
+/// Print the token kind precisely, without converting `$crate` into its respective crate name.
+pub fn token_kind_to_string(tok: &TokenKind) -> String {
+    token_kind_to_string_ext(tok, None)
+}
+
+fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
+    match *tok {
+        token::Eq => "=".to_string(),
+        token::Lt => "<".to_string(),
+        token::Le => "<=".to_string(),
+        token::EqEq => "==".to_string(),
+        token::Ne => "!=".to_string(),
+        token::Ge => ">=".to_string(),
+        token::Gt => ">".to_string(),
+        token::Not => "!".to_string(),
+        token::Tilde => "~".to_string(),
+        token::OrOr => "||".to_string(),
+        token::AndAnd => "&&".to_string(),
+        token::BinOp(op) => binop_to_string(op).to_string(),
+        token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
+
+        /* Structural symbols */
+        token::At => "@".to_string(),
+        token::Dot => ".".to_string(),
+        token::DotDot => "..".to_string(),
+        token::DotDotDot => "...".to_string(),
+        token::DotDotEq => "..=".to_string(),
+        token::Comma => ",".to_string(),
+        token::Semi => ";".to_string(),
+        token::Colon => ":".to_string(),
+        token::ModSep => "::".to_string(),
+        token::RArrow => "->".to_string(),
+        token::LArrow => "<-".to_string(),
+        token::FatArrow => "=>".to_string(),
+        token::OpenDelim(token::Paren) => "(".to_string(),
+        token::CloseDelim(token::Paren) => ")".to_string(),
+        token::OpenDelim(token::Bracket) => "[".to_string(),
+        token::CloseDelim(token::Bracket) => "]".to_string(),
+        token::OpenDelim(token::Brace) => "{".to_string(),
+        token::CloseDelim(token::Brace) => "}".to_string(),
+        token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => " ".to_string(),
+        token::Pound => "#".to_string(),
+        token::Dollar => "$".to_string(),
+        token::Question => "?".to_string(),
+        token::SingleQuote => "'".to_string(),
+
+        /* Literals */
+        token::Literal(lit) => literal_to_string(lit),
+
+        /* Name components */
+        token::Ident(s, is_raw) => ident_to_string(s, is_raw, convert_dollar_crate),
+        token::Lifetime(s) => s.to_string(),
+
+        /* Other */
+        token::DocComment(s) => s.to_string(),
+        token::Eof => "<eof>".to_string(),
+        token::Whitespace => " ".to_string(),
+        token::Comment => "/* */".to_string(),
+        token::Shebang(s) => format!("/* shebang: {}*/", s),
+        token::Unknown(s) => s.to_string(),
+
+        token::Interpolated(ref nt) => nonterminal_to_string(nt),
+    }
+}
+
+/// Print the token precisely, without converting `$crate` into its respective crate name.
+pub fn token_to_string(token: &Token) -> String {
+    token_to_string_ext(token, false)
+}
+
+fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
+    let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
+    token_kind_to_string_ext(&token.kind, convert_dollar_crate)
+}
+
+pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
+    match *nt {
+        token::NtExpr(ref e) => expr_to_string(e),
+        token::NtMeta(ref e) => attr_item_to_string(e),
+        token::NtTy(ref e) => ty_to_string(e),
+        token::NtPath(ref e) => path_to_string(e),
+        token::NtItem(ref e) => item_to_string(e),
+        token::NtBlock(ref e) => block_to_string(e),
+        token::NtStmt(ref e) => stmt_to_string(e),
+        token::NtPat(ref e) => pat_to_string(e),
+        token::NtIdent(e, is_raw) => ast_ident_to_string(e, is_raw),
+        token::NtLifetime(e) => e.to_string(),
+        token::NtLiteral(ref e) => expr_to_string(e),
+        token::NtTT(ref tree) => tt_to_string(tree.clone()),
+        // FIXME(Centril): merge these variants.
+        token::NtImplItem(ref e) | token::NtTraitItem(ref e) => assoc_item_to_string(e),
+        token::NtVis(ref e) => vis_to_string(e),
+        token::NtForeignItem(ref e) => foreign_item_to_string(e),
+    }
+}
+
+pub fn ty_to_string(ty: &ast::Ty) -> String {
+    to_string(|s| s.print_type(ty))
+}
+
+pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
+    to_string(|s| s.print_type_bounds("", bounds))
+}
+
+pub fn pat_to_string(pat: &ast::Pat) -> String {
+    to_string(|s| s.print_pat(pat))
+}
+
+pub fn expr_to_string(e: &ast::Expr) -> String {
+    to_string(|s| s.print_expr(e))
+}
+
+pub fn tt_to_string(tt: tokenstream::TokenTree) -> String {
+    to_string(|s| s.print_tt(tt, false))
+}
+
+pub fn tts_to_string(tokens: TokenStream) -> String {
+    to_string(|s| s.print_tts(tokens, false))
+}
+
+pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
+    to_string(|s| s.print_stmt(stmt))
+}
+
+pub fn item_to_string(i: &ast::Item) -> String {
+    to_string(|s| s.print_item(i))
+}
+
+fn assoc_item_to_string(i: &ast::AssocItem) -> String {
+    to_string(|s| s.print_assoc_item(i))
+}
+
+pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
+    to_string(|s| s.print_generic_params(generic_params))
+}
+
+pub fn path_to_string(p: &ast::Path) -> String {
+    to_string(|s| s.print_path(p, false, 0))
+}
+
+pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
+    to_string(|s| s.print_path_segment(p, false))
+}
+
+pub fn vis_to_string(v: &ast::Visibility) -> String {
+    to_string(|s| s.print_visibility(v))
+}
+
+fn block_to_string(blk: &ast::Block) -> String {
+    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)
+    })
+}
+
+pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
+    to_string(|s| s.print_meta_list_item(li))
+}
+
+fn attr_item_to_string(ai: &ast::AttrItem) -> String {
+    to_string(|s| s.print_attr_item(ai, ai.path.span))
+}
+
+pub fn attribute_to_string(attr: &ast::Attribute) -> String {
+    to_string(|s| s.print_attribute(attr))
+}
+
+pub fn param_to_string(arg: &ast::Param) -> String {
+    to_string(|s| s.print_param(arg, false))
+}
+
+fn foreign_item_to_string(arg: &ast::ForeignItem) -> String {
+    to_string(|s| s.print_foreign_item(arg))
+}
+
+fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
+    format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
+}
+
+impl std::ops::Deref for State<'_> {
+    type Target = pp::Printer;
+    fn deref(&self) -> &Self::Target {
+        &self.s
+    }
+}
+
+impl std::ops::DerefMut for State<'_> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.s
+    }
+}
+
+pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
+    fn comments(&mut self) -> &mut Option<Comments<'a>>;
+    fn print_ident(&mut self, ident: ast::Ident);
+    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
+
+    fn strsep<T, F>(
+        &mut self,
+        sep: &'static str,
+        space_before: bool,
+        b: Breaks,
+        elts: &[T],
+        mut op: F,
+    ) where
+        F: FnMut(&mut Self, &T),
+    {
+        self.rbox(0, b);
+        if let Some((first, rest)) = elts.split_first() {
+            op(self, first);
+            for elt in rest {
+                if space_before {
+                    self.space();
+                }
+                self.word_space(sep);
+                op(self, elt);
+            }
+        }
+        self.end();
+    }
+
+    fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
+    where
+        F: FnMut(&mut Self, &T),
+    {
+        self.strsep(",", false, b, elts, op)
+    }
+
+    fn maybe_print_comment(&mut self, pos: BytePos) {
+        while let Some(ref cmnt) = self.next_comment() {
+            if cmnt.pos < pos {
+                self.print_comment(cmnt);
+            } else {
+                break;
+            }
+        }
+    }
+
+    fn print_comment(&mut self, cmnt: &comments::Comment) {
+        match cmnt.style {
+            comments::Mixed => {
+                assert_eq!(cmnt.lines.len(), 1);
+                self.zerobreak();
+                self.word(cmnt.lines[0].clone());
+                self.zerobreak()
+            }
+            comments::Isolated => {
+                self.hardbreak_if_not_bol();
+                for line in &cmnt.lines {
+                    // Don't print empty lines because they will end up as trailing
+                    // whitespace.
+                    if !line.is_empty() {
+                        self.word(line.clone());
+                    }
+                    self.hardbreak();
+                }
+            }
+            comments::Trailing => {
+                if !self.is_beginning_of_line() {
+                    self.word(" ");
+                }
+                if cmnt.lines.len() == 1 {
+                    self.word(cmnt.lines[0].clone());
+                    self.hardbreak()
+                } else {
+                    self.ibox(0);
+                    for line in &cmnt.lines {
+                        if !line.is_empty() {
+                            self.word(line.clone());
+                        }
+                        self.hardbreak();
+                    }
+                    self.end();
+                }
+            }
+            comments::BlankLine => {
+                // We need to do at least one, possibly two hardbreaks.
+                let twice = match self.last_token() {
+                    pp::Token::String(s) => ";" == s,
+                    pp::Token::Begin(_) => true,
+                    pp::Token::End => true,
+                    _ => false,
+                };
+                if twice {
+                    self.hardbreak();
+                }
+                self.hardbreak();
+            }
+        }
+        if let Some(cm) = self.comments() {
+            cm.current += 1;
+        }
+    }
+
+    fn next_comment(&mut self) -> Option<comments::Comment> {
+        self.comments().as_mut().and_then(|c| c.next())
+    }
+
+    fn print_literal(&mut self, lit: &ast::Lit) {
+        self.maybe_print_comment(lit.span.lo());
+        self.word(lit.token.to_string())
+    }
+
+    fn print_string(&mut self, st: &str, style: ast::StrStyle) {
+        let st = match style {
+            ast::StrStyle::Cooked => (format!("\"{}\"", st.escape_debug())),
+            ast::StrStyle::Raw(n) => {
+                format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
+            }
+        };
+        self.word(st)
+    }
+
+    fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
+    }
+
+    fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
+    }
+
+    fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) {
+        self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
+    }
+
+    fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
+    }
+
+    fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
+        self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
+    }
+
+    fn print_either_attributes(
+        &mut self,
+        attrs: &[ast::Attribute],
+        kind: ast::AttrStyle,
+        is_inline: bool,
+        trailing_hardbreak: bool,
+    ) {
+        let mut count = 0;
+        for attr in attrs {
+            if attr.style == kind {
+                self.print_attribute_inline(attr, is_inline);
+                if is_inline {
+                    self.nbsp();
+                }
+                count += 1;
+            }
+        }
+        if count > 0 && trailing_hardbreak && !is_inline {
+            self.hardbreak_if_not_bol();
+        }
+    }
+
+    fn print_attribute(&mut self, attr: &ast::Attribute) {
+        self.print_attribute_inline(attr, false)
+    }
+
+    fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
+        if !is_inline {
+            self.hardbreak_if_not_bol();
+        }
+        self.maybe_print_comment(attr.span.lo());
+        match attr.kind {
+            ast::AttrKind::Normal(ref item) => {
+                match attr.style {
+                    ast::AttrStyle::Inner => self.word("#!["),
+                    ast::AttrStyle::Outer => self.word("#["),
+                }
+                self.print_attr_item(&item, attr.span);
+                self.word("]");
+            }
+            ast::AttrKind::DocComment(comment) => {
+                self.word(comment.to_string());
+                self.hardbreak()
+            }
+        }
+    }
+
+    fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
+        self.ibox(0);
+        match &item.args {
+            MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+                Some(MacHeader::Path(&item.path)),
+                false,
+                None,
+                delim.to_token(),
+                tokens.clone(),
+                true,
+                span,
+            ),
+            MacArgs::Empty | MacArgs::Eq(..) => {
+                self.print_path(&item.path, false, 0);
+                if let MacArgs::Eq(_, tokens) = &item.args {
+                    self.space();
+                    self.word_space("=");
+                    self.print_tts(tokens.clone(), true);
+                }
+            }
+        }
+        self.end();
+    }
+
+    fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
+        match item {
+            ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
+            ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
+        }
+    }
+
+    fn print_meta_item(&mut self, item: &ast::MetaItem) {
+        self.ibox(INDENT_UNIT);
+        match item.kind {
+            ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
+            ast::MetaItemKind::NameValue(ref value) => {
+                self.print_path(&item.path, false, 0);
+                self.space();
+                self.word_space("=");
+                self.print_literal(value);
+            }
+            ast::MetaItemKind::List(ref items) => {
+                self.print_path(&item.path, false, 0);
+                self.popen();
+                self.commasep(Consistent, &items[..], |s, i| s.print_meta_list_item(i));
+                self.pclose();
+            }
+        }
+        self.end();
+    }
+
+    /// This doesn't deserve to be called "pretty" printing, but it should be
+    /// meaning-preserving. A quick hack that might help would be to look at the
+    /// spans embedded in the TTs to decide where to put spaces and newlines.
+    /// But it'd be better to parse these according to the grammar of the
+    /// appropriate macro, transcribe back into the grammar we just parsed from,
+    /// and then pretty-print the resulting AST nodes (so, e.g., we print
+    /// expression arguments as expressions). It can be done! I think.
+    fn print_tt(&mut self, tt: tokenstream::TokenTree, convert_dollar_crate: bool) {
+        match tt {
+            TokenTree::Token(ref token) => {
+                self.word(token_to_string_ext(&token, convert_dollar_crate));
+                match token.kind {
+                    token::DocComment(..) => self.hardbreak(),
+                    _ => {}
+                }
+            }
+            TokenTree::Delimited(dspan, delim, tts) => {
+                self.print_mac_common(
+                    None,
+                    false,
+                    None,
+                    delim,
+                    tts,
+                    convert_dollar_crate,
+                    dspan.entire(),
+                );
+            }
+        }
+    }
+
+    fn print_tts(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) {
+        for (i, tt) in tts.into_trees().enumerate() {
+            if i != 0 && tt_prepend_space(&tt) {
+                self.space();
+            }
+            self.print_tt(tt, convert_dollar_crate);
+        }
+    }
+
+    fn print_mac_common(
+        &mut self,
+        header: Option<MacHeader<'_>>,
+        has_bang: bool,
+        ident: Option<ast::Ident>,
+        delim: DelimToken,
+        tts: TokenStream,
+        convert_dollar_crate: bool,
+        span: Span,
+    ) {
+        if delim == DelimToken::Brace {
+            self.cbox(INDENT_UNIT);
+        }
+        match header {
+            Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
+            Some(MacHeader::Keyword(kw)) => self.word(kw),
+            None => {}
+        }
+        if has_bang {
+            self.word("!");
+        }
+        if let Some(ident) = ident {
+            self.nbsp();
+            self.print_ident(ident);
+        }
+        match delim {
+            DelimToken::Brace => {
+                if header.is_some() || has_bang || ident.is_some() {
+                    self.nbsp();
+                }
+                self.word("{");
+                if !tts.is_empty() {
+                    self.space();
+                }
+            }
+            _ => self.word(token_kind_to_string(&token::OpenDelim(delim))),
+        }
+        self.ibox(0);
+        self.print_tts(tts, convert_dollar_crate);
+        self.end();
+        match delim {
+            DelimToken::Brace => self.bclose(span),
+            _ => self.word(token_kind_to_string(&token::CloseDelim(delim))),
+        }
+    }
+
+    fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
+        self.maybe_print_comment(path.span.lo());
+
+        for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
+            if i > 0 {
+                self.word("::")
+            }
+            self.print_path_segment(segment, colons_before_params);
+        }
+    }
+
+    fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
+        if segment.ident.name != kw::PathRoot {
+            self.print_ident(segment.ident);
+            if let Some(ref args) = segment.args {
+                self.print_generic_args(args, colons_before_params);
+            }
+        }
+    }
+
+    fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+        let w = w.into();
+        // Outer-box is consistent.
+        self.cbox(INDENT_UNIT);
+        // Head-box is inconsistent.
+        self.ibox(w.len() + 1);
+        // Keyword that starts the head.
+        if !w.is_empty() {
+            self.word_nbsp(w);
+        }
+    }
+
+    fn bopen(&mut self) {
+        self.word("{");
+        self.end(); // Close the head-box.
+    }
+
+    fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
+        self.maybe_print_comment(span.hi());
+        self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+        self.word("}");
+        if close_box {
+            self.end(); // Close the outer-box.
+        }
+    }
+
+    fn bclose(&mut self, span: rustc_span::Span) {
+        self.bclose_maybe_open(span, true)
+    }
+
+    fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
+        if !self.is_beginning_of_line() {
+            self.break_offset(n, off)
+        } else {
+            if off != 0 && self.last_token().is_hardbreak_tok() {
+                // We do something pretty sketchy here: tuck the nonzero
+                // offset-adjustment we were going to deposit along with the
+                // break into the previous hardbreak.
+                self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
+            }
+        }
+    }
+}
+
+impl<'a> PrintState<'a> for State<'a> {
+    fn comments(&mut self) -> &mut Option<Comments<'a>> {
+        &mut self.comments
+    }
+
+    fn print_ident(&mut self, ident: ast::Ident) {
+        self.s.word(ast_ident_to_string(ident, ident.is_raw_guess()));
+        self.ann.post(self, AnnNode::Ident(&ident))
+    }
+
+    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
+        if colons_before_params {
+            self.s.word("::")
+        }
+
+        match *args {
+            ast::GenericArgs::AngleBracketed(ref data) => {
+                self.s.word("<");
+
+                self.commasep(Inconsistent, &data.args, |s, generic_arg| {
+                    s.print_generic_arg(generic_arg)
+                });
+
+                let mut comma = data.args.len() != 0;
+
+                for constraint in data.constraints.iter() {
+                    if comma {
+                        self.word_space(",")
+                    }
+                    self.print_ident(constraint.ident);
+                    self.s.space();
+                    match constraint.kind {
+                        ast::AssocTyConstraintKind::Equality { ref ty } => {
+                            self.word_space("=");
+                            self.print_type(ty);
+                        }
+                        ast::AssocTyConstraintKind::Bound { ref bounds } => {
+                            self.print_type_bounds(":", &*bounds);
+                        }
+                    }
+                    comma = true;
+                }
+
+                self.s.word(">")
+            }
+
+            ast::GenericArgs::Parenthesized(ref data) => {
+                self.s.word("(");
+                self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
+                self.s.word(")");
+                self.print_fn_ret_ty(&data.output);
+            }
+        }
+    }
+}
+
+impl<'a> State<'a> {
+    // Synthesizes a comment that was not textually present in the original source
+    // file.
+    pub fn synth_comment(&mut self, text: String) {
+        self.s.word("/*");
+        self.s.space();
+        self.s.word(text);
+        self.s.space();
+        self.s.word("*/")
+    }
+
+    crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
+    where
+        F: FnMut(&mut State<'_>, &T),
+        G: FnMut(&T) -> rustc_span::Span,
+    {
+        self.rbox(0, b);
+        let len = elts.len();
+        let mut i = 0;
+        for elt in elts {
+            self.maybe_print_comment(get_span(elt).hi());
+            op(self, elt);
+            i += 1;
+            if i < len {
+                self.s.word(",");
+                self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
+                self.space_if_not_bol();
+            }
+        }
+        self.end();
+    }
+
+    crate fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
+        self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
+    }
+
+    pub fn print_mod(&mut self, _mod: &ast::Mod, attrs: &[ast::Attribute]) {
+        self.print_inner_attributes(attrs);
+        for item in &_mod.items {
+            self.print_item(item);
+        }
+    }
+
+    crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) {
+        self.print_inner_attributes(attrs);
+        for item in &nmod.items {
+            self.print_foreign_item(item);
+        }
+    }
+
+    pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
+        if let Some(lt) = *lifetime {
+            self.print_lifetime(lt);
+            self.nbsp();
+        }
+    }
+
+    crate fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
+        match generic_arg {
+            GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
+            GenericArg::Type(ty) => self.print_type(ty),
+            GenericArg::Const(ct) => self.print_expr(&ct.value),
+        }
+    }
+
+    pub fn print_type(&mut self, ty: &ast::Ty) {
+        self.maybe_print_comment(ty.span.lo());
+        self.ibox(0);
+        match ty.kind {
+            ast::TyKind::Slice(ref ty) => {
+                self.s.word("[");
+                self.print_type(ty);
+                self.s.word("]");
+            }
+            ast::TyKind::Ptr(ref mt) => {
+                self.s.word("*");
+                self.print_mt(mt, true);
+            }
+            ast::TyKind::Rptr(ref lifetime, ref mt) => {
+                self.s.word("&");
+                self.print_opt_lifetime(lifetime);
+                self.print_mt(mt, false);
+            }
+            ast::TyKind::Never => {
+                self.s.word("!");
+            }
+            ast::TyKind::Tup(ref elts) => {
+                self.popen();
+                self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(ty));
+                if elts.len() == 1 {
+                    self.s.word(",");
+                }
+                self.pclose();
+            }
+            ast::TyKind::Paren(ref typ) => {
+                self.popen();
+                self.print_type(typ);
+                self.pclose();
+            }
+            ast::TyKind::BareFn(ref f) => {
+                self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
+            }
+            ast::TyKind::Path(None, ref path) => {
+                self.print_path(path, false, 0);
+            }
+            ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
+            ast::TyKind::TraitObject(ref bounds, syntax) => {
+                let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn" } else { "" };
+                self.print_type_bounds(prefix, &bounds[..]);
+            }
+            ast::TyKind::ImplTrait(_, ref bounds) => {
+                self.print_type_bounds("impl", &bounds[..]);
+            }
+            ast::TyKind::Array(ref ty, ref length) => {
+                self.s.word("[");
+                self.print_type(ty);
+                self.s.word("; ");
+                self.print_expr(&length.value);
+                self.s.word("]");
+            }
+            ast::TyKind::Typeof(ref e) => {
+                self.s.word("typeof(");
+                self.print_expr(&e.value);
+                self.s.word(")");
+            }
+            ast::TyKind::Infer => {
+                self.s.word("_");
+            }
+            ast::TyKind::Err => {
+                self.popen();
+                self.s.word("/*ERROR*/");
+                self.pclose();
+            }
+            ast::TyKind::ImplicitSelf => {
+                self.s.word("Self");
+            }
+            ast::TyKind::Mac(ref m) => {
+                self.print_mac(m);
+            }
+            ast::TyKind::CVarArgs => {
+                self.s.word("...");
+            }
+        }
+        self.end();
+    }
+
+    crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
+        self.hardbreak_if_not_bol();
+        self.maybe_print_comment(item.span.lo());
+        self.print_outer_attributes(&item.attrs);
+        match item.kind {
+            ast::ForeignItemKind::Fn(ref decl, ref generics) => {
+                self.head("");
+                self.print_fn(
+                    decl,
+                    ast::FnHeader::default(),
+                    Some(item.ident),
+                    generics,
+                    &item.vis,
+                );
+                self.end(); // end head-ibox
+                self.s.word(";");
+                self.end(); // end the outer fn box
+            }
+            ast::ForeignItemKind::Static(ref t, m) => {
+                self.head(visibility_qualified(&item.vis, "static"));
+                if m == ast::Mutability::Mut {
+                    self.word_space("mut");
+                }
+                self.print_ident(item.ident);
+                self.word_space(":");
+                self.print_type(t);
+                self.s.word(";");
+                self.end(); // end the head-ibox
+                self.end(); // end the outer cbox
+            }
+            ast::ForeignItemKind::Ty => {
+                self.head(visibility_qualified(&item.vis, "type"));
+                self.print_ident(item.ident);
+                self.s.word(";");
+                self.end(); // end the head-ibox
+                self.end(); // end the outer cbox
+            }
+            ast::ForeignItemKind::Macro(ref m) => {
+                self.print_mac(m);
+                if m.args.need_semicolon() {
+                    self.s.word(";");
+                }
+            }
+        }
+    }
+
+    fn print_associated_const(
+        &mut self,
+        ident: ast::Ident,
+        ty: &ast::Ty,
+        default: Option<&ast::Expr>,
+        vis: &ast::Visibility,
+    ) {
+        self.s.word(visibility_qualified(vis, ""));
+        self.word_space("const");
+        self.print_ident(ident);
+        self.word_space(":");
+        self.print_type(ty);
+        if let Some(expr) = default {
+            self.s.space();
+            self.word_space("=");
+            self.print_expr(expr);
+        }
+        self.s.word(";")
+    }
+
+    fn print_associated_type(
+        &mut self,
+        ident: ast::Ident,
+        bounds: &ast::GenericBounds,
+        ty: Option<&ast::Ty>,
+    ) {
+        self.word_space("type");
+        self.print_ident(ident);
+        self.print_type_bounds(":", bounds);
+        if let Some(ty) = ty {
+            self.s.space();
+            self.word_space("=");
+            self.print_type(ty);
+        }
+        self.s.word(";")
+    }
+
+    /// Pretty-prints an item.
+    crate fn print_item(&mut self, item: &ast::Item) {
+        self.hardbreak_if_not_bol();
+        self.maybe_print_comment(item.span.lo());
+        self.print_outer_attributes(&item.attrs);
+        self.ann.pre(self, AnnNode::Item(item));
+        match item.kind {
+            ast::ItemKind::ExternCrate(orig_name) => {
+                self.head(visibility_qualified(&item.vis, "extern crate"));
+                if let Some(orig_name) = orig_name {
+                    self.print_name(orig_name);
+                    self.s.space();
+                    self.s.word("as");
+                    self.s.space();
+                }
+                self.print_ident(item.ident);
+                self.s.word(";");
+                self.end(); // end inner head-block
+                self.end(); // end outer head-block
+            }
+            ast::ItemKind::Use(ref tree) => {
+                self.head(visibility_qualified(&item.vis, "use"));
+                self.print_use_tree(tree);
+                self.s.word(";");
+                self.end(); // end inner head-block
+                self.end(); // end outer head-block
+            }
+            ast::ItemKind::Static(ref ty, m, ref expr) => {
+                self.head(visibility_qualified(&item.vis, "static"));
+                if m == ast::Mutability::Mut {
+                    self.word_space("mut");
+                }
+                self.print_ident(item.ident);
+                self.word_space(":");
+                self.print_type(ty);
+                self.s.space();
+                self.end(); // end the head-ibox
+
+                self.word_space("=");
+                self.print_expr(expr);
+                self.s.word(";");
+                self.end(); // end the outer cbox
+            }
+            ast::ItemKind::Const(ref ty, ref expr) => {
+                self.head(visibility_qualified(&item.vis, "const"));
+                self.print_ident(item.ident);
+                self.word_space(":");
+                self.print_type(ty);
+                self.s.space();
+                self.end(); // end the head-ibox
+
+                self.word_space("=");
+                self.print_expr(expr);
+                self.s.word(";");
+                self.end(); // end the outer cbox
+            }
+            ast::ItemKind::Fn(ref sig, ref param_names, ref body) => {
+                self.head("");
+                self.print_fn(&sig.decl, sig.header, Some(item.ident), param_names, &item.vis);
+                self.s.word(" ");
+                self.print_block_with_attrs(body, &item.attrs);
+            }
+            ast::ItemKind::Mod(ref _mod) => {
+                self.head(visibility_qualified(&item.vis, "mod"));
+                self.print_ident(item.ident);
+
+                if _mod.inline || self.is_expanded {
+                    self.nbsp();
+                    self.bopen();
+                    self.print_mod(_mod, &item.attrs);
+                    self.bclose(item.span);
+                } else {
+                    self.s.word(";");
+                    self.end(); // end inner head-block
+                    self.end(); // end outer head-block
+                }
+            }
+            ast::ItemKind::ForeignMod(ref nmod) => {
+                self.head("extern");
+                if let Some(abi) = nmod.abi {
+                    self.print_literal(&abi.as_lit());
+                    self.nbsp();
+                }
+                self.bopen();
+                self.print_foreign_mod(nmod, &item.attrs);
+                self.bclose(item.span);
+            }
+            ast::ItemKind::GlobalAsm(ref ga) => {
+                self.head(visibility_qualified(&item.vis, "global_asm!"));
+                self.s.word(ga.asm.to_string());
+                self.end();
+            }
+            ast::ItemKind::TyAlias(ref ty, ref generics) => {
+                self.head(visibility_qualified(&item.vis, "type"));
+                self.print_ident(item.ident);
+                self.print_generic_params(&generics.params);
+                self.end(); // end the inner ibox
+
+                self.print_where_clause(&generics.where_clause);
+                self.s.space();
+                self.word_space("=");
+                self.print_type(ty);
+                self.s.word(";");
+                self.end(); // end the outer ibox
+            }
+            ast::ItemKind::Enum(ref enum_definition, ref params) => {
+                self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
+            }
+            ast::ItemKind::Struct(ref struct_def, ref generics) => {
+                self.head(visibility_qualified(&item.vis, "struct"));
+                self.print_struct(struct_def, generics, item.ident, item.span, true);
+            }
+            ast::ItemKind::Union(ref struct_def, ref generics) => {
+                self.head(visibility_qualified(&item.vis, "union"));
+                self.print_struct(struct_def, generics, item.ident, item.span, true);
+            }
+            ast::ItemKind::Impl {
+                unsafety,
+                polarity,
+                defaultness,
+                constness,
+                ref generics,
+                ref of_trait,
+                ref self_ty,
+                ref items,
+            } => {
+                self.head("");
+                self.print_visibility(&item.vis);
+                self.print_defaultness(defaultness);
+                self.print_unsafety(unsafety);
+                self.word_nbsp("impl");
+                self.print_constness(constness);
+
+                if !generics.params.is_empty() {
+                    self.print_generic_params(&generics.params);
+                    self.s.space();
+                }
+
+                if polarity == ast::ImplPolarity::Negative {
+                    self.s.word("!");
+                }
+
+                if let Some(ref t) = *of_trait {
+                    self.print_trait_ref(t);
+                    self.s.space();
+                    self.word_space("for");
+                }
+
+                self.print_type(self_ty);
+                self.print_where_clause(&generics.where_clause);
+
+                self.s.space();
+                self.bopen();
+                self.print_inner_attributes(&item.attrs);
+                for impl_item in items {
+                    self.print_assoc_item(impl_item);
+                }
+                self.bclose(item.span);
+            }
+            ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => {
+                self.head("");
+                self.print_visibility(&item.vis);
+                self.print_unsafety(unsafety);
+                self.print_is_auto(is_auto);
+                self.word_nbsp("trait");
+                self.print_ident(item.ident);
+                self.print_generic_params(&generics.params);
+                let mut real_bounds = Vec::with_capacity(bounds.len());
+                for b in bounds.iter() {
+                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+                        self.s.space();
+                        self.word_space("for ?");
+                        self.print_trait_ref(&ptr.trait_ref);
+                    } else {
+                        real_bounds.push(b.clone());
+                    }
+                }
+                self.print_type_bounds(":", &real_bounds[..]);
+                self.print_where_clause(&generics.where_clause);
+                self.s.word(" ");
+                self.bopen();
+                for trait_item in trait_items {
+                    self.print_assoc_item(trait_item);
+                }
+                self.bclose(item.span);
+            }
+            ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
+                self.head("");
+                self.print_visibility(&item.vis);
+                self.word_nbsp("trait");
+                self.print_ident(item.ident);
+                self.print_generic_params(&generics.params);
+                let mut real_bounds = Vec::with_capacity(bounds.len());
+                // FIXME(durka) this seems to be some quite outdated syntax
+                for b in bounds.iter() {
+                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+                        self.s.space();
+                        self.word_space("for ?");
+                        self.print_trait_ref(&ptr.trait_ref);
+                    } else {
+                        real_bounds.push(b.clone());
+                    }
+                }
+                self.nbsp();
+                self.print_type_bounds("=", &real_bounds[..]);
+                self.print_where_clause(&generics.where_clause);
+                self.s.word(";");
+            }
+            ast::ItemKind::Mac(ref mac) => {
+                self.print_mac(mac);
+                if mac.args.need_semicolon() {
+                    self.s.word(";");
+                }
+            }
+            ast::ItemKind::MacroDef(ref macro_def) => {
+                let (kw, has_bang) = if macro_def.legacy {
+                    ("macro_rules", true)
+                } else {
+                    self.print_visibility(&item.vis);
+                    ("macro", false)
+                };
+                self.print_mac_common(
+                    Some(MacHeader::Keyword(kw)),
+                    has_bang,
+                    Some(item.ident),
+                    macro_def.body.delim(),
+                    macro_def.body.inner_tokens(),
+                    true,
+                    item.span,
+                );
+            }
+        }
+        self.ann.post(self, AnnNode::Item(item))
+    }
+
+    fn print_trait_ref(&mut self, t: &ast::TraitRef) {
+        self.print_path(&t.path, false, 0)
+    }
+
+    fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+        if !generic_params.is_empty() {
+            self.s.word("for");
+            self.print_generic_params(generic_params);
+            self.nbsp();
+        }
+    }
+
+    fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
+        self.print_formal_generic_params(&t.bound_generic_params);
+        self.print_trait_ref(&t.trait_ref)
+    }
+
+    crate fn print_enum_def(
+        &mut self,
+        enum_definition: &ast::EnumDef,
+        generics: &ast::Generics,
+        ident: ast::Ident,
+        span: rustc_span::Span,
+        visibility: &ast::Visibility,
+    ) {
+        self.head(visibility_qualified(visibility, "enum"));
+        self.print_ident(ident);
+        self.print_generic_params(&generics.params);
+        self.print_where_clause(&generics.where_clause);
+        self.s.space();
+        self.print_variants(&enum_definition.variants, span)
+    }
+
+    crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
+        self.bopen();
+        for v in variants {
+            self.space_if_not_bol();
+            self.maybe_print_comment(v.span.lo());
+            self.print_outer_attributes(&v.attrs);
+            self.ibox(INDENT_UNIT);
+            self.print_variant(v);
+            self.s.word(",");
+            self.end();
+            self.maybe_print_trailing_comment(v.span, None);
+        }
+        self.bclose(span)
+    }
+
+    crate fn print_visibility(&mut self, vis: &ast::Visibility) {
+        match vis.node {
+            ast::VisibilityKind::Public => self.word_nbsp("pub"),
+            ast::VisibilityKind::Crate(sugar) => match sugar {
+                ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
+                ast::CrateSugar::JustCrate => self.word_nbsp("crate"),
+            },
+            ast::VisibilityKind::Restricted { ref path, .. } => {
+                let path = to_string(|s| s.print_path(path, false, 0));
+                if path == "self" || path == "super" {
+                    self.word_nbsp(format!("pub({})", path))
+                } else {
+                    self.word_nbsp(format!("pub(in {})", path))
+                }
+            }
+            ast::VisibilityKind::Inherited => {}
+        }
+    }
+
+    crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
+        if let ast::Defaultness::Default = defaultness {
+            self.word_nbsp("default");
+        }
+    }
+
+    crate fn print_struct(
+        &mut self,
+        struct_def: &ast::VariantData,
+        generics: &ast::Generics,
+        ident: ast::Ident,
+        span: rustc_span::Span,
+        print_finalizer: bool,
+    ) {
+        self.print_ident(ident);
+        self.print_generic_params(&generics.params);
+        match struct_def {
+            ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
+                if let ast::VariantData::Tuple(..) = struct_def {
+                    self.popen();
+                    self.commasep(Inconsistent, struct_def.fields(), |s, field| {
+                        s.maybe_print_comment(field.span.lo());
+                        s.print_outer_attributes(&field.attrs);
+                        s.print_visibility(&field.vis);
+                        s.print_type(&field.ty)
+                    });
+                    self.pclose();
+                }
+                self.print_where_clause(&generics.where_clause);
+                if print_finalizer {
+                    self.s.word(";");
+                }
+                self.end();
+                self.end(); // Close the outer-box.
+            }
+            ast::VariantData::Struct(..) => {
+                self.print_where_clause(&generics.where_clause);
+                self.nbsp();
+                self.bopen();
+                self.hardbreak_if_not_bol();
+
+                for field in struct_def.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.s.word(",");
+                }
+
+                self.bclose(span)
+            }
+        }
+    }
+
+    crate fn print_variant(&mut self, v: &ast::Variant) {
+        self.head("");
+        self.print_visibility(&v.vis);
+        let generics = ast::Generics::default();
+        self.print_struct(&v.data, &generics, v.ident, v.span, false);
+        match v.disr_expr {
+            Some(ref d) => {
+                self.s.space();
+                self.word_space("=");
+                self.print_expr(&d.value)
+            }
+            _ => {}
+        }
+    }
+
+    crate fn print_assoc_item(&mut self, item: &ast::AssocItem) {
+        self.ann.pre(self, AnnNode::SubItem(item.id));
+        self.hardbreak_if_not_bol();
+        self.maybe_print_comment(item.span.lo());
+        self.print_outer_attributes(&item.attrs);
+        self.print_defaultness(item.defaultness);
+        match &item.kind {
+            ast::AssocItemKind::Const(ty, expr) => {
+                self.print_associated_const(item.ident, ty, expr.as_deref(), &item.vis);
+            }
+            ast::AssocItemKind::Fn(sig, body) => {
+                if body.is_some() {
+                    self.head("");
+                }
+                self.print_fn(&sig.decl, sig.header, Some(item.ident), &item.generics, &item.vis);
+                if let Some(body) = body {
+                    self.nbsp();
+                    self.print_block_with_attrs(body, &item.attrs);
+                } else {
+                    self.s.word(";");
+                }
+            }
+            ast::AssocItemKind::TyAlias(bounds, ty) => {
+                self.print_associated_type(item.ident, bounds, ty.as_deref());
+            }
+            ast::AssocItemKind::Macro(mac) => {
+                self.print_mac(mac);
+                if mac.args.need_semicolon() {
+                    self.s.word(";");
+                }
+            }
+        }
+        self.ann.post(self, AnnNode::SubItem(item.id))
+    }
+
+    crate fn print_stmt(&mut self, st: &ast::Stmt) {
+        self.maybe_print_comment(st.span.lo());
+        match st.kind {
+            ast::StmtKind::Local(ref loc) => {
+                self.print_outer_attributes(&loc.attrs);
+                self.space_if_not_bol();
+                self.ibox(INDENT_UNIT);
+                self.word_nbsp("let");
+
+                self.ibox(INDENT_UNIT);
+                self.print_local_decl(loc);
+                self.end();
+                if let Some(ref init) = loc.init {
+                    self.nbsp();
+                    self.word_space("=");
+                    self.print_expr(init);
+                }
+                self.s.word(";");
+                self.end();
+            }
+            ast::StmtKind::Item(ref item) => self.print_item(item),
+            ast::StmtKind::Expr(ref expr) => {
+                self.space_if_not_bol();
+                self.print_expr_outer_attr_style(expr, false);
+                if classify::expr_requires_semi_to_be_stmt(expr) {
+                    self.s.word(";");
+                }
+            }
+            ast::StmtKind::Semi(ref expr) => {
+                match expr.kind {
+                    // Filter out empty `Tup` exprs created for the `redundant_semicolon`
+                    // lint, as they shouldn't be visible and interact poorly
+                    // with proc macros.
+                    ast::ExprKind::Tup(ref exprs) if exprs.is_empty() && expr.attrs.is_empty() => {
+                        ()
+                    }
+                    _ => {
+                        self.space_if_not_bol();
+                        self.print_expr_outer_attr_style(expr, false);
+                        self.s.word(";");
+                    }
+                }
+            }
+            ast::StmtKind::Mac(ref mac) => {
+                let (ref mac, style, ref attrs) = **mac;
+                self.space_if_not_bol();
+                self.print_outer_attributes(attrs);
+                self.print_mac(mac);
+                if style == ast::MacStmtStyle::Semicolon {
+                    self.s.word(";");
+                }
+            }
+        }
+        self.maybe_print_trailing_comment(st.span, None)
+    }
+
+    crate fn print_block(&mut self, blk: &ast::Block) {
+        self.print_block_with_attrs(blk, &[])
+    }
+
+    crate fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
+        self.print_block_maybe_unclosed(blk, &[], false)
+    }
+
+    crate fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
+        self.print_block_maybe_unclosed(blk, attrs, true)
+    }
+
+    crate fn print_block_maybe_unclosed(
+        &mut self,
+        blk: &ast::Block,
+        attrs: &[ast::Attribute],
+        close_box: bool,
+    ) {
+        match blk.rules {
+            BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
+            BlockCheckMode::Default => (),
+        }
+        self.maybe_print_comment(blk.span.lo());
+        self.ann.pre(self, AnnNode::Block(blk));
+        self.bopen();
+
+        self.print_inner_attributes(attrs);
+
+        for (i, st) in blk.stmts.iter().enumerate() {
+            match st.kind {
+                ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+                    self.maybe_print_comment(st.span.lo());
+                    self.space_if_not_bol();
+                    self.print_expr_outer_attr_style(expr, false);
+                    self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
+                }
+                _ => self.print_stmt(st),
+            }
+        }
+
+        self.bclose_maybe_open(blk.span, close_box);
+        self.ann.post(self, AnnNode::Block(blk))
+    }
+
+    /// Print a `let pat = scrutinee` expression.
+    crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
+        self.s.word("let ");
+
+        self.print_pat(pat);
+        self.s.space();
+
+        self.word_space("=");
+        self.print_expr_cond_paren(
+            scrutinee,
+            Self::cond_needs_par(scrutinee)
+                || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()),
+        )
+    }
+
+    fn print_else(&mut self, els: Option<&ast::Expr>) {
+        if let Some(_else) = els {
+            match _else.kind {
+                // Another `else if` block.
+                ast::ExprKind::If(ref i, ref then, ref e) => {
+                    self.cbox(INDENT_UNIT - 1);
+                    self.ibox(0);
+                    self.s.word(" else if ");
+                    self.print_expr_as_cond(i);
+                    self.s.space();
+                    self.print_block(then);
+                    self.print_else(e.as_deref())
+                }
+                // Final `else` block.
+                ast::ExprKind::Block(ref b, _) => {
+                    self.cbox(INDENT_UNIT - 1);
+                    self.ibox(0);
+                    self.s.word(" else ");
+                    self.print_block(b)
+                }
+                // Constraints would be great here!
+                _ => {
+                    panic!("print_if saw if with weird alternative");
+                }
+            }
+        }
+    }
+
+    crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
+        self.head("if");
+
+        self.print_expr_as_cond(test);
+        self.s.space();
+
+        self.print_block(blk);
+        self.print_else(elseopt)
+    }
+
+    crate fn print_mac(&mut self, m: &ast::Mac) {
+        self.print_mac_common(
+            Some(MacHeader::Path(&m.path)),
+            true,
+            None,
+            m.args.delim(),
+            m.args.inner_tokens(),
+            true,
+            m.span(),
+        );
+    }
+
+    fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
+        self.popen();
+        self.commasep_exprs(Inconsistent, args);
+        self.pclose()
+    }
+
+    crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
+        self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
+    }
+
+    /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
+    /// `if cond { ... }`.
+    crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
+        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
+    }
+
+    /// Does `expr` need parenthesis when printed in a condition position?
+    fn cond_needs_par(expr: &ast::Expr) -> bool {
+        match expr.kind {
+            // These cases need parens due to the parse error observed in #26461: `if return {}`
+            // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
+            ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Break(..) => true,
+
+            _ => parser::contains_exterior_struct_lit(expr),
+        }
+    }
+
+    /// Prints `expr` or `(expr)` when `needs_par` holds.
+    fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
+        if needs_par {
+            self.popen();
+        }
+        self.print_expr(expr);
+        if needs_par {
+            self.pclose();
+        }
+    }
+
+    fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>], attrs: &[Attribute]) {
+        self.ibox(INDENT_UNIT);
+        self.s.word("[");
+        self.print_inner_attributes_inline(attrs);
+        self.commasep_exprs(Inconsistent, &exprs[..]);
+        self.s.word("]");
+        self.end();
+    }
+
+    fn print_expr_repeat(
+        &mut self,
+        element: &ast::Expr,
+        count: &ast::AnonConst,
+        attrs: &[Attribute],
+    ) {
+        self.ibox(INDENT_UNIT);
+        self.s.word("[");
+        self.print_inner_attributes_inline(attrs);
+        self.print_expr(element);
+        self.word_space(";");
+        self.print_expr(&count.value);
+        self.s.word("]");
+        self.end();
+    }
+
+    fn print_expr_struct(
+        &mut self,
+        path: &ast::Path,
+        fields: &[ast::Field],
+        wth: &Option<P<ast::Expr>>,
+        attrs: &[Attribute],
+    ) {
+        self.print_path(path, true, 0);
+        self.s.word("{");
+        self.print_inner_attributes_inline(attrs);
+        self.commasep_cmnt(
+            Consistent,
+            &fields[..],
+            |s, field| {
+                s.ibox(INDENT_UNIT);
+                if !field.is_shorthand {
+                    s.print_ident(field.ident);
+                    s.word_space(":");
+                }
+                s.print_expr(&field.expr);
+                s.end();
+            },
+            |f| f.span,
+        );
+        match *wth {
+            Some(ref expr) => {
+                self.ibox(INDENT_UNIT);
+                if !fields.is_empty() {
+                    self.s.word(",");
+                    self.s.space();
+                }
+                self.s.word("..");
+                self.print_expr(expr);
+                self.end();
+            }
+            _ => {
+                if !fields.is_empty() {
+                    self.s.word(",")
+                }
+            }
+        }
+        self.s.word("}");
+    }
+
+    fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>], attrs: &[Attribute]) {
+        self.popen();
+        self.print_inner_attributes_inline(attrs);
+        self.commasep_exprs(Inconsistent, &exprs[..]);
+        if exprs.len() == 1 {
+            self.s.word(",");
+        }
+        self.pclose()
+    }
+
+    fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
+        let prec = match func.kind {
+            ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
+            _ => parser::PREC_POSTFIX,
+        };
+
+        self.print_expr_maybe_paren(func, prec);
+        self.print_call_post(args)
+    }
+
+    fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P<ast::Expr>]) {
+        let base_args = &args[1..];
+        self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
+        self.s.word(".");
+        self.print_ident(segment.ident);
+        if let Some(ref args) = segment.args {
+            self.print_generic_args(args, true);
+        }
+        self.print_call_post(base_args)
+    }
+
+    fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
+        let assoc_op = AssocOp::from_ast_binop(op.node);
+        let prec = assoc_op.precedence() as i8;
+        let fixity = assoc_op.fixity();
+
+        let (left_prec, right_prec) = match fixity {
+            Fixity::Left => (prec, prec + 1),
+            Fixity::Right => (prec + 1, prec),
+            Fixity::None => (prec + 1, prec + 1),
+        };
+
+        let left_prec = match (&lhs.kind, op.node) {
+            // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
+            // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
+            // of `(x as i32) < ...`. We need to convince it _not_ to do that.
+            (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt)
+            | (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN,
+            // We are given `(let _ = a) OP b`.
+            //
+            // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
+            //   as the parser will interpret this as `(let _ = a) OP b`.
+            //
+            // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
+            //   parens are required since the parser would interpret `let a = b < c` as
+            //   `let a = (b < c)`. To achieve this, we force parens.
+            (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
+                parser::PREC_FORCE_PAREN
+            }
+            _ => left_prec,
+        };
+
+        self.print_expr_maybe_paren(lhs, left_prec);
+        self.s.space();
+        self.word_space(op.node.to_string());
+        self.print_expr_maybe_paren(rhs, right_prec)
+    }
+
+    fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
+        self.s.word(ast::UnOp::to_string(op));
+        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+    }
+
+    fn print_expr_addr_of(
+        &mut self,
+        kind: ast::BorrowKind,
+        mutability: ast::Mutability,
+        expr: &ast::Expr,
+    ) {
+        self.s.word("&");
+        match kind {
+            ast::BorrowKind::Ref => self.print_mutability(mutability, false),
+            ast::BorrowKind::Raw => {
+                self.word_nbsp("raw");
+                self.print_mutability(mutability, true);
+            }
+        }
+        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+    }
+
+    pub fn print_expr(&mut self, expr: &ast::Expr) {
+        self.print_expr_outer_attr_style(expr, true)
+    }
+
+    fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
+        self.maybe_print_comment(expr.span.lo());
+
+        let attrs = &expr.attrs;
+        if is_inline {
+            self.print_outer_attributes_inline(attrs);
+        } else {
+            self.print_outer_attributes(attrs);
+        }
+
+        self.ibox(INDENT_UNIT);
+        self.ann.pre(self, AnnNode::Expr(expr));
+        match expr.kind {
+            ast::ExprKind::Box(ref expr) => {
+                self.word_space("box");
+                self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
+            }
+            ast::ExprKind::Array(ref exprs) => {
+                self.print_expr_vec(&exprs[..], attrs);
+            }
+            ast::ExprKind::Repeat(ref element, ref count) => {
+                self.print_expr_repeat(element, count, attrs);
+            }
+            ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
+                self.print_expr_struct(path, &fields[..], wth, attrs);
+            }
+            ast::ExprKind::Tup(ref exprs) => {
+                self.print_expr_tup(&exprs[..], attrs);
+            }
+            ast::ExprKind::Call(ref func, ref args) => {
+                self.print_expr_call(func, &args[..]);
+            }
+            ast::ExprKind::MethodCall(ref segment, ref args) => {
+                self.print_expr_method_call(segment, &args[..]);
+            }
+            ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
+                self.print_expr_binary(op, lhs, rhs);
+            }
+            ast::ExprKind::Unary(op, ref expr) => {
+                self.print_expr_unary(op, expr);
+            }
+            ast::ExprKind::AddrOf(k, m, ref expr) => {
+                self.print_expr_addr_of(k, m, expr);
+            }
+            ast::ExprKind::Lit(ref lit) => {
+                self.print_literal(lit);
+            }
+            ast::ExprKind::Cast(ref expr, ref ty) => {
+                let prec = AssocOp::As.precedence() as i8;
+                self.print_expr_maybe_paren(expr, prec);
+                self.s.space();
+                self.word_space("as");
+                self.print_type(ty);
+            }
+            ast::ExprKind::Type(ref expr, ref ty) => {
+                let prec = AssocOp::Colon.precedence() as i8;
+                self.print_expr_maybe_paren(expr, prec);
+                self.word_space(":");
+                self.print_type(ty);
+            }
+            ast::ExprKind::Let(ref pat, ref scrutinee) => {
+                self.print_let(pat, scrutinee);
+            }
+            ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
+                self.print_if(test, blk, elseopt.as_deref())
+            }
+            ast::ExprKind::While(ref test, ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.word_space(":");
+                }
+                self.head("while");
+                self.print_expr_as_cond(test);
+                self.s.space();
+                self.print_block_with_attrs(blk, attrs);
+            }
+            ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.word_space(":");
+                }
+                self.head("for");
+                self.print_pat(pat);
+                self.s.space();
+                self.word_space("in");
+                self.print_expr_as_cond(iter);
+                self.s.space();
+                self.print_block_with_attrs(blk, attrs);
+            }
+            ast::ExprKind::Loop(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.word_space(":");
+                }
+                self.head("loop");
+                self.s.space();
+                self.print_block_with_attrs(blk, attrs);
+            }
+            ast::ExprKind::Match(ref expr, ref arms) => {
+                self.cbox(INDENT_UNIT);
+                self.ibox(INDENT_UNIT);
+                self.word_nbsp("match");
+                self.print_expr_as_cond(expr);
+                self.s.space();
+                self.bopen();
+                self.print_inner_attributes_no_trailing_hardbreak(attrs);
+                for arm in arms {
+                    self.print_arm(arm);
+                }
+                self.bclose(expr.span);
+            }
+            ast::ExprKind::Closure(
+                capture_clause,
+                asyncness,
+                movability,
+                ref decl,
+                ref body,
+                _,
+            ) => {
+                self.print_movability(movability);
+                self.print_asyncness(asyncness);
+                self.print_capture_clause(capture_clause);
+
+                self.print_fn_params_and_ret(decl, true);
+                self.s.space();
+                self.print_expr(body);
+                self.end(); // need to close a box
+
+                // 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);
+            }
+            ast::ExprKind::Block(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.word_space(":");
+                }
+                // containing cbox, will be closed by print-block at }
+                self.cbox(INDENT_UNIT);
+                // head-box, will be closed by print-block after {
+                self.ibox(0);
+                self.print_block_with_attrs(blk, attrs);
+            }
+            ast::ExprKind::Async(capture_clause, _, ref blk) => {
+                self.word_nbsp("async");
+                self.print_capture_clause(capture_clause);
+                self.s.space();
+                // cbox/ibox in analogy to the `ExprKind::Block` arm above
+                self.cbox(INDENT_UNIT);
+                self.ibox(0);
+                self.print_block_with_attrs(blk, attrs);
+            }
+            ast::ExprKind::Await(ref expr) => {
+                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+                self.s.word(".await");
+            }
+            ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
+                let prec = AssocOp::Assign.precedence() as i8;
+                self.print_expr_maybe_paren(lhs, prec + 1);
+                self.s.space();
+                self.word_space("=");
+                self.print_expr_maybe_paren(rhs, prec);
+            }
+            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+                let prec = AssocOp::Assign.precedence() as i8;
+                self.print_expr_maybe_paren(lhs, prec + 1);
+                self.s.space();
+                self.s.word(op.node.to_string());
+                self.word_space("=");
+                self.print_expr_maybe_paren(rhs, prec);
+            }
+            ast::ExprKind::Field(ref expr, ident) => {
+                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+                self.s.word(".");
+                self.print_ident(ident);
+            }
+            ast::ExprKind::Index(ref expr, ref index) => {
+                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+                self.s.word("[");
+                self.print_expr(index);
+                self.s.word("]");
+            }
+            ast::ExprKind::Range(ref start, ref end, limits) => {
+                // Special case for `Range`.  `AssocOp` claims that `Range` has higher precedence
+                // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
+                // Here we use a fake precedence value so that any child with lower precedence than
+                // a "normal" binop gets parenthesized.  (`LOr` is the lowest-precedence binop.)
+                let fake_prec = AssocOp::LOr.precedence() as i8;
+                if let Some(ref e) = *start {
+                    self.print_expr_maybe_paren(e, fake_prec);
+                }
+                if limits == ast::RangeLimits::HalfOpen {
+                    self.s.word("..");
+                } else {
+                    self.s.word("..=");
+                }
+                if let Some(ref e) = *end {
+                    self.print_expr_maybe_paren(e, fake_prec);
+                }
+            }
+            ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
+            ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
+            ast::ExprKind::Break(opt_label, ref opt_expr) => {
+                self.s.word("break");
+                self.s.space();
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.s.space();
+                }
+                if let Some(ref expr) = *opt_expr {
+                    self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+                    self.s.space();
+                }
+            }
+            ast::ExprKind::Continue(opt_label) => {
+                self.s.word("continue");
+                self.s.space();
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.s.space()
+                }
+            }
+            ast::ExprKind::Ret(ref result) => {
+                self.s.word("return");
+                if let Some(ref expr) = *result {
+                    self.s.word(" ");
+                    self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+                }
+            }
+            ast::ExprKind::InlineAsm(ref a) => {
+                self.s.word("asm!");
+                self.popen();
+                self.print_string(&a.asm.as_str(), a.asm_str_style);
+                self.word_space(":");
+
+                self.commasep(Inconsistent, &a.outputs, |s, out| {
+                    let constraint = out.constraint.as_str();
+                    let mut ch = constraint.chars();
+                    match ch.next() {
+                        Some('=') if out.is_rw => {
+                            s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked)
+                        }
+                        _ => s.print_string(&constraint, ast::StrStyle::Cooked),
+                    }
+                    s.popen();
+                    s.print_expr(&out.expr);
+                    s.pclose();
+                });
+                self.s.space();
+                self.word_space(":");
+
+                self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
+                    s.print_string(&co.as_str(), ast::StrStyle::Cooked);
+                    s.popen();
+                    s.print_expr(o);
+                    s.pclose();
+                });
+                self.s.space();
+                self.word_space(":");
+
+                self.commasep(Inconsistent, &a.clobbers, |s, co| {
+                    s.print_string(&co.as_str(), ast::StrStyle::Cooked);
+                });
+
+                let mut options = vec![];
+                if a.volatile {
+                    options.push("volatile");
+                }
+                if a.alignstack {
+                    options.push("alignstack");
+                }
+                if a.dialect == ast::AsmDialect::Intel {
+                    options.push("intel");
+                }
+
+                if !options.is_empty() {
+                    self.s.space();
+                    self.word_space(":");
+                    self.commasep(Inconsistent, &options, |s, &co| {
+                        s.print_string(co, ast::StrStyle::Cooked);
+                    });
+                }
+
+                self.pclose();
+            }
+            ast::ExprKind::Mac(ref m) => self.print_mac(m),
+            ast::ExprKind::Paren(ref e) => {
+                self.popen();
+                self.print_inner_attributes_inline(attrs);
+                self.print_expr(e);
+                self.pclose();
+            }
+            ast::ExprKind::Yield(ref e) => {
+                self.s.word("yield");
+                match *e {
+                    Some(ref expr) => {
+                        self.s.space();
+                        self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+                    }
+                    _ => (),
+                }
+            }
+            ast::ExprKind::Try(ref e) => {
+                self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
+                self.s.word("?")
+            }
+            ast::ExprKind::TryBlock(ref blk) => {
+                self.head("try");
+                self.s.space();
+                self.print_block_with_attrs(blk, attrs)
+            }
+            ast::ExprKind::Err => {
+                self.popen();
+                self.s.word("/*ERROR*/");
+                self.pclose()
+            }
+        }
+        self.ann.post(self, AnnNode::Expr(expr));
+        self.end();
+    }
+
+    crate fn print_local_decl(&mut self, loc: &ast::Local) {
+        self.print_pat(&loc.pat);
+        if let Some(ref ty) = loc.ty {
+            self.word_space(":");
+            self.print_type(ty);
+        }
+    }
+
+    pub fn print_usize(&mut self, i: usize) {
+        self.s.word(i.to_string())
+    }
+
+    crate fn print_name(&mut self, name: ast::Name) {
+        self.s.word(name.to_string());
+        self.ann.post(self, AnnNode::Name(&name))
+    }
+
+    fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
+        self.s.word("<");
+        self.print_type(&qself.ty);
+        if qself.position > 0 {
+            self.s.space();
+            self.word_space("as");
+            let depth = path.segments.len() - qself.position;
+            self.print_path(path, false, depth);
+        }
+        self.s.word(">");
+        self.s.word("::");
+        let item_segment = path.segments.last().unwrap();
+        self.print_ident(item_segment.ident);
+        match item_segment.args {
+            Some(ref args) => self.print_generic_args(args, colons_before_params),
+            None => {}
+        }
+    }
+
+    crate fn print_pat(&mut self, pat: &ast::Pat) {
+        self.maybe_print_comment(pat.span.lo());
+        self.ann.pre(self, AnnNode::Pat(pat));
+        /* Pat isn't normalized, but the beauty of it
+        is that it doesn't matter */
+        match pat.kind {
+            PatKind::Wild => self.s.word("_"),
+            PatKind::Ident(binding_mode, ident, ref sub) => {
+                match binding_mode {
+                    ast::BindingMode::ByRef(mutbl) => {
+                        self.word_nbsp("ref");
+                        self.print_mutability(mutbl, false);
+                    }
+                    ast::BindingMode::ByValue(ast::Mutability::Not) => {}
+                    ast::BindingMode::ByValue(ast::Mutability::Mut) => {
+                        self.word_nbsp("mut");
+                    }
+                }
+                self.print_ident(ident);
+                if let Some(ref p) = *sub {
+                    self.s.space();
+                    self.s.word_space("@");
+                    self.print_pat(p);
+                }
+            }
+            PatKind::TupleStruct(ref path, ref elts) => {
+                self.print_path(path, true, 0);
+                self.popen();
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+                self.pclose();
+            }
+            PatKind::Or(ref pats) => {
+                self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
+            }
+            PatKind::Path(None, ref path) => {
+                self.print_path(path, true, 0);
+            }
+            PatKind::Path(Some(ref qself), ref path) => {
+                self.print_qpath(path, qself, false);
+            }
+            PatKind::Struct(ref path, ref fields, etc) => {
+                self.print_path(path, true, 0);
+                self.nbsp();
+                self.word_space("{");
+                self.commasep_cmnt(
+                    Consistent,
+                    &fields[..],
+                    |s, f| {
+                        s.cbox(INDENT_UNIT);
+                        if !f.is_shorthand {
+                            s.print_ident(f.ident);
+                            s.word_nbsp(":");
+                        }
+                        s.print_pat(&f.pat);
+                        s.end();
+                    },
+                    |f| f.pat.span,
+                );
+                if etc {
+                    if !fields.is_empty() {
+                        self.word_space(",");
+                    }
+                    self.s.word("..");
+                }
+                self.s.space();
+                self.s.word("}");
+            }
+            PatKind::Tuple(ref elts) => {
+                self.popen();
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+                if elts.len() == 1 {
+                    self.s.word(",");
+                }
+                self.pclose();
+            }
+            PatKind::Box(ref inner) => {
+                self.s.word("box ");
+                self.print_pat(inner);
+            }
+            PatKind::Ref(ref inner, mutbl) => {
+                self.s.word("&");
+                if mutbl == ast::Mutability::Mut {
+                    self.s.word("mut ");
+                }
+                self.print_pat(inner);
+            }
+            PatKind::Lit(ref e) => self.print_expr(&**e),
+            PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
+                if let Some(e) = begin {
+                    self.print_expr(e);
+                    self.s.space();
+                }
+                match *end_kind {
+                    RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
+                    RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
+                    RangeEnd::Excluded => self.s.word(".."),
+                }
+                if let Some(e) = end {
+                    self.print_expr(e);
+                }
+            }
+            PatKind::Slice(ref elts) => {
+                self.s.word("[");
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+                self.s.word("]");
+            }
+            PatKind::Rest => self.s.word(".."),
+            PatKind::Paren(ref inner) => {
+                self.popen();
+                self.print_pat(inner);
+                self.pclose();
+            }
+            PatKind::Mac(ref m) => self.print_mac(m),
+        }
+        self.ann.post(self, AnnNode::Pat(pat))
+    }
+
+    fn print_arm(&mut self, arm: &ast::Arm) {
+        // Note, I have no idea why this check is necessary, but here it is.
+        if arm.attrs.is_empty() {
+            self.s.space();
+        }
+        self.cbox(INDENT_UNIT);
+        self.ibox(0);
+        self.maybe_print_comment(arm.pat.span.lo());
+        self.print_outer_attributes(&arm.attrs);
+        self.print_pat(&arm.pat);
+        self.s.space();
+        if let Some(ref e) = arm.guard {
+            self.word_space("if");
+            self.print_expr(e);
+            self.s.space();
+        }
+        self.word_space("=>");
+
+        match arm.body.kind {
+            ast::ExprKind::Block(ref blk, opt_label) => {
+                if let Some(label) = opt_label {
+                    self.print_ident(label.ident);
+                    self.word_space(":");
+                }
+
+                // The block will close the pattern's ibox.
+                self.print_block_unclosed_indent(blk);
+
+                // If it is a user-provided unsafe block, print a comma after it.
+                if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
+                    self.s.word(",");
+                }
+            }
+            _ => {
+                self.end(); // Close the ibox for the pattern.
+                self.print_expr(&arm.body);
+                self.s.word(",");
+            }
+        }
+        self.end(); // Close enclosing cbox.
+    }
+
+    fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
+        match explicit_self.node {
+            SelfKind::Value(m) => {
+                self.print_mutability(m, false);
+                self.s.word("self")
+            }
+            SelfKind::Region(ref lt, m) => {
+                self.s.word("&");
+                self.print_opt_lifetime(lt);
+                self.print_mutability(m, false);
+                self.s.word("self")
+            }
+            SelfKind::Explicit(ref typ, m) => {
+                self.print_mutability(m, false);
+                self.s.word("self");
+                self.word_space(":");
+                self.print_type(typ)
+            }
+        }
+    }
+
+    crate fn print_fn(
+        &mut self,
+        decl: &ast::FnDecl,
+        header: ast::FnHeader,
+        name: Option<ast::Ident>,
+        generics: &ast::Generics,
+        vis: &ast::Visibility,
+    ) {
+        self.print_fn_header_info(header, vis);
+
+        if let Some(name) = name {
+            self.nbsp();
+            self.print_ident(name);
+        }
+        self.print_generic_params(&generics.params);
+        self.print_fn_params_and_ret(decl, false);
+        self.print_where_clause(&generics.where_clause)
+    }
+
+    crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
+        let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") };
+        self.word(open);
+        self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure));
+        self.word(close);
+        self.print_fn_ret_ty(&decl.output)
+    }
+
+    crate fn print_movability(&mut self, movability: ast::Movability) {
+        match movability {
+            ast::Movability::Static => self.word_space("static"),
+            ast::Movability::Movable => {}
+        }
+    }
+
+    crate fn print_asyncness(&mut self, asyncness: ast::IsAsync) {
+        if asyncness.is_async() {
+            self.word_nbsp("async");
+        }
+    }
+
+    crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
+        match capture_clause {
+            ast::CaptureBy::Value => self.word_space("move"),
+            ast::CaptureBy::Ref => {}
+        }
+    }
+
+    pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) {
+        if !bounds.is_empty() {
+            self.s.word(prefix);
+            let mut first = true;
+            for bound in bounds {
+                if !(first && prefix.is_empty()) {
+                    self.nbsp();
+                }
+                if first {
+                    first = false;
+                } else {
+                    self.word_space("+");
+                }
+
+                match bound {
+                    GenericBound::Trait(tref, modifier) => {
+                        if modifier == &TraitBoundModifier::Maybe {
+                            self.s.word("?");
+                        }
+                        self.print_poly_trait_ref(tref);
+                    }
+                    GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+                }
+            }
+        }
+    }
+
+    crate fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
+        self.print_name(lifetime.ident.name)
+    }
+
+    crate fn print_lifetime_bounds(
+        &mut self,
+        lifetime: ast::Lifetime,
+        bounds: &ast::GenericBounds,
+    ) {
+        self.print_lifetime(lifetime);
+        if !bounds.is_empty() {
+            self.s.word(": ");
+            for (i, bound) in bounds.iter().enumerate() {
+                if i != 0 {
+                    self.s.word(" + ");
+                }
+                match bound {
+                    ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+                    _ => panic!(),
+                }
+            }
+        }
+    }
+
+    crate fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+        if generic_params.is_empty() {
+            return;
+        }
+
+        self.s.word("<");
+
+        self.commasep(Inconsistent, &generic_params, |s, param| {
+            s.print_outer_attributes_inline(&param.attrs);
+
+            match param.kind {
+                ast::GenericParamKind::Lifetime => {
+                    let lt = ast::Lifetime { id: param.id, ident: param.ident };
+                    s.print_lifetime_bounds(lt, &param.bounds)
+                }
+                ast::GenericParamKind::Type { ref default } => {
+                    s.print_ident(param.ident);
+                    s.print_type_bounds(":", &param.bounds);
+                    if let Some(ref default) = default {
+                        s.s.space();
+                        s.word_space("=");
+                        s.print_type(default)
+                    }
+                }
+                ast::GenericParamKind::Const { ref ty } => {
+                    s.word_space("const");
+                    s.print_ident(param.ident);
+                    s.s.space();
+                    s.word_space(":");
+                    s.print_type(ty);
+                    s.print_type_bounds(":", &param.bounds)
+                }
+            }
+        });
+
+        self.s.word(">");
+    }
+
+    crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
+        if where_clause.predicates.is_empty() {
+            return;
+        }
+
+        self.s.space();
+        self.word_space("where");
+
+        for (i, predicate) in where_clause.predicates.iter().enumerate() {
+            if i != 0 {
+                self.word_space(",");
+            }
+
+            match *predicate {
+                ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+                    ref bound_generic_params,
+                    ref bounded_ty,
+                    ref bounds,
+                    ..
+                }) => {
+                    self.print_formal_generic_params(bound_generic_params);
+                    self.print_type(bounded_ty);
+                    self.print_type_bounds(":", bounds);
+                }
+                ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+                    ref lifetime,
+                    ref bounds,
+                    ..
+                }) => {
+                    self.print_lifetime_bounds(*lifetime, bounds);
+                }
+                ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
+                    ref lhs_ty,
+                    ref rhs_ty,
+                    ..
+                }) => {
+                    self.print_type(lhs_ty);
+                    self.s.space();
+                    self.word_space("=");
+                    self.print_type(rhs_ty);
+                }
+            }
+        }
+    }
+
+    crate fn print_use_tree(&mut self, tree: &ast::UseTree) {
+        match tree.kind {
+            ast::UseTreeKind::Simple(rename, ..) => {
+                self.print_path(&tree.prefix, false, 0);
+                if let Some(rename) = rename {
+                    self.s.space();
+                    self.word_space("as");
+                    self.print_ident(rename);
+                }
+            }
+            ast::UseTreeKind::Glob => {
+                if !tree.prefix.segments.is_empty() {
+                    self.print_path(&tree.prefix, false, 0);
+                    self.s.word("::");
+                }
+                self.s.word("*");
+            }
+            ast::UseTreeKind::Nested(ref items) => {
+                if tree.prefix.segments.is_empty() {
+                    self.s.word("{");
+                } else {
+                    self.print_path(&tree.prefix, false, 0);
+                    self.s.word("::{");
+                }
+                self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| {
+                    this.print_use_tree(tree)
+                });
+                self.s.word("}");
+            }
+        }
+    }
+
+    pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
+        match mutbl {
+            ast::Mutability::Mut => self.word_nbsp("mut"),
+            ast::Mutability::Not => {
+                if print_const {
+                    self.word_nbsp("const");
+                }
+            }
+        }
+    }
+
+    crate fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
+        self.print_mutability(mt.mutbl, print_const);
+        self.print_type(&mt.ty)
+    }
+
+    crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
+        self.ibox(INDENT_UNIT);
+
+        self.print_outer_attributes_inline(&input.attrs);
+
+        match input.ty.kind {
+            ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
+            _ => {
+                if let Some(eself) = input.to_self() {
+                    self.print_explicit_self(&eself);
+                } else {
+                    let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
+                        ident.name == kw::Invalid
+                    } else {
+                        false
+                    };
+                    if !invalid {
+                        self.print_pat(&input.pat);
+                        self.s.word(":");
+                        self.s.space();
+                    }
+                    self.print_type(&input.ty);
+                }
+            }
+        }
+        self.end();
+    }
+
+    crate fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FunctionRetTy) {
+        if let ast::FunctionRetTy::Ty(ty) = fn_ret_ty {
+            self.space_if_not_bol();
+            self.ibox(INDENT_UNIT);
+            self.word_space("->");
+            self.print_type(ty);
+            self.end();
+            self.maybe_print_comment(ty.span.lo());
+        }
+    }
+
+    crate fn print_ty_fn(
+        &mut self,
+        ext: ast::Extern,
+        unsafety: ast::Unsafety,
+        decl: &ast::FnDecl,
+        name: Option<ast::Ident>,
+        generic_params: &[ast::GenericParam],
+    ) {
+        self.ibox(INDENT_UNIT);
+        if !generic_params.is_empty() {
+            self.s.word("for");
+            self.print_generic_params(generic_params);
+        }
+        let generics = ast::Generics {
+            params: Vec::new(),
+            where_clause: ast::WhereClause { predicates: Vec::new(), span: rustc_span::DUMMY_SP },
+            span: rustc_span::DUMMY_SP,
+        };
+        self.print_fn(
+            decl,
+            ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() },
+            name,
+            &generics,
+            &dummy_spanned(ast::VisibilityKind::Inherited),
+        );
+        self.end();
+    }
+
+    crate fn maybe_print_trailing_comment(
+        &mut self,
+        span: rustc_span::Span,
+        next_pos: Option<BytePos>,
+    ) {
+        if let Some(cmnts) = self.comments() {
+            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
+                self.print_comment(&cmnt);
+            }
+        }
+    }
+
+    crate fn print_remaining_comments(&mut self) {
+        // If there aren't any remaining comments, then we need to manually
+        // make sure there is a line break at the end.
+        if self.next_comment().is_none() {
+            self.s.hardbreak();
+        }
+        while let Some(ref cmnt) = self.next_comment() {
+            self.print_comment(cmnt);
+        }
+    }
+
+    crate fn print_fn_header_info(&mut self, header: ast::FnHeader, vis: &ast::Visibility) {
+        self.s.word(visibility_qualified(vis, ""));
+
+        match header.constness.node {
+            ast::Constness::NotConst => {}
+            ast::Constness::Const => self.word_nbsp("const"),
+        }
+
+        self.print_asyncness(header.asyncness.node);
+        self.print_unsafety(header.unsafety);
+
+        match header.ext {
+            ast::Extern::None => {}
+            ast::Extern::Implicit => {
+                self.word_nbsp("extern");
+            }
+            ast::Extern::Explicit(abi) => {
+                self.word_nbsp("extern");
+                self.print_literal(&abi.as_lit());
+                self.nbsp();
+            }
+        }
+
+        self.s.word("fn")
+    }
+
+    crate fn print_unsafety(&mut self, s: ast::Unsafety) {
+        match s {
+            ast::Unsafety::Normal => {}
+            ast::Unsafety::Unsafe => self.word_nbsp("unsafe"),
+        }
+    }
+
+    crate fn print_constness(&mut self, s: ast::Constness) {
+        match s {
+            ast::Constness::Const => self.word_nbsp("const"),
+            ast::Constness::NotConst => {}
+        }
+    }
+
+    crate fn print_is_auto(&mut self, s: ast::IsAuto) {
+        match s {
+            ast::IsAuto::Yes => self.word_nbsp("auto"),
+            ast::IsAuto::No => {}
+        }
+    }
+}
diff --git a/src/librustc_ast_pretty/pprust/tests.rs b/src/librustc_ast_pretty/pprust/tests.rs
new file mode 100644 (file)
index 0000000..ec6f509
--- /dev/null
@@ -0,0 +1,68 @@
+use super::*;
+
+use rustc_span;
+use rustc_span::source_map::{dummy_spanned, respan};
+use syntax::ast;
+use syntax::attr::with_default_globals;
+
+fn fun_to_string(
+    decl: &ast::FnDecl,
+    header: ast::FnHeader,
+    name: ast::Ident,
+    generics: &ast::Generics,
+) -> String {
+    to_string(|s| {
+        s.head("");
+        s.print_fn(
+            decl,
+            header,
+            Some(name),
+            generics,
+            &dummy_spanned(ast::VisibilityKind::Inherited),
+        );
+        s.end(); // Close the head box.
+        s.end(); // Close the outer box.
+    })
+}
+
+fn variant_to_string(var: &ast::Variant) -> String {
+    to_string(|s| s.print_variant(var))
+}
+
+#[test]
+fn test_fun_to_string() {
+    with_default_globals(|| {
+        let abba_ident = ast::Ident::from_str("abba");
+
+        let decl = ast::FnDecl {
+            inputs: Vec::new(),
+            output: ast::FunctionRetTy::Default(rustc_span::DUMMY_SP),
+        };
+        let generics = ast::Generics::default();
+        assert_eq!(
+            fun_to_string(&decl, ast::FnHeader::default(), abba_ident, &generics),
+            "fn abba()"
+        );
+    })
+}
+
+#[test]
+fn test_variant_to_string() {
+    with_default_globals(|| {
+        let ident = ast::Ident::from_str("principal_skinner");
+
+        let var = ast::Variant {
+            ident,
+            vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited),
+            attrs: Vec::new(),
+            id: ast::DUMMY_NODE_ID,
+            data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
+            disr_expr: None,
+            span: rustc_span::DUMMY_SP,
+            is_placeholder: false,
+        };
+
+        let varstr = variant_to_string(&var);
+        assert_eq!(varstr, "principal_skinner");
+    })
+}
index acb93e1d64070ba9945417af1f81f84783e8fb0c..83a5f41989b6c2f7125b7168cefb245eca1ce36c 100644 (file)
@@ -10,6 +10,7 @@ path = "lib.rs"
 doctest = false
 
 [dependencies]
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_serialize = { path = "../libserialize", package = "serialize" }
 rustc_errors = { path = "../librustc_errors" }
 rustc_span = { path = "../librustc_span" }
index c944537048fa20fde6ee7bbe2c37b15ea5ed9d59..be7c164395b0a9e766cb72ec5a19d1570525b2fe 100644 (file)
@@ -2,6 +2,7 @@
 
 use super::{find_by_name, mark_used};
 
+use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, Handler};
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 use rustc_macros::HashStable_Generic;
@@ -10,7 +11,6 @@
 use rustc_span::{symbol::sym, symbol::Symbol, Span};
 use std::num::NonZeroU32;
 use syntax::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
-use syntax::print::pprust;
 
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
index d0558a50acf6435eea2ff3bb32f4ed4e9592fd36..b424ce432148fb755dd20e218fb55dc9c13d94a8 100644 (file)
@@ -12,6 +12,7 @@ doctest = false
 [dependencies]
 fmt_macros = { path = "../libfmt_macros" }
 log = "0.4"
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
index a992b6e2662d2ec34537dce0b3e4e82425702cc5..3fc86a5469c2f75c6ed4da1bdf3192c14bb894a2 100644 (file)
@@ -1,11 +1,11 @@
 use rustc_errors::{Applicability, DiagnosticBuilder};
 
+use rustc_ast_pretty::pprust;
 use rustc_expand::base::*;
 use rustc_parse::parser::Parser;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use syntax::ast::{self, *};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token::{self, TokenKind};
 use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
index 7c7fc286e0a5e0e0aa04c3fc86cfbabeb851c37a..6d9bfbfd05f0aed01509d9e49c72d7bbb7dfbaf3 100644 (file)
@@ -1,6 +1,6 @@
+use rustc_ast_pretty::pprust;
 use rustc_expand::base;
 use rustc_span;
-use syntax::print;
 use syntax::tokenstream::TokenStream;
 
 pub fn expand_log_syntax<'cx>(
@@ -8,7 +8,7 @@ pub fn expand_log_syntax<'cx>(
     sp: rustc_span::Span,
     tts: TokenStream,
 ) -> Box<dyn base::MacResult + 'cx> {
-    println!("{}", print::pprust::tts_to_string(tts));
+    println!("{}", pprust::tts_to_string(tts));
 
     // any so that `log_syntax` can be invoked as an expression and item.
     base::DummyResult::any_valid(sp)
index 75bd64895b0db788bf5c3eb1259db658f6cf791e..222456d8fe0d98477eff3655d7b2a2c46ee9cf69 100644 (file)
@@ -1,5 +1,6 @@
 use std::mem;
 
+use rustc_ast_pretty::pprust;
 use rustc_expand::base::{ExtCtxt, Resolver};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_session::parse::ParseSess;
@@ -10,7 +11,6 @@
 use syntax::ast::{self, Ident};
 use syntax::attr;
 use syntax::expand::is_proc_macro_attr;
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::visit::{self, Visitor};
 
index dc85a92d272c48bb21d047cbdb4f873b8e5b3150..264223bafbcf9c6eb68572489a7568bea78ae383 100644 (file)
@@ -1,3 +1,4 @@
+use rustc_ast_pretty::pprust;
 use rustc_expand::base::{self, *};
 use rustc_expand::panictry;
 use rustc_parse::{self, new_sub_parser_from_file, parser::Parser, DirectoryOwnership};
@@ -5,7 +6,6 @@
 use rustc_span::symbol::Symbol;
 use rustc_span::{self, Pos, Span};
 use syntax::ast;
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token;
 use syntax::tokenstream::TokenStream;
index 07715cdbcb5e92e2dede68640c07d8209184cdfc..2d6ff81aea8b840628b70cad567668a20c0d1804 100644 (file)
@@ -2,13 +2,13 @@
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 use crate::util::check_builtin_macro_attribute;
 
+use rustc_ast_pretty::pprust;
 use rustc_expand::base::*;
 use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 use syntax::ast;
 use syntax::attr;
-use syntax::print::pprust;
 
 use std::iter;
 
index b856e5da5a093262125cd339be50409ab79f436c..7a5966269b30196e4b42f15e8d7d053e9f7e5ef5 100644 (file)
@@ -14,6 +14,7 @@ lazy_static = "1.0"
 log = "0.4"
 env_logger = { version = "0.7", default-features = false }
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_target = { path = "../librustc_target" }
 rustc_lint = { path = "../librustc_lint" }
 rustc_data_structures = { path = "../librustc_data_structures" }
index 5cd9e9a4a58485e7fc7a88933720879b05278d8a..65dbc59ea55b087d62c763e18f357a4e8f3ee020 100644 (file)
@@ -5,14 +5,13 @@
 use rustc::session::Session;
 use rustc::ty::{self, TyCtxt};
 use rustc::util::common::ErrorReported;
+use rustc_ast_pretty::pprust;
 use rustc_hir as hir;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::print as pprust_hir;
 use rustc_mir::util::{write_mir_graphviz, write_mir_pretty};
-
 use rustc_span::FileName;
 use syntax::ast;
-use syntax::print::pprust;
 
 use std::cell::Cell;
 use std::fs::File;
index 1310e7fbd095f4431a3b157acaf01ddba5ae123a..cb7919d630ad40e99a791a72a2ee23bed416539b 100644 (file)
@@ -14,6 +14,7 @@ doctest = false
 rustc_serialize = { path = "../libserialize", package = "serialize" }
 log = "0.4"
 rustc_span = { path = "../librustc_span" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_ast_passes = { path = "../librustc_ast_passes" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
index 9964818c36736a65ca2737397d6fca6667e5c691..f08bed5731530e8558f892d6d05e4e814c808ab9 100644 (file)
@@ -5,6 +5,7 @@
 use crate::placeholders::{placeholder, PlaceholderExpander};
 use crate::proc_macro::collect_derives;
 
+use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, FatalError, PResult};
@@ -20,7 +21,6 @@
 use syntax::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
 use syntax::ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind};
 use syntax::mut_visit::*;
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token;
 use syntax::tokenstream::{TokenStream, TokenTree};
index a7d7f811c56f880d627c0f60fef8c56210629f76..b14725fd731b1fb0d0e0a491243bcadc113ad7c1 100644 (file)
 
 use crate::mbe::{self, TokenTree};
 
+use rustc_ast_pretty::pprust;
 use rustc_parse::parser::{FollowedByType, Parser, PathStyle};
 use rustc_parse::Directory;
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{kw, sym, Symbol};
 use syntax::ast::{Ident, Name};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token::{self, DocComment, Nonterminal, Token};
 use syntax::tokenstream::TokenStream;
index 34a0616eadbe6d6825ddb465104969889db78be6..29d41543fbf8cefb9e562c403634c6304e99fecf 100644 (file)
@@ -8,6 +8,7 @@
 use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedParseResult};
 use crate::mbe::transcribe::transcribe;
 
+use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, TransparencyError};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -21,7 +22,6 @@
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 use syntax::ast;
-use syntax::print::pprust;
 use syntax::token::{self, NtTT, Token, TokenKind::*};
 use syntax::tokenstream::{DelimSpan, TokenStream};
 
index 8cac1fa658e04417d71be82e3a696c20de941634..9ae8ead1a724d1bc8b63acb1d09e841e1325385b 100644 (file)
@@ -1,10 +1,10 @@
 use crate::mbe::macro_parser;
 use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree};
 
+use rustc_ast_pretty::pprust;
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::kw;
 use syntax::ast;
-use syntax::print::pprust;
 use syntax::token::{self, Token};
 use syntax::tokenstream;
 
index 0a86754b23f04e6efabe8f3c9b7d17e3d66f9240..a7397e576b18cd02e190ab966dfaed0fa6c2f9fd 100644 (file)
@@ -1,5 +1,6 @@
 use crate::base::ExtCtxt;
 
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
 use rustc_parse::lexer::nfc_normalize;
@@ -8,7 +9,6 @@
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
 use syntax::ast;
-use syntax::print::pprust;
 use syntax::token;
 use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
 use syntax::util::comments;
index 02b394b6d798033d2ea39a8d83bcb13b8b963a26..3ae943a4ce08bf7cd3c176ea4baf67ab50068d31 100644 (file)
@@ -10,6 +10,7 @@ path = "lib.rs"
 doctest = false
 
 [dependencies]
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_target = { path = "../librustc_target" }
 rustc_macros = { path = "../librustc_macros" }
 rustc_data_structures = { path = "../librustc_data_structures" }
index 7beabacecc292b989ff80f624014476925dd2d10..b0d2f96c71a03ac7b750466bd127265825fa5557 100644 (file)
@@ -1,11 +1,11 @@
+use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
+use rustc_ast_pretty::pp::{self, Breaks};
+use rustc_ast_pretty::pprust::{self, Comments, PrintState};
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::kw;
 use rustc_span::{self, BytePos, FileName};
 use rustc_target::spec::abi::Abi;
 use syntax::ast;
-use syntax::print::pp::Breaks::{Consistent, Inconsistent};
-use syntax::print::pp::{self, Breaks};
-use syntax::print::pprust::{self, Comments, PrintState};
 use syntax::util::parser::{self, AssocOp, Fixity};
 
 use crate::hir;
index d32622c09c6237e26fc140f83e202ce3059d9dca..27df0f904e48aaf93d8c192b30247817b142b181 100644 (file)
@@ -12,6 +12,7 @@ path = "lib.rs"
 log = "0.4"
 unicode-security = "0.0.2"
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_errors = { path = "../librustc_errors" }
 rustc_hir = { path = "../librustc_hir" }
index c8d3d5f9c83d8d18ac7f44fcbef9c9c29c57318e..345665de63c3cd67fd300706462ee121a29ac5f8 100644 (file)
@@ -25,6 +25,7 @@
 use rustc::hir::map::Map;
 use rustc::traits::misc::can_type_implement_copy;
 use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt};
+use rustc_ast_pretty::pprust::{self, expr_to_string};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_feature::Stability;
@@ -41,7 +42,6 @@
 use rustc_span::{BytePos, Span};
 use syntax::ast::{self, Expr};
 use syntax::attr::{self, HasAttrs};
-use syntax::print::pprust::{self, expr_to_string};
 use syntax::tokenstream::{TokenStream, TokenTree};
 use syntax::visit::FnKind;
 
index ae490a1c6ddc8f7397411b087a0f37290c95a31b..4f30d2b2226843180500a1be812c0e84e8b5b40a 100644 (file)
@@ -1,10 +1,10 @@
 use crate::context::{CheckLintNameResult, LintStore};
 use crate::late::unerased_lint_store;
 use rustc::hir::map::Map;
-use rustc::lint::struct_lint_level;
-use rustc::lint::{LintLevelMap, LintLevelSets, LintSet, LintSource};
+use rustc::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource};
 use rustc::ty::query::Providers;
 use rustc::ty::TyCtxt;
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
@@ -17,7 +17,6 @@
 use rustc_span::symbol::{sym, Symbol};
 use syntax::ast;
 use syntax::attr;
-use syntax::print::pprust;
 use syntax::unwrap_or;
 
 use std::cmp;
index bb2c4fa1aaff64f2d6a5fad451f13feaa285498d..272c4f29203d8d635919db879d4e03a759a65831 100644 (file)
@@ -1,6 +1,7 @@
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc::ty::adjustment;
 use rustc::ty::{self, Ty};
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, Applicability};
 use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
@@ -13,7 +14,6 @@
 use rustc_span::{BytePos, Span};
 use syntax::ast;
 use syntax::attr;
-use syntax::print::pprust;
 use syntax::util::parser;
 
 use log::debug;
index a74f886043b6a71c207e6ad533e670647cf5c900..a4fdcee5e12975a8e888beeeb016d45251cdb5cf 100644 (file)
@@ -15,6 +15,7 @@ log = "0.4"
 memmap = "0.7"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
index 9d2bea28c8c2ebf88da381576bb93becd9c8ad9c..54fbdb14010c94d246df13d71b9cc8e55162ecf6 100644 (file)
@@ -1236,7 +1236,7 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
 
     /// Serialize the text of exported macros
     fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) {
-        use syntax::print::pprust;
+        use rustc_ast_pretty::pprust;
         let def_id = self.tcx.hir().local_def_id(macro_def.hir_id);
         record!(self.per_def.kind[def_id] <- EntryKind::MacroDef(self.lazy(MacroDef {
             body: pprust::tts_to_string(macro_def.body.clone()),
index eead88dcb0c3036db2e2de597cc91a9993f94805..6b2e2bb919c1302825f494e79aa27a62e400f1a0 100644 (file)
@@ -17,6 +17,7 @@ log = "0.4"
 log_settings = "0.1.1"
 polonius-engine = "0.11.0"
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
index e29730f267c2c61493866554f7070e2d0070c9cc..7cd7fc309b6b91ce18965e2fa9f813a0ba410255 100644 (file)
@@ -1,6 +1,6 @@
+use rustc_ast_pretty::pprust;
 use rustc_span::symbol::{sym, Symbol};
 use syntax::ast::{self, MetaItem};
-use syntax::print::pprust;
 
 use rustc_data_structures::work_queue::WorkQueue;
 use rustc_index::bit_set::{BitSet, HybridBitSet};
index 6b0b6db8ed4685078b6afef9f10b223006c7ee3d..176bb58ad27e6d34471c06bb130b0f29351130c2 100644 (file)
@@ -12,6 +12,7 @@ doctest = false
 [dependencies]
 bitflags = "1.0"
 log = "0.4"
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_feature = { path = "../librustc_feature" }
index a28bff3babfbf53909ab982746a2e6b6beb216bb..c28b59a7908015572cce6e33c3d1858a8536cf88 100644 (file)
@@ -1,9 +1,9 @@
 use super::{StringReader, UnmatchedBrace};
 
+use rustc_ast_pretty::pprust::token_to_string;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::PResult;
 use rustc_span::Span;
-use syntax::print::pprust::token_to_string;
 use syntax::token::{self, Token};
 use syntax::tokenstream::{
     DelimSpan,
index d25557855427d208c0616e9b4aa1213127599bb3..bf0f8ff0064d556d2a554a5e5cfdd00fa76358f6 100644 (file)
@@ -4,12 +4,12 @@
 #![feature(crate_visibility_modifier)]
 #![cfg_attr(bootstrap, feature(slice_patterns))]
 
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Diagnostic, FatalError, Level, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::{FileName, SourceFile, Span};
 use syntax::ast;
-use syntax::print::pprust;
 use syntax::token::{self, Nonterminal};
 use syntax::tokenstream::{self, TokenStream, TokenTree};
 
index 1869389dbd9e6428dbe72187bbaeac595c904dce..e58eb9ffc51e74f254bf3db277f2c0be78933c3b 100644 (file)
@@ -1,9 +1,9 @@
 use super::{Parser, PathStyle, TokenType};
+use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
 use rustc_span::{Span, Symbol};
 use syntax::ast;
 use syntax::attr;
-use syntax::print::pprust;
 use syntax::token::{self, Nonterminal};
 use syntax::util::comments;
 
index 80bc5c158a64f947bb25984211ef21e8b9c858cd..e2227f669738cb2da5128510f9599eff8e8a95f3 100644 (file)
@@ -1,5 +1,6 @@
 use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType};
 
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err};
 use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult};
@@ -10,7 +11,6 @@
     self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, Param,
 };
 use syntax::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token::{self, token_can_begin_expr, TokenKind};
 use syntax::util::parser::AssocOp;
index 098c8355ab94498b3eacf626da50beffb3610573..0d12f8cf6c03959be57cba54fbc3409a2fa2c5b8 100644 (file)
@@ -3,6 +3,7 @@
 use super::{SemiColonMode, SeqSep, TokenExpectType};
 use crate::maybe_recover_from_interpolated_ty_qpath;
 
+use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, PResult};
 use rustc_span::source_map::{self, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -12,7 +13,6 @@
     AnonConst, BinOp, BinOpKind, FnDecl, FunctionRetTy, Mac, Param, Ty, TyKind, UnOp,
 };
 use syntax::ast::{Arm, BlockCheckMode, Expr, ExprKind, IsAsync, Label, Movability, RangeLimits};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token::{self, Token, TokenKind};
 use syntax::util::classify;
index 66116611323066ad99a0c3d6dfe310f7991749d7..7f15c403e9af9718f614eb9ee21d88633b0a7b21 100644 (file)
@@ -3,6 +3,7 @@
 
 use crate::maybe_whole;
 
+use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult, StashKey};
 use rustc_span::source_map::{self, respan, Span};
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -13,7 +14,6 @@
 use syntax::ast::{Constness, Defaultness, Extern, IsAsync, IsAuto, PathSegment, StrLit, Unsafety};
 use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
 use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token;
 use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
index 4c7b37ff7e1f1f4f8e74c9c94344b5c95356fad8..1e28372c384f5d6bb2831dbcdc1d7f53b4617ad6 100644 (file)
@@ -16,6 +16,7 @@
 use crate::{Directory, DirectoryOwnership};
 
 use log::debug;
+use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::respan;
@@ -23,7 +24,6 @@
 use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
 use syntax::ast::{self, AttrStyle, AttrVec, CrateSugar, Extern, Ident, Unsafety, DUMMY_NODE_ID};
 use syntax::ast::{IsAsync, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token::{self, DelimToken, Token, TokenKind};
 use syntax::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
index edb9044df9206012ac9593682e0d0d9197a2ee63..e07b0733739d16da678661da044f8e35dd638f75 100644 (file)
@@ -1,12 +1,12 @@
 use super::{Parser, PathStyle};
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
+use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult};
 use rustc_span::source_map::{respan, Span, Spanned};
 use rustc_span::symbol::{kw, sym};
 use syntax::ast::{self, AttrVec, Attribute, FieldPat, Mac, Pat, PatKind, RangeEnd, RangeSyntax};
 use syntax::ast::{BindingMode, Expr, ExprKind, Ident, Mutability, Path, QSelf};
 use syntax::mut_visit::{noop_visit_mac, noop_visit_pat, MutVisitor};
-use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::token;
 
index 74f0d24749208d8ef6cd0c7abec7c34e6a17c2d5..f8c96ecaf9373f176b3dad5072bf016d3c9d4399 100644 (file)
@@ -17,6 +17,7 @@ syntax = { path = "../libsyntax" }
 arena = { path = "../libarena" }
 rustc = { path = "../librustc" }
 rustc_ast_lowering = { path = "../librustc_ast_lowering" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
index b762e0b08ac044bbeb420814fb5428aac5c26ee8..f8e963192c99f9a6d0f336b25ac8c2ac93dba8bb 100644 (file)
@@ -4,6 +4,7 @@
 use rustc::bug;
 use rustc::session::Session;
 use rustc::ty::{self, DefIdTree};
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_feature::BUILTIN_ATTRIBUTES;
@@ -16,7 +17,6 @@
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{BytePos, MultiSpan, Span};
 use syntax::ast::{self, Ident, Path};
-use syntax::print::pprust;
 use syntax::util::lev_distance::find_best_match_for_name;
 
 use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
index 55d1be70ad71ec75b616087ba1d6d48708b8e674..402e25fcf482b2af0121feb4577c894069e98a92 100644 (file)
@@ -25,6 +25,7 @@
 use rustc::span_bug;
 use rustc::ty::query::Providers;
 use rustc::ty::{self, DefIdTree, ResolverOutputs};
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::ptr_key::PtrKey;
 use rustc_data_structures::sync::Lrc;
@@ -47,7 +48,6 @@
 use syntax::ast::{ItemKind, Path};
 use syntax::attr;
 use syntax::node_id::{NodeMap, NodeSet};
-use syntax::print::pprust;
 use syntax::unwrap_or;
 use syntax::visit::{self, Visitor};
 
index 966638db493b37b28d0b2f2b49a0c15513f030e5..11139a3dc94fcea89a44e282b7de37526284585e 100644 (file)
@@ -10,6 +10,7 @@
 use rustc::session::parse::feature_err;
 use rustc::session::Session;
 use rustc::{lint, span_bug, ty};
+use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, StabilityLevel};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::SyntaxExtension;
@@ -24,7 +25,6 @@
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use syntax::ast::{self, Ident, NodeId};
-use syntax::print::pprust;
 
 use rustc_data_structures::sync::Lrc;
 use rustc_span::hygiene::{AstPass, MacroKind};
index e7a7eefc509e1e35218bd16de33354689d218f1c..2cbed75eaf597292bb498eb6ef43a9b584955075 100644 (file)
@@ -11,6 +11,7 @@ path = "lib.rs"
 [dependencies]
 log = "0.4"
 rustc = { path = "../librustc" }
+rustc_ast_pretty = { path = "../librustc_ast_pretty" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_codegen_utils = { path = "../librustc_codegen_utils" }
 rustc_hir = { path = "../librustc_hir" }
index 2e3e06c36f22b8c9f2597c07ea7bfd0ac9754b25..09c261cdc2388abf11162bce28712e7786799e59 100644 (file)
 use rustc::session::config::Input;
 use rustc::span_bug;
 use rustc::ty::{self, DefIdTree, TyCtxt};
+use rustc_ast_pretty::pprust::{bounds_to_string, generic_params_to_string, ty_to_string};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind as HirDefKind, Res};
 use rustc_hir::def_id::DefId;
-
-use std::env;
-use std::path::Path;
-
 use rustc_span::source_map::{respan, DUMMY_SP};
 use rustc_span::*;
 use syntax::ast::{self, Attribute, NodeId, PatKind};
-use syntax::print::pprust::{bounds_to_string, generic_params_to_string, ty_to_string};
 use syntax::ptr::P;
 use syntax::token;
 use syntax::visit::{self, Visitor};
 use syntax::walk_list;
 
+use std::env;
+use std::path::Path;
+
 use crate::dumper::{Access, Dumper};
 use crate::sig;
 use crate::span_utils::SpanUtils;
index f44ce6f4eac2744d590f10736e5c8b997df6fb06..89054441fa3bfed728822f887d4424295358e818 100644 (file)
 use rustc::session::config::{CrateType, Input, OutputType};
 use rustc::ty::{self, DefIdTree, TyCtxt};
 use rustc::{bug, span_bug};
+use rustc_ast_pretty::pprust::{self, param_to_string, ty_to_string};
 use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind as HirDefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::Node;
+use rustc_span::source_map::Spanned;
+use rustc_span::*;
+use syntax::ast::{self, Attribute, NodeId, PatKind, DUMMY_NODE_ID};
+use syntax::util::comments::strip_doc_comment_decoration;
+use syntax::visit::{self, Visitor};
 
 use std::cell::Cell;
 use std::default::Default;
 use std::io::BufWriter;
 use std::path::{Path, PathBuf};
 
-use rustc_span::source_map::Spanned;
-use rustc_span::*;
-use syntax::ast::{self, Attribute, NodeId, PatKind, DUMMY_NODE_ID};
-use syntax::print::pprust;
-use syntax::print::pprust::{param_to_string, ty_to_string};
-use syntax::util::comments::strip_doc_comment_decoration;
-use syntax::visit::{self, Visitor};
-
 use dump_visitor::DumpVisitor;
 use span_utils::SpanUtils;
 
index a9d2bfabb1ba61c6ff84dd48b7f99e7ee943b175..dbf29b6531d2abd87da36e9e9f4e56b21867711c 100644 (file)
@@ -29,9 +29,9 @@
 
 use rls_data::{SigElement, Signature};
 
+use rustc_ast_pretty::pprust;
 use rustc_hir::def::{DefKind, Res};
 use syntax::ast::{self, Extern, NodeId};
-use syntax::print::pprust;
 
 pub fn item_signature(item: &ast::Item, scx: &SaveContext<'_, '_>) -> Option<Signature> {
     if !scx.config.signatures {
index c73960fe33b9c6582d046239bafe10f83e833a11..c67064139426706b733ce69ccfb6b4615d086433 100644 (file)
@@ -44,6 +44,7 @@
 
 use rustc::middle::privacy::AccessLevels;
 use rustc::middle::stability;
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_feature::UnstableFeatures;
@@ -57,7 +58,6 @@
 use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 use syntax::ast;
-use syntax::print::pprust;
 
 use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy};
 use crate::config::RenderOptions;
index 27a6a52c02644155516f2d0e1c81c5994eb934fc..ed3f0f94e0ed8903b3f2688cbde8f9382843547e 100644 (file)
@@ -21,6 +21,7 @@
 extern crate env_logger;
 extern crate getopts;
 extern crate rustc;
+extern crate rustc_ast_pretty;
 extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
index 381308ac6be1cd736109fd6891b26b103f229367..52776eabdf562904c238f5102da6b39f9c231c10 100644 (file)
@@ -141,7 +141,7 @@ pub fn run(options: Options) -> i32 {
 
 // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
 fn scrape_test_config(krate: &::rustc_hir::Crate) -> TestOptions {
-    use syntax::print::pprust;
+    use rustc_ast_pretty::pprust;
 
     let mut opts =
         TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() };
index 49f559de1b123d9b0632b6d6a5c675d2f9b7553a..5a8c9f76ea9435c50253c6ede80ccacddc683b32 100644 (file)
@@ -1417,7 +1417,7 @@ pub enum MacDelimiter {
 }
 
 impl MacDelimiter {
-    crate fn to_token(self) -> DelimToken {
+    pub fn to_token(self) -> DelimToken {
         match self {
             MacDelimiter::Parenthesis => DelimToken::Paren,
             MacDelimiter::Bracket => DelimToken::Bracket,
@@ -1480,7 +1480,7 @@ pub struct StrLit {
 }
 
 impl StrLit {
-    crate fn as_lit(&self) -> Lit {
+    pub fn as_lit(&self) -> Lit {
         let token_kind = match self.style {
             StrStyle::Cooked => token::Str,
             StrStyle::Raw(n) => token::StrRaw(n),
index a0b2d50cef3f69fada8cc6b78e6c38a6b8638c8c..c9800da700fc5a24fa7127d6c609fc14d2061727 100644 (file)
@@ -47,12 +47,6 @@ pub mod util {
 pub mod tokenstream;
 pub mod visit;
 
-pub mod print {
-    mod helpers;
-    pub mod pp;
-    pub mod pprust;
-}
-
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
diff --git a/src/libsyntax/print/helpers.rs b/src/libsyntax/print/helpers.rs
deleted file mode 100644 (file)
index 88942cb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-use crate::print::pp::Printer;
-use std::borrow::Cow;
-
-impl Printer {
-    pub fn word_space<W: Into<Cow<'static, str>>>(&mut self, w: W) {
-        self.word(w);
-        self.space();
-    }
-
-    pub fn popen(&mut self) {
-        self.word("(");
-    }
-
-    pub fn pclose(&mut self) {
-        self.word(")");
-    }
-
-    pub fn hardbreak_if_not_bol(&mut self) {
-        if !self.is_beginning_of_line() {
-            self.hardbreak()
-        }
-    }
-
-    pub fn space_if_not_bol(&mut self) {
-        if !self.is_beginning_of_line() {
-            self.space();
-        }
-    }
-
-    pub fn nbsp(&mut self) {
-        self.word(" ")
-    }
-
-    pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
-        self.word(w);
-        self.nbsp()
-    }
-}
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
deleted file mode 100644 (file)
index e6090db..0000000
+++ /dev/null
@@ -1,640 +0,0 @@
-//! This pretty-printer is a direct reimplementation of Philip Karlton's
-//! Mesa pretty-printer, as described in appendix A of
-//!
-//! ```text
-//! STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen.
-//! Stanford Department of Computer Science, 1979.
-//! ```
-//!
-//! The algorithm's aim is to break a stream into as few lines as possible
-//! while respecting the indentation-consistency requirements of the enclosing
-//! block, and avoiding breaking at silly places on block boundaries, for
-//! example, between "x" and ")" in "x)".
-//!
-//! I am implementing this algorithm because it comes with 20 pages of
-//! documentation explaining its theory, and because it addresses the set of
-//! concerns I've seen other pretty-printers fall down on. Weirdly. Even though
-//! it's 32 years old. What can I say?
-//!
-//! Despite some redundancies and quirks in the way it's implemented in that
-//! paper, I've opted to keep the implementation here as similar as I can,
-//! changing only what was blatantly wrong, a typo, or sufficiently
-//! non-idiomatic rust that it really stuck out.
-//!
-//! In particular you'll see a certain amount of churn related to INTEGER vs.
-//! CARDINAL in the Mesa implementation. Mesa apparently interconverts the two
-//! somewhat readily? In any case, I've used usize for indices-in-buffers and
-//! ints for character-sizes-and-indentation-offsets. This respects the need
-//! for ints to "go negative" while carrying a pending-calculation balance, and
-//! helps differentiate all the numbers flying around internally (slightly).
-//!
-//! I also inverted the indentation arithmetic used in the print stack, since
-//! the Mesa implementation (somewhat randomly) stores the offset on the print
-//! stack in terms of margin-col rather than col itself. I store col.
-//!
-//! I also implemented a small change in the String token, in that I store an
-//! explicit length for the string. For most tokens this is just the length of
-//! the accompanying string. But it's necessary to permit it to differ, for
-//! encoding things that are supposed to "go on their own line" -- certain
-//! classes of comment and blank-line -- where relying on adjacent
-//! hardbreak-like Break tokens with long blankness indication doesn't actually
-//! work. To see why, consider when there is a "thing that should be on its own
-//! line" between two long blocks, say functions. If you put a hardbreak after
-//! each function (or before each) and the breaking algorithm decides to break
-//! there anyways (because the functions themselves are long) you wind up with
-//! extra blank lines. If you don't put hardbreaks you can wind up with the
-//! "thing which should be on its own line" not getting its own line in the
-//! rare case of "really small functions" or such. This re-occurs with comments
-//! and explicit blank lines. So in those cases we use a string with a payload
-//! we want isolated to a line and an explicit length that's huge, surrounded
-//! by two zero-length breaks. The algorithm will try its best to fit it on a
-//! line (which it can't) and so naturally place the content on its own line to
-//! avoid combining it with other lines and making matters even worse.
-//!
-//! # Explanation
-//!
-//! In case you do not have the paper, here is an explanation of what's going
-//! on.
-//!
-//! There is a stream of input tokens flowing through this printer.
-//!
-//! The printer buffers up to 3N tokens inside itself, where N is linewidth.
-//! Yes, linewidth is chars and tokens are multi-char, but in the worst
-//! case every token worth buffering is 1 char long, so it's ok.
-//!
-//! Tokens are String, Break, and Begin/End to delimit blocks.
-//!
-//! Begin tokens can carry an offset, saying "how far to indent when you break
-//! inside here", as well as a flag indicating "consistent" or "inconsistent"
-//! breaking. Consistent breaking means that after the first break, no attempt
-//! will be made to flow subsequent breaks together onto lines. Inconsistent
-//! is the opposite. Inconsistent breaking example would be, say:
-//!
-//! ```
-//! foo(hello, there, good, friends)
-//! ```
-//!
-//! breaking inconsistently to become
-//!
-//! ```
-//! foo(hello, there
-//!     good, friends);
-//! ```
-//!
-//! whereas a consistent breaking would yield:
-//!
-//! ```
-//! foo(hello,
-//!     there
-//!     good,
-//!     friends);
-//! ```
-//!
-//! That is, in the consistent-break blocks we value vertical alignment
-//! more than the ability to cram stuff onto a line. But in all cases if it
-//! can make a block a one-liner, it'll do so.
-//!
-//! Carrying on with high-level logic:
-//!
-//! The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and
-//! 'right' indices denote the active portion of the ring buffer as well as
-//! describing hypothetical points-in-the-infinite-stream at most 3N tokens
-//! apart (i.e., "not wrapped to ring-buffer boundaries"). The paper will switch
-//! between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer
-//! and point-in-infinite-stream senses freely.
-//!
-//! There is a parallel ring buffer, `size`, that holds the calculated size of
-//! each token. Why calculated? Because for Begin/End pairs, the "size"
-//! includes everything between the pair. That is, the "size" of Begin is
-//! actually the sum of the sizes of everything between Begin and the paired
-//! End that follows. Since that is arbitrarily far in the future, `size` is
-//! being rewritten regularly while the printer runs; in fact most of the
-//! machinery is here to work out `size` entries on the fly (and give up when
-//! they're so obviously over-long that "infinity" is a good enough
-//! approximation for purposes of line breaking).
-//!
-//! The "input side" of the printer is managed as an abstract process called
-//! SCAN, which uses `scan_stack`, to manage calculating `size`. SCAN is, in
-//! other words, the process of calculating 'size' entries.
-//!
-//! The "output side" of the printer is managed by an abstract process called
-//! PRINT, which uses `print_stack`, `margin` and `space` to figure out what to
-//! do with each token/size pair it consumes as it goes. It's trying to consume
-//! the entire buffered window, but can't output anything until the size is >=
-//! 0 (sizes are set to negative while they're pending calculation).
-//!
-//! So SCAN takes input and buffers tokens and pending calculations, while
-//! PRINT gobbles up completed calculations and tokens from the buffer. The
-//! theory is that the two can never get more than 3N tokens apart, because
-//! once there's "obviously" too much data to fit on a line, in a size
-//! calculation, SCAN will write "infinity" to the size and let PRINT consume
-//! it.
-//!
-//! In this implementation (following the paper, again) the SCAN process is the
-//! methods called `Printer::scan_*`, and the 'PRINT' process is the
-//! method called `Printer::print`.
-
-use log::debug;
-use std::borrow::Cow;
-use std::collections::VecDeque;
-use std::fmt;
-
-/// How to break. Described in more detail in the module docs.
-#[derive(Clone, Copy, PartialEq)]
-pub enum Breaks {
-    Consistent,
-    Inconsistent,
-}
-
-#[derive(Clone, Copy)]
-pub struct BreakToken {
-    offset: isize,
-    blank_space: isize,
-}
-
-#[derive(Clone, Copy)]
-pub struct BeginToken {
-    offset: isize,
-    breaks: Breaks,
-}
-
-#[derive(Clone)]
-pub enum Token {
-    // In practice a string token contains either a `&'static str` or a
-    // `String`. `Cow` is overkill for this because we never modify the data,
-    // but it's more convenient than rolling our own more specialized type.
-    String(Cow<'static, str>),
-    Break(BreakToken),
-    Begin(BeginToken),
-    End,
-    Eof,
-}
-
-impl Token {
-    crate fn is_eof(&self) -> bool {
-        match *self {
-            Token::Eof => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_hardbreak_tok(&self) -> bool {
-        match *self {
-            Token::Break(BreakToken { offset: 0, blank_space: bs }) if bs == SIZE_INFINITY => true,
-            _ => false,
-        }
-    }
-}
-
-impl fmt::Display for Token {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            Token::String(ref s) => write!(f, "STR({},{})", s, s.len()),
-            Token::Break(_) => f.write_str("BREAK"),
-            Token::Begin(_) => f.write_str("BEGIN"),
-            Token::End => f.write_str("END"),
-            Token::Eof => f.write_str("EOF"),
-        }
-    }
-}
-
-fn buf_str(buf: &[BufEntry], left: usize, right: usize, lim: usize) -> String {
-    let n = buf.len();
-    let mut i = left;
-    let mut l = lim;
-    let mut s = String::from("[");
-    while i != right && l != 0 {
-        l -= 1;
-        if i != left {
-            s.push_str(", ");
-        }
-        s.push_str(&format!("{}={}", buf[i].size, &buf[i].token));
-        i += 1;
-        i %= n;
-    }
-    s.push(']');
-    s
-}
-
-#[derive(Copy, Clone)]
-enum PrintStackBreak {
-    Fits,
-    Broken(Breaks),
-}
-
-#[derive(Copy, Clone)]
-struct PrintStackElem {
-    offset: isize,
-    pbreak: PrintStackBreak,
-}
-
-const SIZE_INFINITY: isize = 0xffff;
-
-pub fn mk_printer() -> Printer {
-    let linewidth = 78;
-    // Yes 55, it makes the ring buffers big enough to never fall behind.
-    let n: usize = 55 * linewidth;
-    debug!("mk_printer {}", linewidth);
-    Printer {
-        out: String::new(),
-        buf_max_len: n,
-        margin: linewidth as isize,
-        space: linewidth as isize,
-        left: 0,
-        right: 0,
-        // Initialize a single entry; advance_right() will extend it on demand
-        // up to `buf_max_len` elements.
-        buf: vec![BufEntry::default()],
-        left_total: 0,
-        right_total: 0,
-        scan_stack: VecDeque::new(),
-        print_stack: Vec::new(),
-        pending_indentation: 0,
-    }
-}
-
-pub struct Printer {
-    out: String,
-    buf_max_len: usize,
-    /// Width of lines we're constrained to
-    margin: isize,
-    /// Number of spaces left on line
-    space: isize,
-    /// Index of left side of input stream
-    left: usize,
-    /// Index of right side of input stream
-    right: usize,
-    /// Ring-buffer of tokens and calculated sizes
-    buf: Vec<BufEntry>,
-    /// Running size of stream "...left"
-    left_total: isize,
-    /// Running size of stream "...right"
-    right_total: isize,
-    /// Pseudo-stack, really a ring too. Holds the
-    /// primary-ring-buffers index of the Begin that started the
-    /// current block, possibly with the most recent Break after that
-    /// Begin (if there is any) on top of it. Stuff is flushed off the
-    /// bottom as it becomes irrelevant due to the primary ring-buffer
-    /// advancing.
-    scan_stack: VecDeque<usize>,
-    /// Stack of blocks-in-progress being flushed by print
-    print_stack: Vec<PrintStackElem>,
-    /// Buffered indentation to avoid writing trailing whitespace
-    pending_indentation: isize,
-}
-
-#[derive(Clone)]
-struct BufEntry {
-    token: Token,
-    size: isize,
-}
-
-impl Default for BufEntry {
-    fn default() -> Self {
-        BufEntry { token: Token::Eof, size: 0 }
-    }
-}
-
-impl Printer {
-    pub fn last_token(&self) -> Token {
-        self.buf[self.right].token.clone()
-    }
-
-    /// Be very careful with this!
-    pub fn replace_last_token(&mut self, t: Token) {
-        self.buf[self.right].token = t;
-    }
-
-    fn scan_eof(&mut self) {
-        if !self.scan_stack.is_empty() {
-            self.check_stack(0);
-            self.advance_left();
-        }
-    }
-
-    fn scan_begin(&mut self, b: BeginToken) {
-        if self.scan_stack.is_empty() {
-            self.left_total = 1;
-            self.right_total = 1;
-            self.left = 0;
-            self.right = 0;
-        } else {
-            self.advance_right();
-        }
-        debug!("pp Begin({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
-        self.scan_push(BufEntry { token: Token::Begin(b), size: -self.right_total });
-    }
-
-    fn scan_end(&mut self) {
-        if self.scan_stack.is_empty() {
-            debug!("pp End/print Vec<{},{}>", self.left, self.right);
-            self.print_end();
-        } else {
-            debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
-            self.advance_right();
-            self.scan_push(BufEntry { token: Token::End, size: -1 });
-        }
-    }
-
-    fn scan_break(&mut self, b: BreakToken) {
-        if self.scan_stack.is_empty() {
-            self.left_total = 1;
-            self.right_total = 1;
-            self.left = 0;
-            self.right = 0;
-        } else {
-            self.advance_right();
-        }
-        debug!("pp Break({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
-        self.check_stack(0);
-        self.scan_push(BufEntry { token: Token::Break(b), size: -self.right_total });
-        self.right_total += b.blank_space;
-    }
-
-    fn scan_string(&mut self, s: Cow<'static, str>) {
-        if self.scan_stack.is_empty() {
-            debug!("pp String('{}')/print Vec<{},{}>", s, self.left, self.right);
-            self.print_string(s);
-        } else {
-            debug!("pp String('{}')/buffer Vec<{},{}>", s, self.left, self.right);
-            self.advance_right();
-            let len = s.len() as isize;
-            self.buf[self.right] = BufEntry { token: Token::String(s), size: len };
-            self.right_total += len;
-            self.check_stream();
-        }
-    }
-
-    fn check_stream(&mut self) {
-        debug!(
-            "check_stream Vec<{}, {}> with left_total={}, right_total={}",
-            self.left, self.right, self.left_total, self.right_total
-        );
-        if self.right_total - self.left_total > self.space {
-            debug!(
-                "scan window is {}, longer than space on line ({})",
-                self.right_total - self.left_total,
-                self.space
-            );
-            if Some(&self.left) == self.scan_stack.back() {
-                debug!("setting {} to infinity and popping", self.left);
-                let scanned = self.scan_pop_bottom();
-                self.buf[scanned].size = SIZE_INFINITY;
-            }
-            self.advance_left();
-            if self.left != self.right {
-                self.check_stream();
-            }
-        }
-    }
-
-    fn scan_push(&mut self, entry: BufEntry) {
-        debug!("scan_push {}", self.right);
-        self.buf[self.right] = entry;
-        self.scan_stack.push_front(self.right);
-    }
-
-    fn scan_pop(&mut self) -> usize {
-        self.scan_stack.pop_front().unwrap()
-    }
-
-    fn scan_top(&mut self) -> usize {
-        *self.scan_stack.front().unwrap()
-    }
-
-    fn scan_pop_bottom(&mut self) -> usize {
-        self.scan_stack.pop_back().unwrap()
-    }
-
-    fn advance_right(&mut self) {
-        self.right += 1;
-        self.right %= self.buf_max_len;
-        // Extend the buf if necessary.
-        if self.right == self.buf.len() {
-            self.buf.push(BufEntry::default());
-        }
-        assert_ne!(self.right, self.left);
-    }
-
-    fn advance_left(&mut self) {
-        debug!(
-            "advance_left Vec<{},{}>, sizeof({})={}",
-            self.left, self.right, self.left, self.buf[self.left].size
-        );
-
-        let mut left_size = self.buf[self.left].size;
-
-        while left_size >= 0 {
-            let left = self.buf[self.left].token.clone();
-
-            let len = match left {
-                Token::Break(b) => b.blank_space,
-                Token::String(ref s) => {
-                    let len = s.len() as isize;
-                    assert_eq!(len, left_size);
-                    len
-                }
-                _ => 0,
-            };
-
-            self.print(left, left_size);
-
-            self.left_total += len;
-
-            if self.left == self.right {
-                break;
-            }
-
-            self.left += 1;
-            self.left %= self.buf_max_len;
-
-            left_size = self.buf[self.left].size;
-        }
-    }
-
-    fn check_stack(&mut self, k: usize) {
-        if !self.scan_stack.is_empty() {
-            let x = self.scan_top();
-            match self.buf[x].token {
-                Token::Begin(_) => {
-                    if k > 0 {
-                        self.scan_pop();
-                        self.buf[x].size += self.right_total;
-                        self.check_stack(k - 1);
-                    }
-                }
-                Token::End => {
-                    // paper says + not =, but that makes no sense.
-                    self.scan_pop();
-                    self.buf[x].size = 1;
-                    self.check_stack(k + 1);
-                }
-                _ => {
-                    self.scan_pop();
-                    self.buf[x].size += self.right_total;
-                    if k > 0 {
-                        self.check_stack(k);
-                    }
-                }
-            }
-        }
-    }
-
-    fn print_newline(&mut self, amount: isize) {
-        debug!("NEWLINE {}", amount);
-        self.out.push('\n');
-        self.pending_indentation = 0;
-        self.indent(amount);
-    }
-
-    fn indent(&mut self, amount: isize) {
-        debug!("INDENT {}", amount);
-        self.pending_indentation += amount;
-    }
-
-    fn get_top(&mut self) -> PrintStackElem {
-        match self.print_stack.last() {
-            Some(el) => *el,
-            None => {
-                PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
-            }
-        }
-    }
-
-    fn print_begin(&mut self, b: BeginToken, l: isize) {
-        if l > self.space {
-            let col = self.margin - self.space + b.offset;
-            debug!("print Begin -> push broken block at col {}", col);
-            self.print_stack
-                .push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) });
-        } else {
-            debug!("print Begin -> push fitting block");
-            self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits });
-        }
-    }
-
-    fn print_end(&mut self) {
-        debug!("print End -> pop End");
-        self.print_stack.pop().unwrap();
-    }
-
-    fn print_break(&mut self, b: BreakToken, l: isize) {
-        let top = self.get_top();
-        match top.pbreak {
-            PrintStackBreak::Fits => {
-                debug!("print Break({}) in fitting block", b.blank_space);
-                self.space -= b.blank_space;
-                self.indent(b.blank_space);
-            }
-            PrintStackBreak::Broken(Breaks::Consistent) => {
-                debug!("print Break({}+{}) in consistent block", top.offset, b.offset);
-                self.print_newline(top.offset + b.offset);
-                self.space = self.margin - (top.offset + b.offset);
-            }
-            PrintStackBreak::Broken(Breaks::Inconsistent) => {
-                if l > self.space {
-                    debug!("print Break({}+{}) w/ newline in inconsistent", top.offset, b.offset);
-                    self.print_newline(top.offset + b.offset);
-                    self.space = self.margin - (top.offset + b.offset);
-                } else {
-                    debug!("print Break({}) w/o newline in inconsistent", b.blank_space);
-                    self.indent(b.blank_space);
-                    self.space -= b.blank_space;
-                }
-            }
-        }
-    }
-
-    fn print_string(&mut self, s: Cow<'static, str>) {
-        let len = s.len() as isize;
-        debug!("print String({})", s);
-        // assert!(len <= space);
-        self.space -= len;
-
-        // Write the pending indent. A more concise way of doing this would be:
-        //
-        //   write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
-        //
-        // But that is significantly slower. This code is sufficiently hot, and indents can get
-        // sufficiently large, that the difference is significant on some workloads.
-        self.out.reserve(self.pending_indentation as usize);
-        self.out.extend(std::iter::repeat(' ').take(self.pending_indentation as usize));
-        self.pending_indentation = 0;
-        self.out.push_str(&s);
-    }
-
-    fn print(&mut self, token: Token, l: isize) {
-        debug!("print {} {} (remaining line space={})", token, l, self.space);
-        debug!("{}", buf_str(&self.buf, self.left, self.right, 6));
-        match token {
-            Token::Begin(b) => self.print_begin(b, l),
-            Token::End => self.print_end(),
-            Token::Break(b) => self.print_break(b, l),
-            Token::String(s) => {
-                let len = s.len() as isize;
-                assert_eq!(len, l);
-                self.print_string(s);
-            }
-            Token::Eof => panic!(), // Eof should never get here.
-        }
-    }
-
-    // Convenience functions to talk to the printer.
-
-    /// "raw box"
-    pub fn rbox(&mut self, indent: usize, b: Breaks) {
-        self.scan_begin(BeginToken { offset: indent as isize, breaks: b })
-    }
-
-    /// Inconsistent breaking box
-    pub fn ibox(&mut self, indent: usize) {
-        self.rbox(indent, Breaks::Inconsistent)
-    }
-
-    /// Consistent breaking box
-    pub fn cbox(&mut self, indent: usize) {
-        self.rbox(indent, Breaks::Consistent)
-    }
-
-    pub fn break_offset(&mut self, n: usize, off: isize) {
-        self.scan_break(BreakToken { offset: off, blank_space: n as isize })
-    }
-
-    pub fn end(&mut self) {
-        self.scan_end()
-    }
-
-    pub fn eof(mut self) -> String {
-        self.scan_eof();
-        self.out
-    }
-
-    pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
-        let s = wrd.into();
-        self.scan_string(s)
-    }
-
-    fn spaces(&mut self, n: usize) {
-        self.break_offset(n, 0)
-    }
-
-    crate fn zerobreak(&mut self) {
-        self.spaces(0)
-    }
-
-    pub fn space(&mut self) {
-        self.spaces(1)
-    }
-
-    pub fn hardbreak(&mut self) {
-        self.spaces(SIZE_INFINITY as usize)
-    }
-
-    pub fn is_beginning_of_line(&self) -> bool {
-        self.last_token().is_eof() || self.last_token().is_hardbreak_tok()
-    }
-
-    pub fn hardbreak_tok_offset(off: isize) -> Token {
-        Token::Break(BreakToken { offset: off, blank_space: SIZE_INFINITY })
-    }
-}
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
deleted file mode 100644 (file)
index 624d1c7..0000000
+++ /dev/null
@@ -1,2781 +0,0 @@
-use crate::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
-use crate::ast::{Attribute, GenericArg, MacArgs};
-use crate::ast::{GenericBound, SelfKind, TraitBoundModifier};
-use crate::attr;
-use crate::print::pp::Breaks::{Consistent, Inconsistent};
-use crate::print::pp::{self, Breaks};
-use crate::ptr::P;
-use crate::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
-use crate::tokenstream::{self, TokenStream, TokenTree};
-use crate::util::classify;
-use crate::util::comments;
-use crate::util::parser::{self, AssocOp, Fixity};
-
-use rustc_data_structures::sync::Once;
-use rustc_span::edition::Edition;
-use rustc_span::source_map::{dummy_spanned, SourceMap, Spanned};
-use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::{BytePos, FileName, Span};
-
-use std::borrow::Cow;
-
-#[cfg(test)]
-mod tests;
-
-pub enum MacHeader<'a> {
-    Path(&'a ast::Path),
-    Keyword(&'static str),
-}
-
-pub enum AnnNode<'a> {
-    Ident(&'a ast::Ident),
-    Name(&'a ast::Name),
-    Block(&'a ast::Block),
-    Item(&'a ast::Item),
-    SubItem(ast::NodeId),
-    Expr(&'a ast::Expr),
-    Pat(&'a ast::Pat),
-    Crate(&'a ast::Crate),
-}
-
-pub trait PpAnn {
-    fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
-    fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
-}
-
-#[derive(Copy, Clone)]
-pub struct NoAnn;
-
-impl PpAnn for NoAnn {}
-
-pub struct Comments<'a> {
-    cm: &'a SourceMap,
-    comments: Vec<comments::Comment>,
-    current: usize,
-}
-
-impl<'a> Comments<'a> {
-    pub fn new(cm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
-        let comments = comments::gather_comments(cm, filename, input);
-        Comments { cm, comments, current: 0 }
-    }
-
-    pub fn next(&self) -> Option<comments::Comment> {
-        self.comments.get(self.current).cloned()
-    }
-
-    pub fn trailing_comment(
-        &mut self,
-        span: rustc_span::Span,
-        next_pos: Option<BytePos>,
-    ) -> Option<comments::Comment> {
-        if let Some(cmnt) = self.next() {
-            if cmnt.style != comments::Trailing {
-                return None;
-            }
-            let span_line = self.cm.lookup_char_pos(span.hi());
-            let comment_line = self.cm.lookup_char_pos(cmnt.pos);
-            let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
-            if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
-                return Some(cmnt);
-            }
-        }
-
-        None
-    }
-}
-
-pub struct State<'a> {
-    pub s: pp::Printer,
-    comments: Option<Comments<'a>>,
-    ann: &'a (dyn PpAnn + 'a),
-    is_expanded: bool,
-}
-
-crate const INDENT_UNIT: usize = 4;
-
-/// Requires you to pass an input filename and reader so that
-/// it can scan the input text for comments to copy forward.
-pub fn print_crate<'a>(
-    cm: &'a SourceMap,
-    krate: &ast::Crate,
-    filename: FileName,
-    input: String,
-    ann: &'a dyn PpAnn,
-    is_expanded: bool,
-    edition: Edition,
-    injected_crate_name: &Once<Symbol>,
-) -> String {
-    let mut s = State {
-        s: pp::mk_printer(),
-        comments: Some(Comments::new(cm, filename, input)),
-        ann,
-        is_expanded,
-    };
-
-    if is_expanded && injected_crate_name.try_get().is_some() {
-        // We need to print `#![no_std]` (and its feature gate) so that
-        // compiling pretty-printed source won't inject libstd again.
-        // However, we don't want these attributes in the AST because
-        // of the feature gate, so we fake them up here.
-
-        // `#![feature(prelude_import)]`
-        let pi_nested = attr::mk_nested_word_item(ast::Ident::with_dummy_span(sym::prelude_import));
-        let list = attr::mk_list_item(ast::Ident::with_dummy_span(sym::feature), vec![pi_nested]);
-        let fake_attr = attr::mk_attr_inner(list);
-        s.print_attribute(&fake_attr);
-
-        // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
-        // root, so this is not needed, and actually breaks things.
-        if edition == Edition::Edition2015 {
-            // `#![no_std]`
-            let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
-            let fake_attr = attr::mk_attr_inner(no_std_meta);
-            s.print_attribute(&fake_attr);
-        }
-    }
-
-    s.print_mod(&krate.module, &krate.attrs);
-    s.print_remaining_comments();
-    s.ann.post(&mut s, AnnNode::Crate(krate));
-    s.s.eof()
-}
-
-pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
-    let mut printer =
-        State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false };
-    f(&mut printer);
-    printer.s.eof()
-}
-
-// This makes comma-separated lists look slightly nicer,
-// and also addresses a specific regression described in issue #63896.
-fn tt_prepend_space(tt: &TokenTree) -> bool {
-    match tt {
-        TokenTree::Token(token) => match token.kind {
-            token::Comma => false,
-            _ => true,
-        },
-        _ => true,
-    }
-}
-
-fn binop_to_string(op: BinOpToken) -> &'static str {
-    match op {
-        token::Plus => "+",
-        token::Minus => "-",
-        token::Star => "*",
-        token::Slash => "/",
-        token::Percent => "%",
-        token::Caret => "^",
-        token::And => "&",
-        token::Or => "|",
-        token::Shl => "<<",
-        token::Shr => ">>",
-    }
-}
-
-pub fn literal_to_string(lit: token::Lit) -> String {
-    let token::Lit { kind, symbol, suffix } = lit;
-    let mut out = match kind {
-        token::Byte => format!("b'{}'", symbol),
-        token::Char => format!("'{}'", symbol),
-        token::Str => format!("\"{}\"", symbol),
-        token::StrRaw(n) => {
-            format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
-        }
-        token::ByteStr => format!("b\"{}\"", symbol),
-        token::ByteStrRaw(n) => {
-            format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
-        }
-        token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
-    };
-
-    if let Some(suffix) = suffix {
-        out.push_str(&suffix.as_str())
-    }
-
-    out
-}
-
-/// Print an ident from AST, `$crate` is converted into its respective crate name.
-pub fn ast_ident_to_string(ident: ast::Ident, is_raw: bool) -> String {
-    ident_to_string(ident.name, is_raw, Some(ident.span))
-}
-
-// AST pretty-printer is used as a fallback for turning AST structures into token streams for
-// proc macros. Additionally, proc macros may stringify their input and expect it survive the
-// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
-// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
-// hygiene data, most importantly name of the crate it refers to.
-// As a result we print `$crate` as `crate` if it refers to the local crate
-// and as `::other_crate_name` if it refers to some other crate.
-// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
-// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
-// so we should not perform this lossy conversion if the top level call to the pretty-printer was
-// done for a token stream or a single token.
-fn ident_to_string(name: ast::Name, is_raw: bool, convert_dollar_crate: Option<Span>) -> String {
-    if is_raw {
-        format!("r#{}", name)
-    } else {
-        if name == kw::DollarCrate {
-            if let Some(span) = convert_dollar_crate {
-                let converted = span.ctxt().dollar_crate_name();
-                return if converted.is_path_segment_keyword() {
-                    converted.to_string()
-                } else {
-                    format!("::{}", converted)
-                };
-            }
-        }
-        name.to_string()
-    }
-}
-
-/// Print the token kind precisely, without converting `$crate` into its respective crate name.
-pub fn token_kind_to_string(tok: &TokenKind) -> String {
-    token_kind_to_string_ext(tok, None)
-}
-
-fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
-    match *tok {
-        token::Eq => "=".to_string(),
-        token::Lt => "<".to_string(),
-        token::Le => "<=".to_string(),
-        token::EqEq => "==".to_string(),
-        token::Ne => "!=".to_string(),
-        token::Ge => ">=".to_string(),
-        token::Gt => ">".to_string(),
-        token::Not => "!".to_string(),
-        token::Tilde => "~".to_string(),
-        token::OrOr => "||".to_string(),
-        token::AndAnd => "&&".to_string(),
-        token::BinOp(op) => binop_to_string(op).to_string(),
-        token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
-
-        /* Structural symbols */
-        token::At => "@".to_string(),
-        token::Dot => ".".to_string(),
-        token::DotDot => "..".to_string(),
-        token::DotDotDot => "...".to_string(),
-        token::DotDotEq => "..=".to_string(),
-        token::Comma => ",".to_string(),
-        token::Semi => ";".to_string(),
-        token::Colon => ":".to_string(),
-        token::ModSep => "::".to_string(),
-        token::RArrow => "->".to_string(),
-        token::LArrow => "<-".to_string(),
-        token::FatArrow => "=>".to_string(),
-        token::OpenDelim(token::Paren) => "(".to_string(),
-        token::CloseDelim(token::Paren) => ")".to_string(),
-        token::OpenDelim(token::Bracket) => "[".to_string(),
-        token::CloseDelim(token::Bracket) => "]".to_string(),
-        token::OpenDelim(token::Brace) => "{".to_string(),
-        token::CloseDelim(token::Brace) => "}".to_string(),
-        token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => " ".to_string(),
-        token::Pound => "#".to_string(),
-        token::Dollar => "$".to_string(),
-        token::Question => "?".to_string(),
-        token::SingleQuote => "'".to_string(),
-
-        /* Literals */
-        token::Literal(lit) => literal_to_string(lit),
-
-        /* Name components */
-        token::Ident(s, is_raw) => ident_to_string(s, is_raw, convert_dollar_crate),
-        token::Lifetime(s) => s.to_string(),
-
-        /* Other */
-        token::DocComment(s) => s.to_string(),
-        token::Eof => "<eof>".to_string(),
-        token::Whitespace => " ".to_string(),
-        token::Comment => "/* */".to_string(),
-        token::Shebang(s) => format!("/* shebang: {}*/", s),
-        token::Unknown(s) => s.to_string(),
-
-        token::Interpolated(ref nt) => nonterminal_to_string(nt),
-    }
-}
-
-/// Print the token precisely, without converting `$crate` into its respective crate name.
-pub fn token_to_string(token: &Token) -> String {
-    token_to_string_ext(token, false)
-}
-
-fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
-    let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
-    token_kind_to_string_ext(&token.kind, convert_dollar_crate)
-}
-
-pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
-    match *nt {
-        token::NtExpr(ref e) => expr_to_string(e),
-        token::NtMeta(ref e) => attr_item_to_string(e),
-        token::NtTy(ref e) => ty_to_string(e),
-        token::NtPath(ref e) => path_to_string(e),
-        token::NtItem(ref e) => item_to_string(e),
-        token::NtBlock(ref e) => block_to_string(e),
-        token::NtStmt(ref e) => stmt_to_string(e),
-        token::NtPat(ref e) => pat_to_string(e),
-        token::NtIdent(e, is_raw) => ast_ident_to_string(e, is_raw),
-        token::NtLifetime(e) => e.to_string(),
-        token::NtLiteral(ref e) => expr_to_string(e),
-        token::NtTT(ref tree) => tt_to_string(tree.clone()),
-        // FIXME(Centril): merge these variants.
-        token::NtImplItem(ref e) | token::NtTraitItem(ref e) => assoc_item_to_string(e),
-        token::NtVis(ref e) => vis_to_string(e),
-        token::NtForeignItem(ref e) => foreign_item_to_string(e),
-    }
-}
-
-pub fn ty_to_string(ty: &ast::Ty) -> String {
-    to_string(|s| s.print_type(ty))
-}
-
-pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
-    to_string(|s| s.print_type_bounds("", bounds))
-}
-
-pub fn pat_to_string(pat: &ast::Pat) -> String {
-    to_string(|s| s.print_pat(pat))
-}
-
-pub fn expr_to_string(e: &ast::Expr) -> String {
-    to_string(|s| s.print_expr(e))
-}
-
-pub fn tt_to_string(tt: tokenstream::TokenTree) -> String {
-    to_string(|s| s.print_tt(tt, false))
-}
-
-pub fn tts_to_string(tokens: TokenStream) -> String {
-    to_string(|s| s.print_tts(tokens, false))
-}
-
-pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
-    to_string(|s| s.print_stmt(stmt))
-}
-
-pub fn item_to_string(i: &ast::Item) -> String {
-    to_string(|s| s.print_item(i))
-}
-
-fn assoc_item_to_string(i: &ast::AssocItem) -> String {
-    to_string(|s| s.print_assoc_item(i))
-}
-
-pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
-    to_string(|s| s.print_generic_params(generic_params))
-}
-
-pub fn path_to_string(p: &ast::Path) -> String {
-    to_string(|s| s.print_path(p, false, 0))
-}
-
-pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
-    to_string(|s| s.print_path_segment(p, false))
-}
-
-pub fn vis_to_string(v: &ast::Visibility) -> String {
-    to_string(|s| s.print_visibility(v))
-}
-
-fn block_to_string(blk: &ast::Block) -> String {
-    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)
-    })
-}
-
-pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
-    to_string(|s| s.print_meta_list_item(li))
-}
-
-fn attr_item_to_string(ai: &ast::AttrItem) -> String {
-    to_string(|s| s.print_attr_item(ai, ai.path.span))
-}
-
-pub fn attribute_to_string(attr: &ast::Attribute) -> String {
-    to_string(|s| s.print_attribute(attr))
-}
-
-pub fn param_to_string(arg: &ast::Param) -> String {
-    to_string(|s| s.print_param(arg, false))
-}
-
-fn foreign_item_to_string(arg: &ast::ForeignItem) -> String {
-    to_string(|s| s.print_foreign_item(arg))
-}
-
-fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
-    format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
-}
-
-impl std::ops::Deref for State<'_> {
-    type Target = pp::Printer;
-    fn deref(&self) -> &Self::Target {
-        &self.s
-    }
-}
-
-impl std::ops::DerefMut for State<'_> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.s
-    }
-}
-
-pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
-    fn comments(&mut self) -> &mut Option<Comments<'a>>;
-    fn print_ident(&mut self, ident: ast::Ident);
-    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
-
-    fn strsep<T, F>(
-        &mut self,
-        sep: &'static str,
-        space_before: bool,
-        b: Breaks,
-        elts: &[T],
-        mut op: F,
-    ) where
-        F: FnMut(&mut Self, &T),
-    {
-        self.rbox(0, b);
-        if let Some((first, rest)) = elts.split_first() {
-            op(self, first);
-            for elt in rest {
-                if space_before {
-                    self.space();
-                }
-                self.word_space(sep);
-                op(self, elt);
-            }
-        }
-        self.end();
-    }
-
-    fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
-    where
-        F: FnMut(&mut Self, &T),
-    {
-        self.strsep(",", false, b, elts, op)
-    }
-
-    fn maybe_print_comment(&mut self, pos: BytePos) {
-        while let Some(ref cmnt) = self.next_comment() {
-            if cmnt.pos < pos {
-                self.print_comment(cmnt);
-            } else {
-                break;
-            }
-        }
-    }
-
-    fn print_comment(&mut self, cmnt: &comments::Comment) {
-        match cmnt.style {
-            comments::Mixed => {
-                assert_eq!(cmnt.lines.len(), 1);
-                self.zerobreak();
-                self.word(cmnt.lines[0].clone());
-                self.zerobreak()
-            }
-            comments::Isolated => {
-                self.hardbreak_if_not_bol();
-                for line in &cmnt.lines {
-                    // Don't print empty lines because they will end up as trailing
-                    // whitespace.
-                    if !line.is_empty() {
-                        self.word(line.clone());
-                    }
-                    self.hardbreak();
-                }
-            }
-            comments::Trailing => {
-                if !self.is_beginning_of_line() {
-                    self.word(" ");
-                }
-                if cmnt.lines.len() == 1 {
-                    self.word(cmnt.lines[0].clone());
-                    self.hardbreak()
-                } else {
-                    self.ibox(0);
-                    for line in &cmnt.lines {
-                        if !line.is_empty() {
-                            self.word(line.clone());
-                        }
-                        self.hardbreak();
-                    }
-                    self.end();
-                }
-            }
-            comments::BlankLine => {
-                // We need to do at least one, possibly two hardbreaks.
-                let twice = match self.last_token() {
-                    pp::Token::String(s) => ";" == s,
-                    pp::Token::Begin(_) => true,
-                    pp::Token::End => true,
-                    _ => false,
-                };
-                if twice {
-                    self.hardbreak();
-                }
-                self.hardbreak();
-            }
-        }
-        if let Some(cm) = self.comments() {
-            cm.current += 1;
-        }
-    }
-
-    fn next_comment(&mut self) -> Option<comments::Comment> {
-        self.comments().as_mut().and_then(|c| c.next())
-    }
-
-    fn print_literal(&mut self, lit: &ast::Lit) {
-        self.maybe_print_comment(lit.span.lo());
-        self.word(lit.token.to_string())
-    }
-
-    fn print_string(&mut self, st: &str, style: ast::StrStyle) {
-        let st = match style {
-            ast::StrStyle::Cooked => (format!("\"{}\"", st.escape_debug())),
-            ast::StrStyle::Raw(n) => {
-                format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
-            }
-        };
-        self.word(st)
-    }
-
-    fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
-    }
-
-    fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
-    }
-
-    fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
-    }
-
-    fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
-    }
-
-    fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
-    }
-
-    fn print_either_attributes(
-        &mut self,
-        attrs: &[ast::Attribute],
-        kind: ast::AttrStyle,
-        is_inline: bool,
-        trailing_hardbreak: bool,
-    ) {
-        let mut count = 0;
-        for attr in attrs {
-            if attr.style == kind {
-                self.print_attribute_inline(attr, is_inline);
-                if is_inline {
-                    self.nbsp();
-                }
-                count += 1;
-            }
-        }
-        if count > 0 && trailing_hardbreak && !is_inline {
-            self.hardbreak_if_not_bol();
-        }
-    }
-
-    fn print_attribute(&mut self, attr: &ast::Attribute) {
-        self.print_attribute_inline(attr, false)
-    }
-
-    fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
-        if !is_inline {
-            self.hardbreak_if_not_bol();
-        }
-        self.maybe_print_comment(attr.span.lo());
-        match attr.kind {
-            ast::AttrKind::Normal(ref item) => {
-                match attr.style {
-                    ast::AttrStyle::Inner => self.word("#!["),
-                    ast::AttrStyle::Outer => self.word("#["),
-                }
-                self.print_attr_item(&item, attr.span);
-                self.word("]");
-            }
-            ast::AttrKind::DocComment(comment) => {
-                self.word(comment.to_string());
-                self.hardbreak()
-            }
-        }
-    }
-
-    fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
-        self.ibox(0);
-        match &item.args {
-            MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
-                Some(MacHeader::Path(&item.path)),
-                false,
-                None,
-                delim.to_token(),
-                tokens.clone(),
-                true,
-                span,
-            ),
-            MacArgs::Empty | MacArgs::Eq(..) => {
-                self.print_path(&item.path, false, 0);
-                if let MacArgs::Eq(_, tokens) = &item.args {
-                    self.space();
-                    self.word_space("=");
-                    self.print_tts(tokens.clone(), true);
-                }
-            }
-        }
-        self.end();
-    }
-
-    fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
-        match item {
-            ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
-            ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
-        }
-    }
-
-    fn print_meta_item(&mut self, item: &ast::MetaItem) {
-        self.ibox(INDENT_UNIT);
-        match item.kind {
-            ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
-            ast::MetaItemKind::NameValue(ref value) => {
-                self.print_path(&item.path, false, 0);
-                self.space();
-                self.word_space("=");
-                self.print_literal(value);
-            }
-            ast::MetaItemKind::List(ref items) => {
-                self.print_path(&item.path, false, 0);
-                self.popen();
-                self.commasep(Consistent, &items[..], |s, i| s.print_meta_list_item(i));
-                self.pclose();
-            }
-        }
-        self.end();
-    }
-
-    /// This doesn't deserve to be called "pretty" printing, but it should be
-    /// meaning-preserving. A quick hack that might help would be to look at the
-    /// spans embedded in the TTs to decide where to put spaces and newlines.
-    /// But it'd be better to parse these according to the grammar of the
-    /// appropriate macro, transcribe back into the grammar we just parsed from,
-    /// and then pretty-print the resulting AST nodes (so, e.g., we print
-    /// expression arguments as expressions). It can be done! I think.
-    fn print_tt(&mut self, tt: tokenstream::TokenTree, convert_dollar_crate: bool) {
-        match tt {
-            TokenTree::Token(ref token) => {
-                self.word(token_to_string_ext(&token, convert_dollar_crate));
-                match token.kind {
-                    token::DocComment(..) => self.hardbreak(),
-                    _ => {}
-                }
-            }
-            TokenTree::Delimited(dspan, delim, tts) => {
-                self.print_mac_common(
-                    None,
-                    false,
-                    None,
-                    delim,
-                    tts,
-                    convert_dollar_crate,
-                    dspan.entire(),
-                );
-            }
-        }
-    }
-
-    fn print_tts(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) {
-        for (i, tt) in tts.into_trees().enumerate() {
-            if i != 0 && tt_prepend_space(&tt) {
-                self.space();
-            }
-            self.print_tt(tt, convert_dollar_crate);
-        }
-    }
-
-    fn print_mac_common(
-        &mut self,
-        header: Option<MacHeader<'_>>,
-        has_bang: bool,
-        ident: Option<ast::Ident>,
-        delim: DelimToken,
-        tts: TokenStream,
-        convert_dollar_crate: bool,
-        span: Span,
-    ) {
-        if delim == DelimToken::Brace {
-            self.cbox(INDENT_UNIT);
-        }
-        match header {
-            Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
-            Some(MacHeader::Keyword(kw)) => self.word(kw),
-            None => {}
-        }
-        if has_bang {
-            self.word("!");
-        }
-        if let Some(ident) = ident {
-            self.nbsp();
-            self.print_ident(ident);
-        }
-        match delim {
-            DelimToken::Brace => {
-                if header.is_some() || has_bang || ident.is_some() {
-                    self.nbsp();
-                }
-                self.word("{");
-                if !tts.is_empty() {
-                    self.space();
-                }
-            }
-            _ => self.word(token_kind_to_string(&token::OpenDelim(delim))),
-        }
-        self.ibox(0);
-        self.print_tts(tts, convert_dollar_crate);
-        self.end();
-        match delim {
-            DelimToken::Brace => self.bclose(span),
-            _ => self.word(token_kind_to_string(&token::CloseDelim(delim))),
-        }
-    }
-
-    fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
-        self.maybe_print_comment(path.span.lo());
-
-        for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
-            if i > 0 {
-                self.word("::")
-            }
-            self.print_path_segment(segment, colons_before_params);
-        }
-    }
-
-    fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
-        if segment.ident.name != kw::PathRoot {
-            self.print_ident(segment.ident);
-            if let Some(ref args) = segment.args {
-                self.print_generic_args(args, colons_before_params);
-            }
-        }
-    }
-
-    fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
-        let w = w.into();
-        // Outer-box is consistent.
-        self.cbox(INDENT_UNIT);
-        // Head-box is inconsistent.
-        self.ibox(w.len() + 1);
-        // Keyword that starts the head.
-        if !w.is_empty() {
-            self.word_nbsp(w);
-        }
-    }
-
-    fn bopen(&mut self) {
-        self.word("{");
-        self.end(); // Close the head-box.
-    }
-
-    fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
-        self.maybe_print_comment(span.hi());
-        self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
-        self.word("}");
-        if close_box {
-            self.end(); // Close the outer-box.
-        }
-    }
-
-    fn bclose(&mut self, span: rustc_span::Span) {
-        self.bclose_maybe_open(span, true)
-    }
-
-    fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
-        if !self.is_beginning_of_line() {
-            self.break_offset(n, off)
-        } else {
-            if off != 0 && self.last_token().is_hardbreak_tok() {
-                // We do something pretty sketchy here: tuck the nonzero
-                // offset-adjustment we were going to deposit along with the
-                // break into the previous hardbreak.
-                self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
-            }
-        }
-    }
-}
-
-impl<'a> PrintState<'a> for State<'a> {
-    fn comments(&mut self) -> &mut Option<Comments<'a>> {
-        &mut self.comments
-    }
-
-    fn print_ident(&mut self, ident: ast::Ident) {
-        self.s.word(ast_ident_to_string(ident, ident.is_raw_guess()));
-        self.ann.post(self, AnnNode::Ident(&ident))
-    }
-
-    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
-        if colons_before_params {
-            self.s.word("::")
-        }
-
-        match *args {
-            ast::GenericArgs::AngleBracketed(ref data) => {
-                self.s.word("<");
-
-                self.commasep(Inconsistent, &data.args, |s, generic_arg| {
-                    s.print_generic_arg(generic_arg)
-                });
-
-                let mut comma = data.args.len() != 0;
-
-                for constraint in data.constraints.iter() {
-                    if comma {
-                        self.word_space(",")
-                    }
-                    self.print_ident(constraint.ident);
-                    self.s.space();
-                    match constraint.kind {
-                        ast::AssocTyConstraintKind::Equality { ref ty } => {
-                            self.word_space("=");
-                            self.print_type(ty);
-                        }
-                        ast::AssocTyConstraintKind::Bound { ref bounds } => {
-                            self.print_type_bounds(":", &*bounds);
-                        }
-                    }
-                    comma = true;
-                }
-
-                self.s.word(">")
-            }
-
-            ast::GenericArgs::Parenthesized(ref data) => {
-                self.s.word("(");
-                self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
-                self.s.word(")");
-                self.print_fn_ret_ty(&data.output);
-            }
-        }
-    }
-}
-
-impl<'a> State<'a> {
-    // Synthesizes a comment that was not textually present in the original source
-    // file.
-    pub fn synth_comment(&mut self, text: String) {
-        self.s.word("/*");
-        self.s.space();
-        self.s.word(text);
-        self.s.space();
-        self.s.word("*/")
-    }
-
-    crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
-    where
-        F: FnMut(&mut State<'_>, &T),
-        G: FnMut(&T) -> rustc_span::Span,
-    {
-        self.rbox(0, b);
-        let len = elts.len();
-        let mut i = 0;
-        for elt in elts {
-            self.maybe_print_comment(get_span(elt).hi());
-            op(self, elt);
-            i += 1;
-            if i < len {
-                self.s.word(",");
-                self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
-                self.space_if_not_bol();
-            }
-        }
-        self.end();
-    }
-
-    crate fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
-        self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
-    }
-
-    pub fn print_mod(&mut self, _mod: &ast::Mod, attrs: &[ast::Attribute]) {
-        self.print_inner_attributes(attrs);
-        for item in &_mod.items {
-            self.print_item(item);
-        }
-    }
-
-    crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) {
-        self.print_inner_attributes(attrs);
-        for item in &nmod.items {
-            self.print_foreign_item(item);
-        }
-    }
-
-    pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
-        if let Some(lt) = *lifetime {
-            self.print_lifetime(lt);
-            self.nbsp();
-        }
-    }
-
-    crate fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
-        match generic_arg {
-            GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
-            GenericArg::Type(ty) => self.print_type(ty),
-            GenericArg::Const(ct) => self.print_expr(&ct.value),
-        }
-    }
-
-    pub fn print_type(&mut self, ty: &ast::Ty) {
-        self.maybe_print_comment(ty.span.lo());
-        self.ibox(0);
-        match ty.kind {
-            ast::TyKind::Slice(ref ty) => {
-                self.s.word("[");
-                self.print_type(ty);
-                self.s.word("]");
-            }
-            ast::TyKind::Ptr(ref mt) => {
-                self.s.word("*");
-                self.print_mt(mt, true);
-            }
-            ast::TyKind::Rptr(ref lifetime, ref mt) => {
-                self.s.word("&");
-                self.print_opt_lifetime(lifetime);
-                self.print_mt(mt, false);
-            }
-            ast::TyKind::Never => {
-                self.s.word("!");
-            }
-            ast::TyKind::Tup(ref elts) => {
-                self.popen();
-                self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(ty));
-                if elts.len() == 1 {
-                    self.s.word(",");
-                }
-                self.pclose();
-            }
-            ast::TyKind::Paren(ref typ) => {
-                self.popen();
-                self.print_type(typ);
-                self.pclose();
-            }
-            ast::TyKind::BareFn(ref f) => {
-                self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
-            }
-            ast::TyKind::Path(None, ref path) => {
-                self.print_path(path, false, 0);
-            }
-            ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
-            ast::TyKind::TraitObject(ref bounds, syntax) => {
-                let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn" } else { "" };
-                self.print_type_bounds(prefix, &bounds[..]);
-            }
-            ast::TyKind::ImplTrait(_, ref bounds) => {
-                self.print_type_bounds("impl", &bounds[..]);
-            }
-            ast::TyKind::Array(ref ty, ref length) => {
-                self.s.word("[");
-                self.print_type(ty);
-                self.s.word("; ");
-                self.print_expr(&length.value);
-                self.s.word("]");
-            }
-            ast::TyKind::Typeof(ref e) => {
-                self.s.word("typeof(");
-                self.print_expr(&e.value);
-                self.s.word(")");
-            }
-            ast::TyKind::Infer => {
-                self.s.word("_");
-            }
-            ast::TyKind::Err => {
-                self.popen();
-                self.s.word("/*ERROR*/");
-                self.pclose();
-            }
-            ast::TyKind::ImplicitSelf => {
-                self.s.word("Self");
-            }
-            ast::TyKind::Mac(ref m) => {
-                self.print_mac(m);
-            }
-            ast::TyKind::CVarArgs => {
-                self.s.word("...");
-            }
-        }
-        self.end();
-    }
-
-    crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
-        self.hardbreak_if_not_bol();
-        self.maybe_print_comment(item.span.lo());
-        self.print_outer_attributes(&item.attrs);
-        match item.kind {
-            ast::ForeignItemKind::Fn(ref decl, ref generics) => {
-                self.head("");
-                self.print_fn(
-                    decl,
-                    ast::FnHeader::default(),
-                    Some(item.ident),
-                    generics,
-                    &item.vis,
-                );
-                self.end(); // end head-ibox
-                self.s.word(";");
-                self.end(); // end the outer fn box
-            }
-            ast::ForeignItemKind::Static(ref t, m) => {
-                self.head(visibility_qualified(&item.vis, "static"));
-                if m == ast::Mutability::Mut {
-                    self.word_space("mut");
-                }
-                self.print_ident(item.ident);
-                self.word_space(":");
-                self.print_type(t);
-                self.s.word(";");
-                self.end(); // end the head-ibox
-                self.end(); // end the outer cbox
-            }
-            ast::ForeignItemKind::Ty => {
-                self.head(visibility_qualified(&item.vis, "type"));
-                self.print_ident(item.ident);
-                self.s.word(";");
-                self.end(); // end the head-ibox
-                self.end(); // end the outer cbox
-            }
-            ast::ForeignItemKind::Macro(ref m) => {
-                self.print_mac(m);
-                if m.args.need_semicolon() {
-                    self.s.word(";");
-                }
-            }
-        }
-    }
-
-    fn print_associated_const(
-        &mut self,
-        ident: ast::Ident,
-        ty: &ast::Ty,
-        default: Option<&ast::Expr>,
-        vis: &ast::Visibility,
-    ) {
-        self.s.word(visibility_qualified(vis, ""));
-        self.word_space("const");
-        self.print_ident(ident);
-        self.word_space(":");
-        self.print_type(ty);
-        if let Some(expr) = default {
-            self.s.space();
-            self.word_space("=");
-            self.print_expr(expr);
-        }
-        self.s.word(";")
-    }
-
-    fn print_associated_type(
-        &mut self,
-        ident: ast::Ident,
-        bounds: &ast::GenericBounds,
-        ty: Option<&ast::Ty>,
-    ) {
-        self.word_space("type");
-        self.print_ident(ident);
-        self.print_type_bounds(":", bounds);
-        if let Some(ty) = ty {
-            self.s.space();
-            self.word_space("=");
-            self.print_type(ty);
-        }
-        self.s.word(";")
-    }
-
-    /// Pretty-prints an item.
-    crate fn print_item(&mut self, item: &ast::Item) {
-        self.hardbreak_if_not_bol();
-        self.maybe_print_comment(item.span.lo());
-        self.print_outer_attributes(&item.attrs);
-        self.ann.pre(self, AnnNode::Item(item));
-        match item.kind {
-            ast::ItemKind::ExternCrate(orig_name) => {
-                self.head(visibility_qualified(&item.vis, "extern crate"));
-                if let Some(orig_name) = orig_name {
-                    self.print_name(orig_name);
-                    self.s.space();
-                    self.s.word("as");
-                    self.s.space();
-                }
-                self.print_ident(item.ident);
-                self.s.word(";");
-                self.end(); // end inner head-block
-                self.end(); // end outer head-block
-            }
-            ast::ItemKind::Use(ref tree) => {
-                self.head(visibility_qualified(&item.vis, "use"));
-                self.print_use_tree(tree);
-                self.s.word(";");
-                self.end(); // end inner head-block
-                self.end(); // end outer head-block
-            }
-            ast::ItemKind::Static(ref ty, m, ref expr) => {
-                self.head(visibility_qualified(&item.vis, "static"));
-                if m == ast::Mutability::Mut {
-                    self.word_space("mut");
-                }
-                self.print_ident(item.ident);
-                self.word_space(":");
-                self.print_type(ty);
-                self.s.space();
-                self.end(); // end the head-ibox
-
-                self.word_space("=");
-                self.print_expr(expr);
-                self.s.word(";");
-                self.end(); // end the outer cbox
-            }
-            ast::ItemKind::Const(ref ty, ref expr) => {
-                self.head(visibility_qualified(&item.vis, "const"));
-                self.print_ident(item.ident);
-                self.word_space(":");
-                self.print_type(ty);
-                self.s.space();
-                self.end(); // end the head-ibox
-
-                self.word_space("=");
-                self.print_expr(expr);
-                self.s.word(";");
-                self.end(); // end the outer cbox
-            }
-            ast::ItemKind::Fn(ref sig, ref param_names, ref body) => {
-                self.head("");
-                self.print_fn(&sig.decl, sig.header, Some(item.ident), param_names, &item.vis);
-                self.s.word(" ");
-                self.print_block_with_attrs(body, &item.attrs);
-            }
-            ast::ItemKind::Mod(ref _mod) => {
-                self.head(visibility_qualified(&item.vis, "mod"));
-                self.print_ident(item.ident);
-
-                if _mod.inline || self.is_expanded {
-                    self.nbsp();
-                    self.bopen();
-                    self.print_mod(_mod, &item.attrs);
-                    self.bclose(item.span);
-                } else {
-                    self.s.word(";");
-                    self.end(); // end inner head-block
-                    self.end(); // end outer head-block
-                }
-            }
-            ast::ItemKind::ForeignMod(ref nmod) => {
-                self.head("extern");
-                if let Some(abi) = nmod.abi {
-                    self.print_literal(&abi.as_lit());
-                    self.nbsp();
-                }
-                self.bopen();
-                self.print_foreign_mod(nmod, &item.attrs);
-                self.bclose(item.span);
-            }
-            ast::ItemKind::GlobalAsm(ref ga) => {
-                self.head(visibility_qualified(&item.vis, "global_asm!"));
-                self.s.word(ga.asm.to_string());
-                self.end();
-            }
-            ast::ItemKind::TyAlias(ref ty, ref generics) => {
-                self.head(visibility_qualified(&item.vis, "type"));
-                self.print_ident(item.ident);
-                self.print_generic_params(&generics.params);
-                self.end(); // end the inner ibox
-
-                self.print_where_clause(&generics.where_clause);
-                self.s.space();
-                self.word_space("=");
-                self.print_type(ty);
-                self.s.word(";");
-                self.end(); // end the outer ibox
-            }
-            ast::ItemKind::Enum(ref enum_definition, ref params) => {
-                self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
-            }
-            ast::ItemKind::Struct(ref struct_def, ref generics) => {
-                self.head(visibility_qualified(&item.vis, "struct"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
-            }
-            ast::ItemKind::Union(ref struct_def, ref generics) => {
-                self.head(visibility_qualified(&item.vis, "union"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
-            }
-            ast::ItemKind::Impl {
-                unsafety,
-                polarity,
-                defaultness,
-                constness,
-                ref generics,
-                ref of_trait,
-                ref self_ty,
-                ref items,
-            } => {
-                self.head("");
-                self.print_visibility(&item.vis);
-                self.print_defaultness(defaultness);
-                self.print_unsafety(unsafety);
-                self.word_nbsp("impl");
-                self.print_constness(constness);
-
-                if !generics.params.is_empty() {
-                    self.print_generic_params(&generics.params);
-                    self.s.space();
-                }
-
-                if polarity == ast::ImplPolarity::Negative {
-                    self.s.word("!");
-                }
-
-                if let Some(ref t) = *of_trait {
-                    self.print_trait_ref(t);
-                    self.s.space();
-                    self.word_space("for");
-                }
-
-                self.print_type(self_ty);
-                self.print_where_clause(&generics.where_clause);
-
-                self.s.space();
-                self.bopen();
-                self.print_inner_attributes(&item.attrs);
-                for impl_item in items {
-                    self.print_assoc_item(impl_item);
-                }
-                self.bclose(item.span);
-            }
-            ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => {
-                self.head("");
-                self.print_visibility(&item.vis);
-                self.print_unsafety(unsafety);
-                self.print_is_auto(is_auto);
-                self.word_nbsp("trait");
-                self.print_ident(item.ident);
-                self.print_generic_params(&generics.params);
-                let mut real_bounds = Vec::with_capacity(bounds.len());
-                for b in bounds.iter() {
-                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
-                        self.s.space();
-                        self.word_space("for ?");
-                        self.print_trait_ref(&ptr.trait_ref);
-                    } else {
-                        real_bounds.push(b.clone());
-                    }
-                }
-                self.print_type_bounds(":", &real_bounds[..]);
-                self.print_where_clause(&generics.where_clause);
-                self.s.word(" ");
-                self.bopen();
-                for trait_item in trait_items {
-                    self.print_assoc_item(trait_item);
-                }
-                self.bclose(item.span);
-            }
-            ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
-                self.head("");
-                self.print_visibility(&item.vis);
-                self.word_nbsp("trait");
-                self.print_ident(item.ident);
-                self.print_generic_params(&generics.params);
-                let mut real_bounds = Vec::with_capacity(bounds.len());
-                // FIXME(durka) this seems to be some quite outdated syntax
-                for b in bounds.iter() {
-                    if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
-                        self.s.space();
-                        self.word_space("for ?");
-                        self.print_trait_ref(&ptr.trait_ref);
-                    } else {
-                        real_bounds.push(b.clone());
-                    }
-                }
-                self.nbsp();
-                self.print_type_bounds("=", &real_bounds[..]);
-                self.print_where_clause(&generics.where_clause);
-                self.s.word(";");
-            }
-            ast::ItemKind::Mac(ref mac) => {
-                self.print_mac(mac);
-                if mac.args.need_semicolon() {
-                    self.s.word(";");
-                }
-            }
-            ast::ItemKind::MacroDef(ref macro_def) => {
-                let (kw, has_bang) = if macro_def.legacy {
-                    ("macro_rules", true)
-                } else {
-                    self.print_visibility(&item.vis);
-                    ("macro", false)
-                };
-                self.print_mac_common(
-                    Some(MacHeader::Keyword(kw)),
-                    has_bang,
-                    Some(item.ident),
-                    macro_def.body.delim(),
-                    macro_def.body.inner_tokens(),
-                    true,
-                    item.span,
-                );
-            }
-        }
-        self.ann.post(self, AnnNode::Item(item))
-    }
-
-    fn print_trait_ref(&mut self, t: &ast::TraitRef) {
-        self.print_path(&t.path, false, 0)
-    }
-
-    fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
-        if !generic_params.is_empty() {
-            self.s.word("for");
-            self.print_generic_params(generic_params);
-            self.nbsp();
-        }
-    }
-
-    fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
-        self.print_formal_generic_params(&t.bound_generic_params);
-        self.print_trait_ref(&t.trait_ref)
-    }
-
-    crate fn print_enum_def(
-        &mut self,
-        enum_definition: &ast::EnumDef,
-        generics: &ast::Generics,
-        ident: ast::Ident,
-        span: rustc_span::Span,
-        visibility: &ast::Visibility,
-    ) {
-        self.head(visibility_qualified(visibility, "enum"));
-        self.print_ident(ident);
-        self.print_generic_params(&generics.params);
-        self.print_where_clause(&generics.where_clause);
-        self.s.space();
-        self.print_variants(&enum_definition.variants, span)
-    }
-
-    crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
-        self.bopen();
-        for v in variants {
-            self.space_if_not_bol();
-            self.maybe_print_comment(v.span.lo());
-            self.print_outer_attributes(&v.attrs);
-            self.ibox(INDENT_UNIT);
-            self.print_variant(v);
-            self.s.word(",");
-            self.end();
-            self.maybe_print_trailing_comment(v.span, None);
-        }
-        self.bclose(span)
-    }
-
-    crate fn print_visibility(&mut self, vis: &ast::Visibility) {
-        match vis.node {
-            ast::VisibilityKind::Public => self.word_nbsp("pub"),
-            ast::VisibilityKind::Crate(sugar) => match sugar {
-                ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
-                ast::CrateSugar::JustCrate => self.word_nbsp("crate"),
-            },
-            ast::VisibilityKind::Restricted { ref path, .. } => {
-                let path = to_string(|s| s.print_path(path, false, 0));
-                if path == "self" || path == "super" {
-                    self.word_nbsp(format!("pub({})", path))
-                } else {
-                    self.word_nbsp(format!("pub(in {})", path))
-                }
-            }
-            ast::VisibilityKind::Inherited => {}
-        }
-    }
-
-    crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
-        if let ast::Defaultness::Default = defaultness {
-            self.word_nbsp("default");
-        }
-    }
-
-    crate fn print_struct(
-        &mut self,
-        struct_def: &ast::VariantData,
-        generics: &ast::Generics,
-        ident: ast::Ident,
-        span: rustc_span::Span,
-        print_finalizer: bool,
-    ) {
-        self.print_ident(ident);
-        self.print_generic_params(&generics.params);
-        match struct_def {
-            ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
-                if let ast::VariantData::Tuple(..) = struct_def {
-                    self.popen();
-                    self.commasep(Inconsistent, struct_def.fields(), |s, field| {
-                        s.maybe_print_comment(field.span.lo());
-                        s.print_outer_attributes(&field.attrs);
-                        s.print_visibility(&field.vis);
-                        s.print_type(&field.ty)
-                    });
-                    self.pclose();
-                }
-                self.print_where_clause(&generics.where_clause);
-                if print_finalizer {
-                    self.s.word(";");
-                }
-                self.end();
-                self.end(); // Close the outer-box.
-            }
-            ast::VariantData::Struct(..) => {
-                self.print_where_clause(&generics.where_clause);
-                self.nbsp();
-                self.bopen();
-                self.hardbreak_if_not_bol();
-
-                for field in struct_def.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.s.word(",");
-                }
-
-                self.bclose(span)
-            }
-        }
-    }
-
-    crate fn print_variant(&mut self, v: &ast::Variant) {
-        self.head("");
-        self.print_visibility(&v.vis);
-        let generics = ast::Generics::default();
-        self.print_struct(&v.data, &generics, v.ident, v.span, false);
-        match v.disr_expr {
-            Some(ref d) => {
-                self.s.space();
-                self.word_space("=");
-                self.print_expr(&d.value)
-            }
-            _ => {}
-        }
-    }
-
-    crate fn print_assoc_item(&mut self, item: &ast::AssocItem) {
-        self.ann.pre(self, AnnNode::SubItem(item.id));
-        self.hardbreak_if_not_bol();
-        self.maybe_print_comment(item.span.lo());
-        self.print_outer_attributes(&item.attrs);
-        self.print_defaultness(item.defaultness);
-        match &item.kind {
-            ast::AssocItemKind::Const(ty, expr) => {
-                self.print_associated_const(item.ident, ty, expr.as_deref(), &item.vis);
-            }
-            ast::AssocItemKind::Fn(sig, body) => {
-                if body.is_some() {
-                    self.head("");
-                }
-                self.print_fn(&sig.decl, sig.header, Some(item.ident), &item.generics, &item.vis);
-                if let Some(body) = body {
-                    self.nbsp();
-                    self.print_block_with_attrs(body, &item.attrs);
-                } else {
-                    self.s.word(";");
-                }
-            }
-            ast::AssocItemKind::TyAlias(bounds, ty) => {
-                self.print_associated_type(item.ident, bounds, ty.as_deref());
-            }
-            ast::AssocItemKind::Macro(mac) => {
-                self.print_mac(mac);
-                if mac.args.need_semicolon() {
-                    self.s.word(";");
-                }
-            }
-        }
-        self.ann.post(self, AnnNode::SubItem(item.id))
-    }
-
-    crate fn print_stmt(&mut self, st: &ast::Stmt) {
-        self.maybe_print_comment(st.span.lo());
-        match st.kind {
-            ast::StmtKind::Local(ref loc) => {
-                self.print_outer_attributes(&loc.attrs);
-                self.space_if_not_bol();
-                self.ibox(INDENT_UNIT);
-                self.word_nbsp("let");
-
-                self.ibox(INDENT_UNIT);
-                self.print_local_decl(loc);
-                self.end();
-                if let Some(ref init) = loc.init {
-                    self.nbsp();
-                    self.word_space("=");
-                    self.print_expr(init);
-                }
-                self.s.word(";");
-                self.end();
-            }
-            ast::StmtKind::Item(ref item) => self.print_item(item),
-            ast::StmtKind::Expr(ref expr) => {
-                self.space_if_not_bol();
-                self.print_expr_outer_attr_style(expr, false);
-                if classify::expr_requires_semi_to_be_stmt(expr) {
-                    self.s.word(";");
-                }
-            }
-            ast::StmtKind::Semi(ref expr) => {
-                match expr.kind {
-                    // Filter out empty `Tup` exprs created for the `redundant_semicolon`
-                    // lint, as they shouldn't be visible and interact poorly
-                    // with proc macros.
-                    ast::ExprKind::Tup(ref exprs) if exprs.is_empty() && expr.attrs.is_empty() => {
-                        ()
-                    }
-                    _ => {
-                        self.space_if_not_bol();
-                        self.print_expr_outer_attr_style(expr, false);
-                        self.s.word(";");
-                    }
-                }
-            }
-            ast::StmtKind::Mac(ref mac) => {
-                let (ref mac, style, ref attrs) = **mac;
-                self.space_if_not_bol();
-                self.print_outer_attributes(attrs);
-                self.print_mac(mac);
-                if style == ast::MacStmtStyle::Semicolon {
-                    self.s.word(";");
-                }
-            }
-        }
-        self.maybe_print_trailing_comment(st.span, None)
-    }
-
-    crate fn print_block(&mut self, blk: &ast::Block) {
-        self.print_block_with_attrs(blk, &[])
-    }
-
-    crate fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
-        self.print_block_maybe_unclosed(blk, &[], false)
-    }
-
-    crate fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
-        self.print_block_maybe_unclosed(blk, attrs, true)
-    }
-
-    crate fn print_block_maybe_unclosed(
-        &mut self,
-        blk: &ast::Block,
-        attrs: &[ast::Attribute],
-        close_box: bool,
-    ) {
-        match blk.rules {
-            BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
-            BlockCheckMode::Default => (),
-        }
-        self.maybe_print_comment(blk.span.lo());
-        self.ann.pre(self, AnnNode::Block(blk));
-        self.bopen();
-
-        self.print_inner_attributes(attrs);
-
-        for (i, st) in blk.stmts.iter().enumerate() {
-            match st.kind {
-                ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
-                    self.maybe_print_comment(st.span.lo());
-                    self.space_if_not_bol();
-                    self.print_expr_outer_attr_style(expr, false);
-                    self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
-                }
-                _ => self.print_stmt(st),
-            }
-        }
-
-        self.bclose_maybe_open(blk.span, close_box);
-        self.ann.post(self, AnnNode::Block(blk))
-    }
-
-    /// Print a `let pat = scrutinee` expression.
-    crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
-        self.s.word("let ");
-
-        self.print_pat(pat);
-        self.s.space();
-
-        self.word_space("=");
-        self.print_expr_cond_paren(
-            scrutinee,
-            Self::cond_needs_par(scrutinee)
-                || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()),
-        )
-    }
-
-    fn print_else(&mut self, els: Option<&ast::Expr>) {
-        if let Some(_else) = els {
-            match _else.kind {
-                // Another `else if` block.
-                ast::ExprKind::If(ref i, ref then, ref e) => {
-                    self.cbox(INDENT_UNIT - 1);
-                    self.ibox(0);
-                    self.s.word(" else if ");
-                    self.print_expr_as_cond(i);
-                    self.s.space();
-                    self.print_block(then);
-                    self.print_else(e.as_deref())
-                }
-                // Final `else` block.
-                ast::ExprKind::Block(ref b, _) => {
-                    self.cbox(INDENT_UNIT - 1);
-                    self.ibox(0);
-                    self.s.word(" else ");
-                    self.print_block(b)
-                }
-                // Constraints would be great here!
-                _ => {
-                    panic!("print_if saw if with weird alternative");
-                }
-            }
-        }
-    }
-
-    crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
-        self.head("if");
-
-        self.print_expr_as_cond(test);
-        self.s.space();
-
-        self.print_block(blk);
-        self.print_else(elseopt)
-    }
-
-    crate fn print_mac(&mut self, m: &ast::Mac) {
-        self.print_mac_common(
-            Some(MacHeader::Path(&m.path)),
-            true,
-            None,
-            m.args.delim(),
-            m.args.inner_tokens(),
-            true,
-            m.span(),
-        );
-    }
-
-    fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
-        self.popen();
-        self.commasep_exprs(Inconsistent, args);
-        self.pclose()
-    }
-
-    crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
-        self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
-    }
-
-    /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
-    /// `if cond { ... }`.
-    crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
-        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
-    }
-
-    /// Does `expr` need parenthesis when printed in a condition position?
-    fn cond_needs_par(expr: &ast::Expr) -> bool {
-        match expr.kind {
-            // These cases need parens due to the parse error observed in #26461: `if return {}`
-            // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
-            ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Break(..) => true,
-
-            _ => parser::contains_exterior_struct_lit(expr),
-        }
-    }
-
-    /// Prints `expr` or `(expr)` when `needs_par` holds.
-    fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
-        if needs_par {
-            self.popen();
-        }
-        self.print_expr(expr);
-        if needs_par {
-            self.pclose();
-        }
-    }
-
-    fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>], attrs: &[Attribute]) {
-        self.ibox(INDENT_UNIT);
-        self.s.word("[");
-        self.print_inner_attributes_inline(attrs);
-        self.commasep_exprs(Inconsistent, &exprs[..]);
-        self.s.word("]");
-        self.end();
-    }
-
-    fn print_expr_repeat(
-        &mut self,
-        element: &ast::Expr,
-        count: &ast::AnonConst,
-        attrs: &[Attribute],
-    ) {
-        self.ibox(INDENT_UNIT);
-        self.s.word("[");
-        self.print_inner_attributes_inline(attrs);
-        self.print_expr(element);
-        self.word_space(";");
-        self.print_expr(&count.value);
-        self.s.word("]");
-        self.end();
-    }
-
-    fn print_expr_struct(
-        &mut self,
-        path: &ast::Path,
-        fields: &[ast::Field],
-        wth: &Option<P<ast::Expr>>,
-        attrs: &[Attribute],
-    ) {
-        self.print_path(path, true, 0);
-        self.s.word("{");
-        self.print_inner_attributes_inline(attrs);
-        self.commasep_cmnt(
-            Consistent,
-            &fields[..],
-            |s, field| {
-                s.ibox(INDENT_UNIT);
-                if !field.is_shorthand {
-                    s.print_ident(field.ident);
-                    s.word_space(":");
-                }
-                s.print_expr(&field.expr);
-                s.end();
-            },
-            |f| f.span,
-        );
-        match *wth {
-            Some(ref expr) => {
-                self.ibox(INDENT_UNIT);
-                if !fields.is_empty() {
-                    self.s.word(",");
-                    self.s.space();
-                }
-                self.s.word("..");
-                self.print_expr(expr);
-                self.end();
-            }
-            _ => {
-                if !fields.is_empty() {
-                    self.s.word(",")
-                }
-            }
-        }
-        self.s.word("}");
-    }
-
-    fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>], attrs: &[Attribute]) {
-        self.popen();
-        self.print_inner_attributes_inline(attrs);
-        self.commasep_exprs(Inconsistent, &exprs[..]);
-        if exprs.len() == 1 {
-            self.s.word(",");
-        }
-        self.pclose()
-    }
-
-    fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
-        let prec = match func.kind {
-            ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
-            _ => parser::PREC_POSTFIX,
-        };
-
-        self.print_expr_maybe_paren(func, prec);
-        self.print_call_post(args)
-    }
-
-    fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P<ast::Expr>]) {
-        let base_args = &args[1..];
-        self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
-        self.s.word(".");
-        self.print_ident(segment.ident);
-        if let Some(ref args) = segment.args {
-            self.print_generic_args(args, true);
-        }
-        self.print_call_post(base_args)
-    }
-
-    fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
-        let assoc_op = AssocOp::from_ast_binop(op.node);
-        let prec = assoc_op.precedence() as i8;
-        let fixity = assoc_op.fixity();
-
-        let (left_prec, right_prec) = match fixity {
-            Fixity::Left => (prec, prec + 1),
-            Fixity::Right => (prec + 1, prec),
-            Fixity::None => (prec + 1, prec + 1),
-        };
-
-        let left_prec = match (&lhs.kind, op.node) {
-            // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
-            // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
-            // of `(x as i32) < ...`. We need to convince it _not_ to do that.
-            (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt)
-            | (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN,
-            // We are given `(let _ = a) OP b`.
-            //
-            // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
-            //   as the parser will interpret this as `(let _ = a) OP b`.
-            //
-            // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
-            //   parens are required since the parser would interpret `let a = b < c` as
-            //   `let a = (b < c)`. To achieve this, we force parens.
-            (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
-                parser::PREC_FORCE_PAREN
-            }
-            _ => left_prec,
-        };
-
-        self.print_expr_maybe_paren(lhs, left_prec);
-        self.s.space();
-        self.word_space(op.node.to_string());
-        self.print_expr_maybe_paren(rhs, right_prec)
-    }
-
-    fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
-        self.s.word(ast::UnOp::to_string(op));
-        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
-    }
-
-    fn print_expr_addr_of(
-        &mut self,
-        kind: ast::BorrowKind,
-        mutability: ast::Mutability,
-        expr: &ast::Expr,
-    ) {
-        self.s.word("&");
-        match kind {
-            ast::BorrowKind::Ref => self.print_mutability(mutability, false),
-            ast::BorrowKind::Raw => {
-                self.word_nbsp("raw");
-                self.print_mutability(mutability, true);
-            }
-        }
-        self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
-    }
-
-    pub fn print_expr(&mut self, expr: &ast::Expr) {
-        self.print_expr_outer_attr_style(expr, true)
-    }
-
-    fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
-        self.maybe_print_comment(expr.span.lo());
-
-        let attrs = &expr.attrs;
-        if is_inline {
-            self.print_outer_attributes_inline(attrs);
-        } else {
-            self.print_outer_attributes(attrs);
-        }
-
-        self.ibox(INDENT_UNIT);
-        self.ann.pre(self, AnnNode::Expr(expr));
-        match expr.kind {
-            ast::ExprKind::Box(ref expr) => {
-                self.word_space("box");
-                self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
-            }
-            ast::ExprKind::Array(ref exprs) => {
-                self.print_expr_vec(&exprs[..], attrs);
-            }
-            ast::ExprKind::Repeat(ref element, ref count) => {
-                self.print_expr_repeat(element, count, attrs);
-            }
-            ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
-                self.print_expr_struct(path, &fields[..], wth, attrs);
-            }
-            ast::ExprKind::Tup(ref exprs) => {
-                self.print_expr_tup(&exprs[..], attrs);
-            }
-            ast::ExprKind::Call(ref func, ref args) => {
-                self.print_expr_call(func, &args[..]);
-            }
-            ast::ExprKind::MethodCall(ref segment, ref args) => {
-                self.print_expr_method_call(segment, &args[..]);
-            }
-            ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
-                self.print_expr_binary(op, lhs, rhs);
-            }
-            ast::ExprKind::Unary(op, ref expr) => {
-                self.print_expr_unary(op, expr);
-            }
-            ast::ExprKind::AddrOf(k, m, ref expr) => {
-                self.print_expr_addr_of(k, m, expr);
-            }
-            ast::ExprKind::Lit(ref lit) => {
-                self.print_literal(lit);
-            }
-            ast::ExprKind::Cast(ref expr, ref ty) => {
-                let prec = AssocOp::As.precedence() as i8;
-                self.print_expr_maybe_paren(expr, prec);
-                self.s.space();
-                self.word_space("as");
-                self.print_type(ty);
-            }
-            ast::ExprKind::Type(ref expr, ref ty) => {
-                let prec = AssocOp::Colon.precedence() as i8;
-                self.print_expr_maybe_paren(expr, prec);
-                self.word_space(":");
-                self.print_type(ty);
-            }
-            ast::ExprKind::Let(ref pat, ref scrutinee) => {
-                self.print_let(pat, scrutinee);
-            }
-            ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
-                self.print_if(test, blk, elseopt.as_deref())
-            }
-            ast::ExprKind::While(ref test, ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.word_space(":");
-                }
-                self.head("while");
-                self.print_expr_as_cond(test);
-                self.s.space();
-                self.print_block_with_attrs(blk, attrs);
-            }
-            ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.word_space(":");
-                }
-                self.head("for");
-                self.print_pat(pat);
-                self.s.space();
-                self.word_space("in");
-                self.print_expr_as_cond(iter);
-                self.s.space();
-                self.print_block_with_attrs(blk, attrs);
-            }
-            ast::ExprKind::Loop(ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.word_space(":");
-                }
-                self.head("loop");
-                self.s.space();
-                self.print_block_with_attrs(blk, attrs);
-            }
-            ast::ExprKind::Match(ref expr, ref arms) => {
-                self.cbox(INDENT_UNIT);
-                self.ibox(INDENT_UNIT);
-                self.word_nbsp("match");
-                self.print_expr_as_cond(expr);
-                self.s.space();
-                self.bopen();
-                self.print_inner_attributes_no_trailing_hardbreak(attrs);
-                for arm in arms {
-                    self.print_arm(arm);
-                }
-                self.bclose(expr.span);
-            }
-            ast::ExprKind::Closure(
-                capture_clause,
-                asyncness,
-                movability,
-                ref decl,
-                ref body,
-                _,
-            ) => {
-                self.print_movability(movability);
-                self.print_asyncness(asyncness);
-                self.print_capture_clause(capture_clause);
-
-                self.print_fn_params_and_ret(decl, true);
-                self.s.space();
-                self.print_expr(body);
-                self.end(); // need to close a box
-
-                // 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);
-            }
-            ast::ExprKind::Block(ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.word_space(":");
-                }
-                // containing cbox, will be closed by print-block at }
-                self.cbox(INDENT_UNIT);
-                // head-box, will be closed by print-block after {
-                self.ibox(0);
-                self.print_block_with_attrs(blk, attrs);
-            }
-            ast::ExprKind::Async(capture_clause, _, ref blk) => {
-                self.word_nbsp("async");
-                self.print_capture_clause(capture_clause);
-                self.s.space();
-                // cbox/ibox in analogy to the `ExprKind::Block` arm above
-                self.cbox(INDENT_UNIT);
-                self.ibox(0);
-                self.print_block_with_attrs(blk, attrs);
-            }
-            ast::ExprKind::Await(ref expr) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
-                self.s.word(".await");
-            }
-            ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
-                let prec = AssocOp::Assign.precedence() as i8;
-                self.print_expr_maybe_paren(lhs, prec + 1);
-                self.s.space();
-                self.word_space("=");
-                self.print_expr_maybe_paren(rhs, prec);
-            }
-            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
-                let prec = AssocOp::Assign.precedence() as i8;
-                self.print_expr_maybe_paren(lhs, prec + 1);
-                self.s.space();
-                self.s.word(op.node.to_string());
-                self.word_space("=");
-                self.print_expr_maybe_paren(rhs, prec);
-            }
-            ast::ExprKind::Field(ref expr, ident) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
-                self.s.word(".");
-                self.print_ident(ident);
-            }
-            ast::ExprKind::Index(ref expr, ref index) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
-                self.s.word("[");
-                self.print_expr(index);
-                self.s.word("]");
-            }
-            ast::ExprKind::Range(ref start, ref end, limits) => {
-                // Special case for `Range`.  `AssocOp` claims that `Range` has higher precedence
-                // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
-                // Here we use a fake precedence value so that any child with lower precedence than
-                // a "normal" binop gets parenthesized.  (`LOr` is the lowest-precedence binop.)
-                let fake_prec = AssocOp::LOr.precedence() as i8;
-                if let Some(ref e) = *start {
-                    self.print_expr_maybe_paren(e, fake_prec);
-                }
-                if limits == ast::RangeLimits::HalfOpen {
-                    self.s.word("..");
-                } else {
-                    self.s.word("..=");
-                }
-                if let Some(ref e) = *end {
-                    self.print_expr_maybe_paren(e, fake_prec);
-                }
-            }
-            ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
-            ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
-            ast::ExprKind::Break(opt_label, ref opt_expr) => {
-                self.s.word("break");
-                self.s.space();
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.s.space();
-                }
-                if let Some(ref expr) = *opt_expr {
-                    self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
-                    self.s.space();
-                }
-            }
-            ast::ExprKind::Continue(opt_label) => {
-                self.s.word("continue");
-                self.s.space();
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.s.space()
-                }
-            }
-            ast::ExprKind::Ret(ref result) => {
-                self.s.word("return");
-                if let Some(ref expr) = *result {
-                    self.s.word(" ");
-                    self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
-                }
-            }
-            ast::ExprKind::InlineAsm(ref a) => {
-                self.s.word("asm!");
-                self.popen();
-                self.print_string(&a.asm.as_str(), a.asm_str_style);
-                self.word_space(":");
-
-                self.commasep(Inconsistent, &a.outputs, |s, out| {
-                    let constraint = out.constraint.as_str();
-                    let mut ch = constraint.chars();
-                    match ch.next() {
-                        Some('=') if out.is_rw => {
-                            s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked)
-                        }
-                        _ => s.print_string(&constraint, ast::StrStyle::Cooked),
-                    }
-                    s.popen();
-                    s.print_expr(&out.expr);
-                    s.pclose();
-                });
-                self.s.space();
-                self.word_space(":");
-
-                self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
-                    s.print_string(&co.as_str(), ast::StrStyle::Cooked);
-                    s.popen();
-                    s.print_expr(o);
-                    s.pclose();
-                });
-                self.s.space();
-                self.word_space(":");
-
-                self.commasep(Inconsistent, &a.clobbers, |s, co| {
-                    s.print_string(&co.as_str(), ast::StrStyle::Cooked);
-                });
-
-                let mut options = vec![];
-                if a.volatile {
-                    options.push("volatile");
-                }
-                if a.alignstack {
-                    options.push("alignstack");
-                }
-                if a.dialect == ast::AsmDialect::Intel {
-                    options.push("intel");
-                }
-
-                if !options.is_empty() {
-                    self.s.space();
-                    self.word_space(":");
-                    self.commasep(Inconsistent, &options, |s, &co| {
-                        s.print_string(co, ast::StrStyle::Cooked);
-                    });
-                }
-
-                self.pclose();
-            }
-            ast::ExprKind::Mac(ref m) => self.print_mac(m),
-            ast::ExprKind::Paren(ref e) => {
-                self.popen();
-                self.print_inner_attributes_inline(attrs);
-                self.print_expr(e);
-                self.pclose();
-            }
-            ast::ExprKind::Yield(ref e) => {
-                self.s.word("yield");
-                match *e {
-                    Some(ref expr) => {
-                        self.s.space();
-                        self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
-                    }
-                    _ => (),
-                }
-            }
-            ast::ExprKind::Try(ref e) => {
-                self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
-                self.s.word("?")
-            }
-            ast::ExprKind::TryBlock(ref blk) => {
-                self.head("try");
-                self.s.space();
-                self.print_block_with_attrs(blk, attrs)
-            }
-            ast::ExprKind::Err => {
-                self.popen();
-                self.s.word("/*ERROR*/");
-                self.pclose()
-            }
-        }
-        self.ann.post(self, AnnNode::Expr(expr));
-        self.end();
-    }
-
-    crate fn print_local_decl(&mut self, loc: &ast::Local) {
-        self.print_pat(&loc.pat);
-        if let Some(ref ty) = loc.ty {
-            self.word_space(":");
-            self.print_type(ty);
-        }
-    }
-
-    pub fn print_usize(&mut self, i: usize) {
-        self.s.word(i.to_string())
-    }
-
-    crate fn print_name(&mut self, name: ast::Name) {
-        self.s.word(name.to_string());
-        self.ann.post(self, AnnNode::Name(&name))
-    }
-
-    fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
-        self.s.word("<");
-        self.print_type(&qself.ty);
-        if qself.position > 0 {
-            self.s.space();
-            self.word_space("as");
-            let depth = path.segments.len() - qself.position;
-            self.print_path(path, false, depth);
-        }
-        self.s.word(">");
-        self.s.word("::");
-        let item_segment = path.segments.last().unwrap();
-        self.print_ident(item_segment.ident);
-        match item_segment.args {
-            Some(ref args) => self.print_generic_args(args, colons_before_params),
-            None => {}
-        }
-    }
-
-    crate fn print_pat(&mut self, pat: &ast::Pat) {
-        self.maybe_print_comment(pat.span.lo());
-        self.ann.pre(self, AnnNode::Pat(pat));
-        /* Pat isn't normalized, but the beauty of it
-        is that it doesn't matter */
-        match pat.kind {
-            PatKind::Wild => self.s.word("_"),
-            PatKind::Ident(binding_mode, ident, ref sub) => {
-                match binding_mode {
-                    ast::BindingMode::ByRef(mutbl) => {
-                        self.word_nbsp("ref");
-                        self.print_mutability(mutbl, false);
-                    }
-                    ast::BindingMode::ByValue(ast::Mutability::Not) => {}
-                    ast::BindingMode::ByValue(ast::Mutability::Mut) => {
-                        self.word_nbsp("mut");
-                    }
-                }
-                self.print_ident(ident);
-                if let Some(ref p) = *sub {
-                    self.s.space();
-                    self.s.word_space("@");
-                    self.print_pat(p);
-                }
-            }
-            PatKind::TupleStruct(ref path, ref elts) => {
-                self.print_path(path, true, 0);
-                self.popen();
-                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
-                self.pclose();
-            }
-            PatKind::Or(ref pats) => {
-                self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
-            }
-            PatKind::Path(None, ref path) => {
-                self.print_path(path, true, 0);
-            }
-            PatKind::Path(Some(ref qself), ref path) => {
-                self.print_qpath(path, qself, false);
-            }
-            PatKind::Struct(ref path, ref fields, etc) => {
-                self.print_path(path, true, 0);
-                self.nbsp();
-                self.word_space("{");
-                self.commasep_cmnt(
-                    Consistent,
-                    &fields[..],
-                    |s, f| {
-                        s.cbox(INDENT_UNIT);
-                        if !f.is_shorthand {
-                            s.print_ident(f.ident);
-                            s.word_nbsp(":");
-                        }
-                        s.print_pat(&f.pat);
-                        s.end();
-                    },
-                    |f| f.pat.span,
-                );
-                if etc {
-                    if !fields.is_empty() {
-                        self.word_space(",");
-                    }
-                    self.s.word("..");
-                }
-                self.s.space();
-                self.s.word("}");
-            }
-            PatKind::Tuple(ref elts) => {
-                self.popen();
-                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
-                if elts.len() == 1 {
-                    self.s.word(",");
-                }
-                self.pclose();
-            }
-            PatKind::Box(ref inner) => {
-                self.s.word("box ");
-                self.print_pat(inner);
-            }
-            PatKind::Ref(ref inner, mutbl) => {
-                self.s.word("&");
-                if mutbl == ast::Mutability::Mut {
-                    self.s.word("mut ");
-                }
-                self.print_pat(inner);
-            }
-            PatKind::Lit(ref e) => self.print_expr(&**e),
-            PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
-                if let Some(e) = begin {
-                    self.print_expr(e);
-                    self.s.space();
-                }
-                match *end_kind {
-                    RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
-                    RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
-                    RangeEnd::Excluded => self.s.word(".."),
-                }
-                if let Some(e) = end {
-                    self.print_expr(e);
-                }
-            }
-            PatKind::Slice(ref elts) => {
-                self.s.word("[");
-                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
-                self.s.word("]");
-            }
-            PatKind::Rest => self.s.word(".."),
-            PatKind::Paren(ref inner) => {
-                self.popen();
-                self.print_pat(inner);
-                self.pclose();
-            }
-            PatKind::Mac(ref m) => self.print_mac(m),
-        }
-        self.ann.post(self, AnnNode::Pat(pat))
-    }
-
-    fn print_arm(&mut self, arm: &ast::Arm) {
-        // Note, I have no idea why this check is necessary, but here it is.
-        if arm.attrs.is_empty() {
-            self.s.space();
-        }
-        self.cbox(INDENT_UNIT);
-        self.ibox(0);
-        self.maybe_print_comment(arm.pat.span.lo());
-        self.print_outer_attributes(&arm.attrs);
-        self.print_pat(&arm.pat);
-        self.s.space();
-        if let Some(ref e) = arm.guard {
-            self.word_space("if");
-            self.print_expr(e);
-            self.s.space();
-        }
-        self.word_space("=>");
-
-        match arm.body.kind {
-            ast::ExprKind::Block(ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident);
-                    self.word_space(":");
-                }
-
-                // The block will close the pattern's ibox.
-                self.print_block_unclosed_indent(blk);
-
-                // If it is a user-provided unsafe block, print a comma after it.
-                if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
-                    self.s.word(",");
-                }
-            }
-            _ => {
-                self.end(); // Close the ibox for the pattern.
-                self.print_expr(&arm.body);
-                self.s.word(",");
-            }
-        }
-        self.end(); // Close enclosing cbox.
-    }
-
-    fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
-        match explicit_self.node {
-            SelfKind::Value(m) => {
-                self.print_mutability(m, false);
-                self.s.word("self")
-            }
-            SelfKind::Region(ref lt, m) => {
-                self.s.word("&");
-                self.print_opt_lifetime(lt);
-                self.print_mutability(m, false);
-                self.s.word("self")
-            }
-            SelfKind::Explicit(ref typ, m) => {
-                self.print_mutability(m, false);
-                self.s.word("self");
-                self.word_space(":");
-                self.print_type(typ)
-            }
-        }
-    }
-
-    crate fn print_fn(
-        &mut self,
-        decl: &ast::FnDecl,
-        header: ast::FnHeader,
-        name: Option<ast::Ident>,
-        generics: &ast::Generics,
-        vis: &ast::Visibility,
-    ) {
-        self.print_fn_header_info(header, vis);
-
-        if let Some(name) = name {
-            self.nbsp();
-            self.print_ident(name);
-        }
-        self.print_generic_params(&generics.params);
-        self.print_fn_params_and_ret(decl, false);
-        self.print_where_clause(&generics.where_clause)
-    }
-
-    crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
-        let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") };
-        self.word(open);
-        self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure));
-        self.word(close);
-        self.print_fn_ret_ty(&decl.output)
-    }
-
-    crate fn print_movability(&mut self, movability: ast::Movability) {
-        match movability {
-            ast::Movability::Static => self.word_space("static"),
-            ast::Movability::Movable => {}
-        }
-    }
-
-    crate fn print_asyncness(&mut self, asyncness: ast::IsAsync) {
-        if asyncness.is_async() {
-            self.word_nbsp("async");
-        }
-    }
-
-    crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
-        match capture_clause {
-            ast::CaptureBy::Value => self.word_space("move"),
-            ast::CaptureBy::Ref => {}
-        }
-    }
-
-    pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) {
-        if !bounds.is_empty() {
-            self.s.word(prefix);
-            let mut first = true;
-            for bound in bounds {
-                if !(first && prefix.is_empty()) {
-                    self.nbsp();
-                }
-                if first {
-                    first = false;
-                } else {
-                    self.word_space("+");
-                }
-
-                match bound {
-                    GenericBound::Trait(tref, modifier) => {
-                        if modifier == &TraitBoundModifier::Maybe {
-                            self.s.word("?");
-                        }
-                        self.print_poly_trait_ref(tref);
-                    }
-                    GenericBound::Outlives(lt) => self.print_lifetime(*lt),
-                }
-            }
-        }
-    }
-
-    crate fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
-        self.print_name(lifetime.ident.name)
-    }
-
-    crate fn print_lifetime_bounds(
-        &mut self,
-        lifetime: ast::Lifetime,
-        bounds: &ast::GenericBounds,
-    ) {
-        self.print_lifetime(lifetime);
-        if !bounds.is_empty() {
-            self.s.word(": ");
-            for (i, bound) in bounds.iter().enumerate() {
-                if i != 0 {
-                    self.s.word(" + ");
-                }
-                match bound {
-                    ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
-                    _ => panic!(),
-                }
-            }
-        }
-    }
-
-    crate fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
-        if generic_params.is_empty() {
-            return;
-        }
-
-        self.s.word("<");
-
-        self.commasep(Inconsistent, &generic_params, |s, param| {
-            s.print_outer_attributes_inline(&param.attrs);
-
-            match param.kind {
-                ast::GenericParamKind::Lifetime => {
-                    let lt = ast::Lifetime { id: param.id, ident: param.ident };
-                    s.print_lifetime_bounds(lt, &param.bounds)
-                }
-                ast::GenericParamKind::Type { ref default } => {
-                    s.print_ident(param.ident);
-                    s.print_type_bounds(":", &param.bounds);
-                    if let Some(ref default) = default {
-                        s.s.space();
-                        s.word_space("=");
-                        s.print_type(default)
-                    }
-                }
-                ast::GenericParamKind::Const { ref ty } => {
-                    s.word_space("const");
-                    s.print_ident(param.ident);
-                    s.s.space();
-                    s.word_space(":");
-                    s.print_type(ty);
-                    s.print_type_bounds(":", &param.bounds)
-                }
-            }
-        });
-
-        self.s.word(">");
-    }
-
-    crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
-        if where_clause.predicates.is_empty() {
-            return;
-        }
-
-        self.s.space();
-        self.word_space("where");
-
-        for (i, predicate) in where_clause.predicates.iter().enumerate() {
-            if i != 0 {
-                self.word_space(",");
-            }
-
-            match *predicate {
-                ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                    ref bound_generic_params,
-                    ref bounded_ty,
-                    ref bounds,
-                    ..
-                }) => {
-                    self.print_formal_generic_params(bound_generic_params);
-                    self.print_type(bounded_ty);
-                    self.print_type_bounds(":", bounds);
-                }
-                ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
-                    ref lifetime,
-                    ref bounds,
-                    ..
-                }) => {
-                    self.print_lifetime_bounds(*lifetime, bounds);
-                }
-                ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
-                    ref lhs_ty,
-                    ref rhs_ty,
-                    ..
-                }) => {
-                    self.print_type(lhs_ty);
-                    self.s.space();
-                    self.word_space("=");
-                    self.print_type(rhs_ty);
-                }
-            }
-        }
-    }
-
-    crate fn print_use_tree(&mut self, tree: &ast::UseTree) {
-        match tree.kind {
-            ast::UseTreeKind::Simple(rename, ..) => {
-                self.print_path(&tree.prefix, false, 0);
-                if let Some(rename) = rename {
-                    self.s.space();
-                    self.word_space("as");
-                    self.print_ident(rename);
-                }
-            }
-            ast::UseTreeKind::Glob => {
-                if !tree.prefix.segments.is_empty() {
-                    self.print_path(&tree.prefix, false, 0);
-                    self.s.word("::");
-                }
-                self.s.word("*");
-            }
-            ast::UseTreeKind::Nested(ref items) => {
-                if tree.prefix.segments.is_empty() {
-                    self.s.word("{");
-                } else {
-                    self.print_path(&tree.prefix, false, 0);
-                    self.s.word("::{");
-                }
-                self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| {
-                    this.print_use_tree(tree)
-                });
-                self.s.word("}");
-            }
-        }
-    }
-
-    pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
-        match mutbl {
-            ast::Mutability::Mut => self.word_nbsp("mut"),
-            ast::Mutability::Not => {
-                if print_const {
-                    self.word_nbsp("const");
-                }
-            }
-        }
-    }
-
-    crate fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
-        self.print_mutability(mt.mutbl, print_const);
-        self.print_type(&mt.ty)
-    }
-
-    crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
-        self.ibox(INDENT_UNIT);
-
-        self.print_outer_attributes_inline(&input.attrs);
-
-        match input.ty.kind {
-            ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
-            _ => {
-                if let Some(eself) = input.to_self() {
-                    self.print_explicit_self(&eself);
-                } else {
-                    let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
-                        ident.name == kw::Invalid
-                    } else {
-                        false
-                    };
-                    if !invalid {
-                        self.print_pat(&input.pat);
-                        self.s.word(":");
-                        self.s.space();
-                    }
-                    self.print_type(&input.ty);
-                }
-            }
-        }
-        self.end();
-    }
-
-    crate fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FunctionRetTy) {
-        if let ast::FunctionRetTy::Ty(ty) = fn_ret_ty {
-            self.space_if_not_bol();
-            self.ibox(INDENT_UNIT);
-            self.word_space("->");
-            self.print_type(ty);
-            self.end();
-            self.maybe_print_comment(ty.span.lo());
-        }
-    }
-
-    crate fn print_ty_fn(
-        &mut self,
-        ext: ast::Extern,
-        unsafety: ast::Unsafety,
-        decl: &ast::FnDecl,
-        name: Option<ast::Ident>,
-        generic_params: &[ast::GenericParam],
-    ) {
-        self.ibox(INDENT_UNIT);
-        if !generic_params.is_empty() {
-            self.s.word("for");
-            self.print_generic_params(generic_params);
-        }
-        let generics = ast::Generics {
-            params: Vec::new(),
-            where_clause: ast::WhereClause { predicates: Vec::new(), span: rustc_span::DUMMY_SP },
-            span: rustc_span::DUMMY_SP,
-        };
-        self.print_fn(
-            decl,
-            ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() },
-            name,
-            &generics,
-            &dummy_spanned(ast::VisibilityKind::Inherited),
-        );
-        self.end();
-    }
-
-    crate fn maybe_print_trailing_comment(
-        &mut self,
-        span: rustc_span::Span,
-        next_pos: Option<BytePos>,
-    ) {
-        if let Some(cmnts) = self.comments() {
-            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
-                self.print_comment(&cmnt);
-            }
-        }
-    }
-
-    crate fn print_remaining_comments(&mut self) {
-        // If there aren't any remaining comments, then we need to manually
-        // make sure there is a line break at the end.
-        if self.next_comment().is_none() {
-            self.s.hardbreak();
-        }
-        while let Some(ref cmnt) = self.next_comment() {
-            self.print_comment(cmnt);
-        }
-    }
-
-    crate fn print_fn_header_info(&mut self, header: ast::FnHeader, vis: &ast::Visibility) {
-        self.s.word(visibility_qualified(vis, ""));
-
-        match header.constness.node {
-            ast::Constness::NotConst => {}
-            ast::Constness::Const => self.word_nbsp("const"),
-        }
-
-        self.print_asyncness(header.asyncness.node);
-        self.print_unsafety(header.unsafety);
-
-        match header.ext {
-            ast::Extern::None => {}
-            ast::Extern::Implicit => {
-                self.word_nbsp("extern");
-            }
-            ast::Extern::Explicit(abi) => {
-                self.word_nbsp("extern");
-                self.print_literal(&abi.as_lit());
-                self.nbsp();
-            }
-        }
-
-        self.s.word("fn")
-    }
-
-    crate fn print_unsafety(&mut self, s: ast::Unsafety) {
-        match s {
-            ast::Unsafety::Normal => {}
-            ast::Unsafety::Unsafe => self.word_nbsp("unsafe"),
-        }
-    }
-
-    crate fn print_constness(&mut self, s: ast::Constness) {
-        match s {
-            ast::Constness::Const => self.word_nbsp("const"),
-            ast::Constness::NotConst => {}
-        }
-    }
-
-    crate fn print_is_auto(&mut self, s: ast::IsAuto) {
-        match s {
-            ast::IsAuto::Yes => self.word_nbsp("auto"),
-            ast::IsAuto::No => {}
-        }
-    }
-}
diff --git a/src/libsyntax/print/pprust/tests.rs b/src/libsyntax/print/pprust/tests.rs
deleted file mode 100644 (file)
index 3091e31..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-use super::*;
-
-use crate::ast;
-use crate::with_default_globals;
-use rustc_span;
-use rustc_span::source_map::{dummy_spanned, respan};
-
-fn fun_to_string(
-    decl: &ast::FnDecl,
-    header: ast::FnHeader,
-    name: ast::Ident,
-    generics: &ast::Generics,
-) -> String {
-    to_string(|s| {
-        s.head("");
-        s.print_fn(
-            decl,
-            header,
-            Some(name),
-            generics,
-            &dummy_spanned(ast::VisibilityKind::Inherited),
-        );
-        s.end(); // Close the head box.
-        s.end(); // Close the outer box.
-    })
-}
-
-fn variant_to_string(var: &ast::Variant) -> String {
-    to_string(|s| s.print_variant(var))
-}
-
-#[test]
-fn test_fun_to_string() {
-    with_default_globals(|| {
-        let abba_ident = ast::Ident::from_str("abba");
-
-        let decl = ast::FnDecl {
-            inputs: Vec::new(),
-            output: ast::FunctionRetTy::Default(rustc_span::DUMMY_SP),
-        };
-        let generics = ast::Generics::default();
-        assert_eq!(
-            fun_to_string(&decl, ast::FnHeader::default(), abba_ident, &generics),
-            "fn abba()"
-        );
-    })
-}
-
-#[test]
-fn test_variant_to_string() {
-    with_default_globals(|| {
-        let ident = ast::Ident::from_str("principal_skinner");
-
-        let var = ast::Variant {
-            ident,
-            vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited),
-            attrs: Vec::new(),
-            id: ast::DUMMY_NODE_ID,
-            data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
-            disr_expr: None,
-            span: rustc_span::DUMMY_SP,
-            is_placeholder: false,
-        };
-
-        let varstr = variant_to_string(&var);
-        assert_eq!(varstr, "principal_skinner");
-    })
-}
index de33189884c3a132793589e1ddb1688bedfc9437..5a67531624d292003bbf19b9e1321478341c3b73 100644 (file)
@@ -1,13 +1,11 @@
 pub use CommentStyle::*;
 
 use crate::ast;
-
 use rustc_span::source_map::SourceMap;
 use rustc_span::{BytePos, CharPos, FileName, Pos};
 
-use std::usize;
-
 use log::debug;
+use std::usize;
 
 #[cfg(test)]
 mod tests;
@@ -190,7 +188,7 @@ fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec<String> {
 
 // it appears this function is called only from pprust... that's
 // probably not a good thing.
-crate fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment> {
+pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment> {
     let cm = SourceMap::new(sm.path_mapping().clone());
     let source_file = cm.new_source_file(path, src);
     let text = (*source_file.src.as_ref().unwrap()).clone();
index a0ed89a9caef17f04e0181bca05b176774b0e65e..b98cc96b3c64798ac4e95f55a29facdc80324ccf 100644 (file)
@@ -367,7 +367,7 @@ pub fn prec_let_scrutinee_needs_par() -> usize {
 ///
 /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
 /// Can we print this as `let _ = a OP b`?
-crate fn needs_par_as_let_scrutinee(order: i8) -> bool {
+pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
     order <= prec_let_scrutinee_needs_par() as i8
 }