rustc_ast_pretty/pprust/
state.rs

1//! AST pretty printing.
2//!
3//! Note that HIR pretty printing is layered on top of this crate.
4
5mod expr;
6mod fixup;
7mod item;
8
9use std::borrow::Cow;
10use std::sync::Arc;
11
12use rustc_ast::attr::AttrIdGenerator;
13use rustc_ast::ptr::P;
14use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind};
15use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
16use rustc_ast::util::classify;
17use rustc_ast::util::comments::{Comment, CommentStyle};
18use rustc_ast::{
19    self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound,
20    InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind,
21    RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr,
22};
23use rustc_span::edition::Edition;
24use rustc_span::source_map::{SourceMap, Spanned};
25use rustc_span::symbol::IdentPrinter;
26use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
27
28use crate::pp::Breaks::{Consistent, Inconsistent};
29use crate::pp::{self, Breaks};
30use crate::pprust::state::fixup::FixupContext;
31
32pub enum MacHeader<'a> {
33    Path(&'a ast::Path),
34    Keyword(&'static str),
35}
36
37pub enum AnnNode<'a> {
38    Ident(&'a Ident),
39    Name(&'a Symbol),
40    Block(&'a ast::Block),
41    Item(&'a ast::Item),
42    SubItem(ast::NodeId),
43    Expr(&'a ast::Expr),
44    Pat(&'a ast::Pat),
45    Crate(&'a ast::Crate),
46}
47
48pub trait PpAnn {
49    fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
50    fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
51}
52
53struct NoAnn;
54
55impl PpAnn for NoAnn {}
56
57pub struct Comments<'a> {
58    sm: &'a SourceMap,
59    // Stored in reverse order so we can consume them by popping.
60    reversed_comments: Vec<Comment>,
61}
62
63/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
64/// Otherwise returns `Some(k)` where `k` is first char offset after that leading
65/// whitespace. Note that `k` may be outside bounds of `s`.
66fn all_whitespace(s: &str, col: CharPos) -> Option<usize> {
67    let mut idx = 0;
68    for (i, ch) in s.char_indices().take(col.to_usize()) {
69        if !ch.is_whitespace() {
70            return None;
71        }
72        idx = i + ch.len_utf8();
73    }
74    Some(idx)
75}
76
77fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str {
78    let len = s.len();
79    match all_whitespace(s, col) {
80        Some(col) => {
81            if col < len {
82                &s[col..]
83            } else {
84                ""
85            }
86        }
87        None => s,
88    }
89}
90
91fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec<String> {
92    let mut res: Vec<String> = vec![];
93    let mut lines = text.lines();
94    // just push the first line
95    res.extend(lines.next().map(|it| it.to_string()));
96    // for other lines, strip common whitespace prefix
97    for line in lines {
98        res.push(trim_whitespace_prefix(line, col).to_string())
99    }
100    res
101}
102
103fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment> {
104    let sm = SourceMap::new(sm.path_mapping().clone());
105    let source_file = sm.new_source_file(path, src);
106    let text = Arc::clone(&(*source_file.src.as_ref().unwrap()));
107
108    let text: &str = text.as_str();
109    let start_bpos = source_file.start_pos;
110    let mut pos = 0;
111    let mut comments: Vec<Comment> = Vec::new();
112    let mut code_to_the_left = false;
113
114    if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
115        comments.push(Comment {
116            style: CommentStyle::Isolated,
117            lines: vec![text[..shebang_len].to_string()],
118            pos: start_bpos,
119        });
120        pos += shebang_len;
121    }
122
123    for token in rustc_lexer::tokenize(&text[pos..]) {
124        let token_text = &text[pos..pos + token.len as usize];
125        match token.kind {
126            rustc_lexer::TokenKind::Whitespace => {
127                if let Some(mut idx) = token_text.find('\n') {
128                    code_to_the_left = false;
129                    while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
130                        idx += 1 + next_newline;
131                        comments.push(Comment {
132                            style: CommentStyle::BlankLine,
133                            lines: vec![],
134                            pos: start_bpos + BytePos((pos + idx) as u32),
135                        });
136                    }
137                }
138            }
139            rustc_lexer::TokenKind::BlockComment { doc_style, .. } => {
140                if doc_style.is_none() {
141                    let code_to_the_right = !matches!(
142                        text[pos + token.len as usize..].chars().next(),
143                        Some('\r' | '\n')
144                    );
145                    let style = match (code_to_the_left, code_to_the_right) {
146                        (_, true) => CommentStyle::Mixed,
147                        (false, false) => CommentStyle::Isolated,
148                        (true, false) => CommentStyle::Trailing,
149                    };
150
151                    // Count the number of chars since the start of the line by rescanning.
152                    let pos_in_file = start_bpos + BytePos(pos as u32);
153                    let line_begin_in_file = source_file.line_begin_pos(pos_in_file);
154                    let line_begin_pos = (line_begin_in_file - start_bpos).to_usize();
155                    let col = CharPos(text[line_begin_pos..pos].chars().count());
156
157                    let lines = split_block_comment_into_lines(token_text, col);
158                    comments.push(Comment { style, lines, pos: pos_in_file })
159                }
160            }
161            rustc_lexer::TokenKind::LineComment { doc_style } => {
162                if doc_style.is_none() {
163                    comments.push(Comment {
164                        style: if code_to_the_left {
165                            CommentStyle::Trailing
166                        } else {
167                            CommentStyle::Isolated
168                        },
169                        lines: vec![token_text.to_string()],
170                        pos: start_bpos + BytePos(pos as u32),
171                    })
172                }
173            }
174            _ => {
175                code_to_the_left = true;
176            }
177        }
178        pos += token.len as usize;
179    }
180
181    comments
182}
183
184impl<'a> Comments<'a> {
185    pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
186        let mut comments = gather_comments(sm, filename, input);
187        comments.reverse();
188        Comments { sm, reversed_comments: comments }
189    }
190
191    fn peek(&self) -> Option<&Comment> {
192        self.reversed_comments.last()
193    }
194
195    fn next(&mut self) -> Option<Comment> {
196        self.reversed_comments.pop()
197    }
198
199    fn trailing_comment(
200        &mut self,
201        span: rustc_span::Span,
202        next_pos: Option<BytePos>,
203    ) -> Option<Comment> {
204        if let Some(cmnt) = self.peek() {
205            if cmnt.style != CommentStyle::Trailing {
206                return None;
207            }
208            let span_line = self.sm.lookup_char_pos(span.hi());
209            let comment_line = self.sm.lookup_char_pos(cmnt.pos);
210            let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
211            if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
212                return Some(self.next().unwrap());
213            }
214        }
215
216        None
217    }
218}
219
220pub struct State<'a> {
221    pub s: pp::Printer,
222    comments: Option<Comments<'a>>,
223    ann: &'a (dyn PpAnn + 'a),
224}
225
226const INDENT_UNIT: isize = 4;
227
228/// Requires you to pass an input filename and reader so that
229/// it can scan the input text for comments to copy forward.
230pub fn print_crate<'a>(
231    sm: &'a SourceMap,
232    krate: &ast::Crate,
233    filename: FileName,
234    input: String,
235    ann: &'a dyn PpAnn,
236    is_expanded: bool,
237    edition: Edition,
238    g: &AttrIdGenerator,
239) -> String {
240    let mut s =
241        State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
242
243    if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
244        // We need to print `#![no_std]` (and its feature gate) so that
245        // compiling pretty-printed source won't inject libstd again.
246        // However, we don't want these attributes in the AST because
247        // of the feature gate, so we fake them up here.
248
249        // `#![feature(prelude_import)]`
250        let fake_attr = attr::mk_attr_nested_word(
251            g,
252            ast::AttrStyle::Inner,
253            Safety::Default,
254            sym::feature,
255            sym::prelude_import,
256            DUMMY_SP,
257        );
258        s.print_attribute(&fake_attr);
259
260        // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
261        // root, so this is not needed, and actually breaks things.
262        if edition.is_rust_2015() {
263            // `#![no_std]`
264            let fake_attr = attr::mk_attr_word(
265                g,
266                ast::AttrStyle::Inner,
267                Safety::Default,
268                sym::no_std,
269                DUMMY_SP,
270            );
271            s.print_attribute(&fake_attr);
272        }
273    }
274
275    s.print_inner_attributes(&krate.attrs);
276    for item in &krate.items {
277        s.print_item(item);
278    }
279    s.print_remaining_comments();
280    s.ann.post(&mut s, AnnNode::Crate(krate));
281    s.s.eof()
282}
283
284/// Should two consecutive tokens be printed with a space between them?
285///
286/// Note: some old proc macros parse pretty-printed output, so changes here can
287/// break old code. For example:
288/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
289/// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]`
290///
291fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
292    use Delimiter::*;
293    use TokenTree::{Delimited as Del, Token as Tok};
294    use token::*;
295
296    fn is_punct(tt: &TokenTree) -> bool {
297        matches!(tt, TokenTree::Token(tok, _) if tok.is_punct())
298    }
299
300    // Each match arm has one or more examples in comments. The default is to
301    // insert space between adjacent tokens, except for the cases listed in
302    // this match.
303    match (tt1, tt2) {
304        // No space after line doc comments.
305        (Tok(Token { kind: DocComment(CommentKind::Line, ..), .. }, _), _) => false,
306
307        // `.` + NON-PUNCT: `x.y`, `tup.0`
308        (Tok(Token { kind: Dot, .. }, _), tt2) if !is_punct(tt2) => false,
309
310        // `$` + IDENT: `$e`
311        (Tok(Token { kind: Dollar, .. }, _), Tok(Token { kind: Ident(..), .. }, _)) => false,
312
313        // NON-PUNCT + `,`: `foo,`
314        // NON-PUNCT + `;`: `x = 3;`, `[T; 3]`
315        // NON-PUNCT + `.`: `x.y`, `tup.0`
316        (tt1, Tok(Token { kind: Comma | Semi | Dot, .. }, _)) if !is_punct(tt1) => false,
317
318        // IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
319        (Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Bang, .. }, _))
320            if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, IdentIsRaw::Yes) =>
321        {
322            false
323        }
324
325        // IDENT|`fn`|`Self`|`pub` + `(`: `f(3)`, `fn(x: u8)`, `Self()`, `pub(crate)`,
326        //      but `let (a, b) = (1, 2)` needs a space after the `let`
327        (Tok(Token { kind: Ident(sym, is_raw), span }, _), Del(_, _, Parenthesis, _))
328            if !Ident::new(*sym, *span).is_reserved()
329                || *sym == kw::Fn
330                || *sym == kw::SelfUpper
331                || *sym == kw::Pub
332                || matches!(is_raw, IdentIsRaw::Yes) =>
333        {
334            false
335        }
336
337        // `#` + `[`: `#[attr]`
338        (Tok(Token { kind: Pound, .. }, _), Del(_, _, Bracket, _)) => false,
339
340        _ => true,
341    }
342}
343
344pub fn doc_comment_to_string(
345    comment_kind: CommentKind,
346    attr_style: ast::AttrStyle,
347    data: Symbol,
348) -> String {
349    match (comment_kind, attr_style) {
350        (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
351        (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
352        (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
353        (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
354    }
355}
356
357fn literal_to_string(lit: token::Lit) -> String {
358    let token::Lit { kind, symbol, suffix } = lit;
359    let mut out = match kind {
360        token::Byte => format!("b'{symbol}'"),
361        token::Char => format!("'{symbol}'"),
362        token::Str => format!("\"{symbol}\""),
363        token::StrRaw(n) => {
364            format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
365        }
366        token::ByteStr => format!("b\"{symbol}\""),
367        token::ByteStrRaw(n) => {
368            format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
369        }
370        token::CStr => format!("c\"{symbol}\""),
371        token::CStrRaw(n) => {
372            format!("cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize))
373        }
374        token::Integer | token::Float | token::Bool | token::Err(_) => symbol.to_string(),
375    };
376
377    if let Some(suffix) = suffix {
378        out.push_str(suffix.as_str())
379    }
380
381    out
382}
383
384impl std::ops::Deref for State<'_> {
385    type Target = pp::Printer;
386    fn deref(&self) -> &Self::Target {
387        &self.s
388    }
389}
390
391impl std::ops::DerefMut for State<'_> {
392    fn deref_mut(&mut self) -> &mut Self::Target {
393        &mut self.s
394    }
395}
396
397/// This trait is used for both AST and HIR pretty-printing.
398pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
399    fn comments(&self) -> Option<&Comments<'a>>;
400    fn comments_mut(&mut self) -> Option<&mut Comments<'a>>;
401    fn ann_post(&mut self, ident: Ident);
402    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
403
404    fn print_ident(&mut self, ident: Ident) {
405        self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
406        self.ann_post(ident)
407    }
408
409    fn strsep<'x, T: 'x, F, I>(
410        &mut self,
411        sep: &'static str,
412        space_before: bool,
413        b: Breaks,
414        elts: I,
415        mut op: F,
416    ) where
417        F: FnMut(&mut Self, &T),
418        I: IntoIterator<Item = &'x T>,
419    {
420        let mut it = elts.into_iter();
421
422        self.rbox(0, b);
423        if let Some(first) = it.next() {
424            op(self, first);
425            for elt in it {
426                if space_before {
427                    self.space();
428                }
429                self.word_space(sep);
430                op(self, elt);
431            }
432        }
433        self.end();
434    }
435
436    fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F)
437    where
438        F: FnMut(&mut Self, &T),
439        I: IntoIterator<Item = &'x T>,
440    {
441        self.strsep(",", false, b, elts, op)
442    }
443
444    fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
445        let mut has_comment = false;
446        while let Some(cmnt) = self.peek_comment() {
447            if cmnt.pos >= pos {
448                break;
449            }
450            has_comment = true;
451            let cmnt = self.next_comment().unwrap();
452            self.print_comment(cmnt);
453        }
454        has_comment
455    }
456
457    fn print_comment(&mut self, cmnt: Comment) {
458        match cmnt.style {
459            CommentStyle::Mixed => {
460                if !self.is_beginning_of_line() {
461                    self.zerobreak();
462                }
463                if let Some((last, lines)) = cmnt.lines.split_last() {
464                    self.ibox(0);
465
466                    for line in lines {
467                        self.word(line.clone());
468                        self.hardbreak()
469                    }
470
471                    self.word(last.clone());
472                    self.space();
473
474                    self.end();
475                }
476                self.zerobreak()
477            }
478            CommentStyle::Isolated => {
479                self.hardbreak_if_not_bol();
480                for line in &cmnt.lines {
481                    // Don't print empty lines because they will end up as trailing
482                    // whitespace.
483                    if !line.is_empty() {
484                        self.word(line.clone());
485                    }
486                    self.hardbreak();
487                }
488            }
489            CommentStyle::Trailing => {
490                if !self.is_beginning_of_line() {
491                    self.word(" ");
492                }
493                if let [line] = cmnt.lines.as_slice() {
494                    self.word(line.clone());
495                    self.hardbreak()
496                } else {
497                    self.visual_align();
498                    for line in &cmnt.lines {
499                        if !line.is_empty() {
500                            self.word(line.clone());
501                        }
502                        self.hardbreak();
503                    }
504                    self.end();
505                }
506            }
507            CommentStyle::BlankLine => {
508                // We need to do at least one, possibly two hardbreaks.
509                let twice = match self.last_token() {
510                    Some(pp::Token::String(s)) => ";" == s,
511                    Some(pp::Token::Begin(_)) => true,
512                    Some(pp::Token::End) => true,
513                    _ => false,
514                };
515                if twice {
516                    self.hardbreak();
517                }
518                self.hardbreak();
519            }
520        }
521    }
522
523    fn peek_comment<'b>(&'b self) -> Option<&'b Comment>
524    where
525        'a: 'b,
526    {
527        self.comments().and_then(|c| c.peek())
528    }
529
530    fn next_comment(&mut self) -> Option<Comment> {
531        self.comments_mut().and_then(|c| c.next())
532    }
533
534    fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
535        if let Some(cmnts) = self.comments_mut() {
536            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
537                self.print_comment(cmnt);
538            }
539        }
540    }
541
542    fn print_remaining_comments(&mut self) {
543        // If there aren't any remaining comments, then we need to manually
544        // make sure there is a line break at the end.
545        if self.peek_comment().is_none() {
546            self.hardbreak();
547        }
548        while let Some(cmnt) = self.next_comment() {
549            self.print_comment(cmnt)
550        }
551    }
552
553    fn print_string(&mut self, st: &str, style: ast::StrStyle) {
554        let st = match style {
555            ast::StrStyle::Cooked => format!("\"{}\"", st.escape_debug()),
556            ast::StrStyle::Raw(n) => {
557                format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
558            }
559        };
560        self.word(st)
561    }
562
563    fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
564        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
565    }
566
567    fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
568        self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
569    }
570
571    fn print_either_attributes(
572        &mut self,
573        attrs: &[ast::Attribute],
574        kind: ast::AttrStyle,
575        is_inline: bool,
576        trailing_hardbreak: bool,
577    ) -> bool {
578        let mut printed = false;
579        for attr in attrs {
580            if attr.style == kind {
581                if self.print_attribute_inline(attr, is_inline) {
582                    if is_inline {
583                        self.nbsp();
584                    }
585                    printed = true;
586                }
587            }
588        }
589        if printed && trailing_hardbreak && !is_inline {
590            self.hardbreak_if_not_bol();
591        }
592        printed
593    }
594
595    fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool {
596        if attr.has_name(sym::cfg_attr_trace) {
597            // It's not a valid identifier, so avoid printing it
598            // to keep the printed code reasonably parse-able.
599            return false;
600        }
601        if !is_inline {
602            self.hardbreak_if_not_bol();
603        }
604        self.maybe_print_comment(attr.span.lo());
605        match &attr.kind {
606            ast::AttrKind::Normal(normal) => {
607                match attr.style {
608                    ast::AttrStyle::Inner => self.word("#!["),
609                    ast::AttrStyle::Outer => self.word("#["),
610                }
611                self.print_attr_item(&normal.item, attr.span);
612                self.word("]");
613            }
614            ast::AttrKind::DocComment(comment_kind, data) => {
615                self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
616                self.hardbreak()
617            }
618        }
619        true
620    }
621
622    fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
623        self.ibox(0);
624        match item.unsafety {
625            ast::Safety::Unsafe(_) => {
626                self.word("unsafe");
627                self.popen();
628            }
629            ast::Safety::Default | ast::Safety::Safe(_) => {}
630        }
631        match &item.args {
632            AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
633                Some(MacHeader::Path(&item.path)),
634                false,
635                None,
636                *delim,
637                tokens,
638                true,
639                span,
640            ),
641            AttrArgs::Empty => {
642                self.print_path(&item.path, false, 0);
643            }
644            AttrArgs::Eq { expr, .. } => {
645                self.print_path(&item.path, false, 0);
646                self.space();
647                self.word_space("=");
648                let token_str = self.expr_to_string(expr);
649                self.word(token_str);
650            }
651        }
652        match item.unsafety {
653            ast::Safety::Unsafe(_) => self.pclose(),
654            ast::Safety::Default | ast::Safety::Safe(_) => {}
655        }
656        self.end();
657    }
658
659    /// This doesn't deserve to be called "pretty" printing, but it should be
660    /// meaning-preserving. A quick hack that might help would be to look at the
661    /// spans embedded in the TTs to decide where to put spaces and newlines.
662    /// But it'd be better to parse these according to the grammar of the
663    /// appropriate macro, transcribe back into the grammar we just parsed from,
664    /// and then pretty-print the resulting AST nodes (so, e.g., we print
665    /// expression arguments as expressions). It can be done! I think.
666    fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) -> Spacing {
667        match tt {
668            TokenTree::Token(token, spacing) => {
669                let token_str = self.token_to_string_ext(token, convert_dollar_crate);
670                self.word(token_str);
671                if let token::DocComment(..) = token.kind {
672                    self.hardbreak()
673                }
674                *spacing
675            }
676            TokenTree::Delimited(dspan, spacing, delim, tts) => {
677                self.print_mac_common(
678                    None,
679                    false,
680                    None,
681                    *delim,
682                    tts,
683                    convert_dollar_crate,
684                    dspan.entire(),
685                );
686                spacing.close
687            }
688        }
689    }
690
691    // The easiest way to implement token stream pretty printing would be to
692    // print each token followed by a single space. But that would produce ugly
693    // output, so we go to some effort to do better.
694    //
695    // First, we track whether each token that appears in source code is
696    // followed by a space, with `Spacing`, and reproduce that in the output.
697    // This works well in a lot of cases. E.g. `stringify!(x + y)` produces
698    // "x + y" and `stringify!(x+y)` produces "x+y".
699    //
700    // But this doesn't work for code produced by proc macros (which have no
701    // original source text representation) nor for code produced by decl
702    // macros (which are tricky because the whitespace after tokens appearing
703    // in macro rules isn't always what you want in the produced output). For
704    // these we mostly use `Spacing::Alone`, which is the conservative choice.
705    //
706    // So we have a backup mechanism for when `Spacing::Alone` occurs between a
707    // pair of tokens: we check if that pair of tokens can obviously go
708    // together without a space between them. E.g. token `x` followed by token
709    // `,` is better printed as `x,` than `x ,`. (Even if the original source
710    // code was `x ,`.)
711    //
712    // Finally, we must be careful about changing the output. Token pretty
713    // printing is used by `stringify!` and `impl Display for
714    // proc_macro::TokenStream`, and some programs rely on the output having a
715    // particular form, even though they shouldn't. In particular, some proc
716    // macros do `format!({stream})` on a token stream and then "parse" the
717    // output with simple string matching that can't handle whitespace changes.
718    // E.g. we have seen cases where a proc macro can handle `a :: b` but not
719    // `a::b`. See #117433 for some examples.
720    fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
721        let mut iter = tts.iter().peekable();
722        while let Some(tt) = iter.next() {
723            let spacing = self.print_tt(tt, convert_dollar_crate);
724            if let Some(next) = iter.peek() {
725                if spacing == Spacing::Alone && space_between(tt, next) {
726                    self.space();
727                }
728            }
729        }
730    }
731
732    fn print_mac_common(
733        &mut self,
734        header: Option<MacHeader<'_>>,
735        has_bang: bool,
736        ident: Option<Ident>,
737        delim: Delimiter,
738        tts: &TokenStream,
739        convert_dollar_crate: bool,
740        span: Span,
741    ) {
742        if delim == Delimiter::Brace {
743            self.cbox(INDENT_UNIT);
744        }
745        match header {
746            Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
747            Some(MacHeader::Keyword(kw)) => self.word(kw),
748            None => {}
749        }
750        if has_bang {
751            self.word("!");
752        }
753        if let Some(ident) = ident {
754            self.nbsp();
755            self.print_ident(ident);
756        }
757        match delim {
758            Delimiter::Brace => {
759                if header.is_some() || has_bang || ident.is_some() {
760                    self.nbsp();
761                }
762                self.word("{");
763                if !tts.is_empty() {
764                    self.space();
765                }
766                self.ibox(0);
767                self.print_tts(tts, convert_dollar_crate);
768                self.end();
769                let empty = tts.is_empty();
770                self.bclose(span, empty);
771            }
772            delim => {
773                let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
774                self.word(token_str);
775                self.ibox(0);
776                self.print_tts(tts, convert_dollar_crate);
777                self.end();
778                let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
779                self.word(token_str);
780            }
781        }
782    }
783
784    fn print_mac_def(
785        &mut self,
786        macro_def: &ast::MacroDef,
787        ident: &Ident,
788        sp: Span,
789        print_visibility: impl FnOnce(&mut Self),
790    ) {
791        let (kw, has_bang) = if macro_def.macro_rules {
792            ("macro_rules", true)
793        } else {
794            print_visibility(self);
795            ("macro", false)
796        };
797        self.print_mac_common(
798            Some(MacHeader::Keyword(kw)),
799            has_bang,
800            Some(*ident),
801            macro_def.body.delim,
802            &macro_def.body.tokens.clone(),
803            true,
804            sp,
805        );
806        if macro_def.body.need_semicolon() {
807            self.word(";");
808        }
809    }
810
811    fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
812        self.maybe_print_comment(path.span.lo());
813
814        for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
815            if i > 0 {
816                self.word("::")
817            }
818            self.print_path_segment(segment, colons_before_params);
819        }
820    }
821
822    fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
823        if segment.ident.name != kw::PathRoot {
824            self.print_ident(segment.ident);
825            if let Some(args) = &segment.args {
826                self.print_generic_args(args, colons_before_params);
827            }
828        }
829    }
830
831    fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
832        let w = w.into();
833        // Outer-box is consistent.
834        self.cbox(INDENT_UNIT);
835        // Head-box is inconsistent.
836        self.ibox(0);
837        // Keyword that starts the head.
838        if !w.is_empty() {
839            self.word_nbsp(w);
840        }
841    }
842
843    fn bopen(&mut self) {
844        self.word("{");
845        self.end(); // Close the head-box.
846    }
847
848    fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
849        let has_comment = self.maybe_print_comment(span.hi());
850        if !empty || has_comment {
851            self.break_offset_if_not_bol(1, -INDENT_UNIT);
852        }
853        self.word("}");
854        if close_box {
855            self.end(); // Close the outer-box.
856        }
857    }
858
859    fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
860        let close_box = true;
861        self.bclose_maybe_open(span, empty, close_box)
862    }
863
864    fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
865        if !self.is_beginning_of_line() {
866            self.break_offset(n, off)
867        } else if off != 0 {
868            if let Some(last_token) = self.last_token_still_buffered() {
869                if last_token.is_hardbreak_tok() {
870                    // We do something pretty sketchy here: tuck the nonzero
871                    // offset-adjustment we were going to deposit along with the
872                    // break into the previous hardbreak.
873                    self.replace_last_token_still_buffered(pp::Printer::hardbreak_tok_offset(off));
874                }
875            }
876        }
877    }
878
879    fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
880        // We extract the token stream from the AST fragment and pretty print
881        // it, rather than using AST pretty printing, because `Nonterminal` is
882        // slated for removal in #124141. (This method will also then be
883        // removed.)
884        self.tts_to_string(&TokenStream::from_nonterminal_ast(nt))
885    }
886
887    /// Print the token kind precisely, without converting `$crate` into its respective crate name.
888    fn token_kind_to_string(&self, tok: &TokenKind) -> Cow<'static, str> {
889        self.token_kind_to_string_ext(tok, None)
890    }
891
892    fn token_kind_to_string_ext(
893        &self,
894        tok: &TokenKind,
895        convert_dollar_crate: Option<Span>,
896    ) -> Cow<'static, str> {
897        match *tok {
898            token::Eq => "=".into(),
899            token::Lt => "<".into(),
900            token::Le => "<=".into(),
901            token::EqEq => "==".into(),
902            token::Ne => "!=".into(),
903            token::Ge => ">=".into(),
904            token::Gt => ">".into(),
905            token::Bang => "!".into(),
906            token::Tilde => "~".into(),
907            token::OrOr => "||".into(),
908            token::AndAnd => "&&".into(),
909            token::Plus => "+".into(),
910            token::Minus => "-".into(),
911            token::Star => "*".into(),
912            token::Slash => "/".into(),
913            token::Percent => "%".into(),
914            token::Caret => "^".into(),
915            token::And => "&".into(),
916            token::Or => "|".into(),
917            token::Shl => "<<".into(),
918            token::Shr => ">>".into(),
919            token::PlusEq => "+=".into(),
920            token::MinusEq => "-=".into(),
921            token::StarEq => "*=".into(),
922            token::SlashEq => "/=".into(),
923            token::PercentEq => "%=".into(),
924            token::CaretEq => "^=".into(),
925            token::AndEq => "&=".into(),
926            token::OrEq => "|=".into(),
927            token::ShlEq => "<<=".into(),
928            token::ShrEq => ">>=".into(),
929
930            /* Structural symbols */
931            token::At => "@".into(),
932            token::Dot => ".".into(),
933            token::DotDot => "..".into(),
934            token::DotDotDot => "...".into(),
935            token::DotDotEq => "..=".into(),
936            token::Comma => ",".into(),
937            token::Semi => ";".into(),
938            token::Colon => ":".into(),
939            token::PathSep => "::".into(),
940            token::RArrow => "->".into(),
941            token::LArrow => "<-".into(),
942            token::FatArrow => "=>".into(),
943            token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
944            token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
945            token::OpenDelim(Delimiter::Bracket) => "[".into(),
946            token::CloseDelim(Delimiter::Bracket) => "]".into(),
947            token::OpenDelim(Delimiter::Brace) => "{".into(),
948            token::CloseDelim(Delimiter::Brace) => "}".into(),
949            token::OpenDelim(Delimiter::Invisible(_))
950            | token::CloseDelim(Delimiter::Invisible(_)) => "".into(),
951            token::Pound => "#".into(),
952            token::Dollar => "$".into(),
953            token::Question => "?".into(),
954            token::SingleQuote => "'".into(),
955
956            /* Literals */
957            token::Literal(lit) => literal_to_string(lit).into(),
958
959            /* Name components */
960            token::Ident(name, is_raw) => {
961                IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
962            }
963            token::NtIdent(ident, is_raw) => {
964                IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
965            }
966
967            token::Lifetime(name, IdentIsRaw::No)
968            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
969            token::Lifetime(name, IdentIsRaw::Yes)
970            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
971                format!("'r#{}", &name.as_str()[1..]).into()
972            }
973
974            /* Other */
975            token::DocComment(comment_kind, attr_style, data) => {
976                doc_comment_to_string(comment_kind, attr_style, data).into()
977            }
978            token::Eof => "<eof>".into(),
979
980            token::Interpolated(ref nt) => self.nonterminal_to_string(&nt).into(),
981        }
982    }
983
984    /// Print the token precisely, without converting `$crate` into its respective crate name.
985    fn token_to_string(&self, token: &Token) -> Cow<'static, str> {
986        self.token_to_string_ext(token, false)
987    }
988
989    fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> Cow<'static, str> {
990        let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
991        self.token_kind_to_string_ext(&token.kind, convert_dollar_crate)
992    }
993
994    fn ty_to_string(&self, ty: &ast::Ty) -> String {
995        Self::to_string(|s| s.print_type(ty))
996    }
997
998    fn pat_to_string(&self, pat: &ast::Pat) -> String {
999        Self::to_string(|s| s.print_pat(pat))
1000    }
1001
1002    fn expr_to_string(&self, e: &ast::Expr) -> String {
1003        Self::to_string(|s| s.print_expr(e, FixupContext::default()))
1004    }
1005
1006    fn meta_item_lit_to_string(&self, lit: &ast::MetaItemLit) -> String {
1007        Self::to_string(|s| s.print_meta_item_lit(lit))
1008    }
1009
1010    fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
1011        Self::to_string(|s| s.print_stmt(stmt))
1012    }
1013
1014    fn item_to_string(&self, i: &ast::Item) -> String {
1015        Self::to_string(|s| s.print_item(i))
1016    }
1017
1018    fn path_to_string(&self, p: &ast::Path) -> String {
1019        Self::to_string(|s| s.print_path(p, false, 0))
1020    }
1021
1022    fn vis_to_string(&self, v: &ast::Visibility) -> String {
1023        Self::to_string(|s| s.print_visibility(v))
1024    }
1025
1026    fn block_to_string(&self, blk: &ast::Block) -> String {
1027        Self::to_string(|s| {
1028            // Containing cbox, will be closed by `print_block` at `}`.
1029            s.cbox(INDENT_UNIT);
1030            // Head-ibox, will be closed by `print_block` after `{`.
1031            s.ibox(0);
1032            s.print_block(blk)
1033        })
1034    }
1035
1036    fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
1037        Self::to_string(|s| s.print_attr_item(ai, ai.path.span))
1038    }
1039
1040    fn tts_to_string(&self, tokens: &TokenStream) -> String {
1041        Self::to_string(|s| s.print_tts(tokens, false))
1042    }
1043
1044    fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
1045        let mut printer = State::new();
1046        f(&mut printer);
1047        printer.s.eof()
1048    }
1049}
1050
1051impl<'a> PrintState<'a> for State<'a> {
1052    fn comments(&self) -> Option<&Comments<'a>> {
1053        self.comments.as_ref()
1054    }
1055
1056    fn comments_mut(&mut self) -> Option<&mut Comments<'a>> {
1057        self.comments.as_mut()
1058    }
1059
1060    fn ann_post(&mut self, ident: Ident) {
1061        self.ann.post(self, AnnNode::Ident(&ident));
1062    }
1063
1064    fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
1065        if colons_before_params {
1066            self.word("::")
1067        }
1068
1069        match args {
1070            ast::GenericArgs::AngleBracketed(data) => {
1071                self.word("<");
1072                self.commasep(Inconsistent, &data.args, |s, arg| match arg {
1073                    ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
1074                    ast::AngleBracketedArg::Constraint(c) => s.print_assoc_item_constraint(c),
1075                });
1076                self.word(">")
1077            }
1078
1079            ast::GenericArgs::Parenthesized(data) => {
1080                self.word("(");
1081                self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
1082                self.word(")");
1083                self.print_fn_ret_ty(&data.output);
1084            }
1085            ast::GenericArgs::ParenthesizedElided(_) => {
1086                self.word("(");
1087                self.word("..");
1088                self.word(")");
1089            }
1090        }
1091    }
1092}
1093
1094impl<'a> State<'a> {
1095    pub fn new() -> State<'a> {
1096        State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
1097    }
1098
1099    fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
1100    where
1101        F: FnMut(&mut State<'_>, &T),
1102        G: FnMut(&T) -> rustc_span::Span,
1103    {
1104        self.rbox(0, b);
1105        let len = elts.len();
1106        let mut i = 0;
1107        for elt in elts {
1108            self.maybe_print_comment(get_span(elt).hi());
1109            op(self, elt);
1110            i += 1;
1111            if i < len {
1112                self.word(",");
1113                self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
1114                self.space_if_not_bol();
1115            }
1116        }
1117        self.end();
1118    }
1119
1120    fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
1121        self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e, FixupContext::default()), |e| e.span)
1122    }
1123
1124    pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
1125        if let Some(lt) = *lifetime {
1126            self.print_lifetime(lt);
1127            self.nbsp();
1128        }
1129    }
1130
1131    pub fn print_assoc_item_constraint(&mut self, constraint: &ast::AssocItemConstraint) {
1132        self.print_ident(constraint.ident);
1133        if let Some(args) = constraint.gen_args.as_ref() {
1134            self.print_generic_args(args, false)
1135        }
1136        self.space();
1137        match &constraint.kind {
1138            ast::AssocItemConstraintKind::Equality { term } => {
1139                self.word_space("=");
1140                match term {
1141                    Term::Ty(ty) => self.print_type(ty),
1142                    Term::Const(c) => self.print_expr_anon_const(c, &[]),
1143                }
1144            }
1145            ast::AssocItemConstraintKind::Bound { bounds } => {
1146                if !bounds.is_empty() {
1147                    self.word_nbsp(":");
1148                    self.print_type_bounds(bounds);
1149                }
1150            }
1151        }
1152    }
1153
1154    pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
1155        match generic_arg {
1156            GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
1157            GenericArg::Type(ty) => self.print_type(ty),
1158            GenericArg::Const(ct) => self.print_expr(&ct.value, FixupContext::default()),
1159        }
1160    }
1161
1162    pub fn print_ty_pat(&mut self, pat: &ast::TyPat) {
1163        match &pat.kind {
1164            rustc_ast::TyPatKind::Range(start, end, include_end) => {
1165                if let Some(start) = start {
1166                    self.print_expr_anon_const(start, &[]);
1167                }
1168                self.word("..");
1169                if let Some(end) = end {
1170                    if let RangeEnd::Included(_) = include_end.node {
1171                        self.word("=");
1172                    }
1173                    self.print_expr_anon_const(end, &[]);
1174                }
1175            }
1176            rustc_ast::TyPatKind::Err(_) => {
1177                self.popen();
1178                self.word("/*ERROR*/");
1179                self.pclose();
1180            }
1181        }
1182    }
1183
1184    pub fn print_type(&mut self, ty: &ast::Ty) {
1185        self.maybe_print_comment(ty.span.lo());
1186        self.ibox(0);
1187        match &ty.kind {
1188            ast::TyKind::Slice(ty) => {
1189                self.word("[");
1190                self.print_type(ty);
1191                self.word("]");
1192            }
1193            ast::TyKind::Ptr(mt) => {
1194                self.word("*");
1195                self.print_mt(mt, true);
1196            }
1197            ast::TyKind::Ref(lifetime, mt) => {
1198                self.word("&");
1199                self.print_opt_lifetime(lifetime);
1200                self.print_mt(mt, false);
1201            }
1202            ast::TyKind::PinnedRef(lifetime, mt) => {
1203                self.word("&");
1204                self.print_opt_lifetime(lifetime);
1205                self.word("pin ");
1206                self.print_mt(mt, true);
1207            }
1208            ast::TyKind::Never => {
1209                self.word("!");
1210            }
1211            ast::TyKind::Tup(elts) => {
1212                self.popen();
1213                self.commasep(Inconsistent, elts, |s, ty| s.print_type(ty));
1214                if elts.len() == 1 {
1215                    self.word(",");
1216                }
1217                self.pclose();
1218            }
1219            ast::TyKind::Paren(typ) => {
1220                self.popen();
1221                self.print_type(typ);
1222                self.pclose();
1223            }
1224            ast::TyKind::BareFn(f) => {
1225                self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params);
1226            }
1227            ast::TyKind::UnsafeBinder(f) => {
1228                self.ibox(INDENT_UNIT);
1229                self.word("unsafe");
1230                self.print_generic_params(&f.generic_params);
1231                self.nbsp();
1232                self.print_type(&f.inner_ty);
1233                self.end();
1234            }
1235            ast::TyKind::Path(None, path) => {
1236                self.print_path(path, false, 0);
1237            }
1238            ast::TyKind::Path(Some(qself), path) => self.print_qpath(path, qself, false),
1239            ast::TyKind::TraitObject(bounds, syntax) => {
1240                match syntax {
1241                    ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"),
1242                    ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"),
1243                    ast::TraitObjectSyntax::None => {}
1244                }
1245                self.print_type_bounds(bounds);
1246            }
1247            ast::TyKind::ImplTrait(_, bounds) => {
1248                self.word_nbsp("impl");
1249                self.print_type_bounds(bounds);
1250            }
1251            ast::TyKind::Array(ty, length) => {
1252                self.word("[");
1253                self.print_type(ty);
1254                self.word("; ");
1255                self.print_expr(&length.value, FixupContext::default());
1256                self.word("]");
1257            }
1258            ast::TyKind::Typeof(e) => {
1259                self.word("typeof(");
1260                self.print_expr(&e.value, FixupContext::default());
1261                self.word(")");
1262            }
1263            ast::TyKind::Infer => {
1264                self.word("_");
1265            }
1266            ast::TyKind::Err(_) => {
1267                self.popen();
1268                self.word("/*ERROR*/");
1269                self.pclose();
1270            }
1271            ast::TyKind::Dummy => {
1272                self.popen();
1273                self.word("/*DUMMY*/");
1274                self.pclose();
1275            }
1276            ast::TyKind::ImplicitSelf => {
1277                self.word("Self");
1278            }
1279            ast::TyKind::MacCall(m) => {
1280                self.print_mac(m);
1281            }
1282            ast::TyKind::CVarArgs => {
1283                self.word("...");
1284            }
1285            ast::TyKind::Pat(ty, pat) => {
1286                self.print_type(ty);
1287                self.word(" is ");
1288                self.print_ty_pat(pat);
1289            }
1290        }
1291        self.end();
1292    }
1293
1294    fn print_trait_ref(&mut self, t: &ast::TraitRef) {
1295        self.print_path(&t.path, false, 0)
1296    }
1297
1298    fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
1299        if !generic_params.is_empty() {
1300            self.word("for");
1301            self.print_generic_params(generic_params);
1302            self.nbsp();
1303        }
1304    }
1305
1306    fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
1307        self.print_formal_generic_params(&t.bound_generic_params);
1308
1309        let ast::TraitBoundModifiers { constness, asyncness, polarity } = t.modifiers;
1310        match constness {
1311            ast::BoundConstness::Never => {}
1312            ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
1313                self.word_space(constness.as_str());
1314            }
1315        }
1316        match asyncness {
1317            ast::BoundAsyncness::Normal => {}
1318            ast::BoundAsyncness::Async(_) => {
1319                self.word_space(asyncness.as_str());
1320            }
1321        }
1322        match polarity {
1323            ast::BoundPolarity::Positive => {}
1324            ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
1325                self.word(polarity.as_str());
1326            }
1327        }
1328
1329        self.print_trait_ref(&t.trait_ref)
1330    }
1331
1332    fn print_stmt(&mut self, st: &ast::Stmt) {
1333        self.maybe_print_comment(st.span.lo());
1334        match &st.kind {
1335            ast::StmtKind::Let(loc) => {
1336                self.print_outer_attributes(&loc.attrs);
1337                self.space_if_not_bol();
1338                self.ibox(INDENT_UNIT);
1339                self.word_nbsp("let");
1340
1341                self.ibox(INDENT_UNIT);
1342                self.print_local_decl(loc);
1343                self.end();
1344                if let Some((init, els)) = loc.kind.init_else_opt() {
1345                    self.nbsp();
1346                    self.word_space("=");
1347                    self.print_expr_cond_paren(
1348                        init,
1349                        els.is_some() && classify::expr_trailing_brace(init).is_some(),
1350                        FixupContext::default(),
1351                    );
1352                    if let Some(els) = els {
1353                        self.cbox(INDENT_UNIT);
1354                        self.ibox(INDENT_UNIT);
1355                        self.word(" else ");
1356                        self.print_block(els);
1357                    }
1358                }
1359                self.word(";");
1360                self.end(); // `let` ibox
1361            }
1362            ast::StmtKind::Item(item) => self.print_item(item),
1363            ast::StmtKind::Expr(expr) => {
1364                self.space_if_not_bol();
1365                self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
1366                if classify::expr_requires_semi_to_be_stmt(expr) {
1367                    self.word(";");
1368                }
1369            }
1370            ast::StmtKind::Semi(expr) => {
1371                self.space_if_not_bol();
1372                self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
1373                self.word(";");
1374            }
1375            ast::StmtKind::Empty => {
1376                self.space_if_not_bol();
1377                self.word(";");
1378            }
1379            ast::StmtKind::MacCall(mac) => {
1380                self.space_if_not_bol();
1381                self.print_outer_attributes(&mac.attrs);
1382                self.print_mac(&mac.mac);
1383                if mac.style == ast::MacStmtStyle::Semicolon {
1384                    self.word(";");
1385                }
1386            }
1387        }
1388        self.maybe_print_trailing_comment(st.span, None)
1389    }
1390
1391    fn print_block(&mut self, blk: &ast::Block) {
1392        self.print_block_with_attrs(blk, &[])
1393    }
1394
1395    fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
1396        self.print_block_maybe_unclosed(blk, &[], false)
1397    }
1398
1399    fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
1400        self.print_block_maybe_unclosed(blk, attrs, true)
1401    }
1402
1403    fn print_block_maybe_unclosed(
1404        &mut self,
1405        blk: &ast::Block,
1406        attrs: &[ast::Attribute],
1407        close_box: bool,
1408    ) {
1409        match blk.rules {
1410            BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
1411            BlockCheckMode::Default => (),
1412        }
1413        self.maybe_print_comment(blk.span.lo());
1414        self.ann.pre(self, AnnNode::Block(blk));
1415        self.bopen();
1416
1417        let has_attrs = self.print_inner_attributes(attrs);
1418
1419        for (i, st) in blk.stmts.iter().enumerate() {
1420            match &st.kind {
1421                ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
1422                    self.maybe_print_comment(st.span.lo());
1423                    self.space_if_not_bol();
1424                    self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
1425                    self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
1426                }
1427                _ => self.print_stmt(st),
1428            }
1429        }
1430
1431        let empty = !has_attrs && blk.stmts.is_empty();
1432        self.bclose_maybe_open(blk.span, empty, close_box);
1433        self.ann.post(self, AnnNode::Block(blk))
1434    }
1435
1436    /// Print a `let pat = expr` expression.
1437    ///
1438    /// Parentheses are inserted surrounding `expr` if a round-trip through the
1439    /// parser would otherwise work out the wrong way in a condition position.
1440    ///
1441    /// For example each of the following would mean the wrong thing without
1442    /// parentheses.
1443    ///
1444    /// ```ignore (illustrative)
1445    /// if let _ = (Struct {}) {}
1446    ///
1447    /// if let _ = (true && false) {}
1448    /// ```
1449    ///
1450    /// In a match guard, the second case still requires parens, but the first
1451    /// case no longer does because anything until `=>` is considered part of
1452    /// the match guard expression. Parsing of the expression is not terminated
1453    /// by `{` in that position.
1454    ///
1455    /// ```ignore (illustrative)
1456    /// match () {
1457    ///     () if let _ = Struct {} => {}
1458    ///     () if let _ = (true && false) => {}
1459    /// }
1460    /// ```
1461    fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, fixup: FixupContext) {
1462        self.word("let ");
1463        self.print_pat(pat);
1464        self.space();
1465        self.word_space("=");
1466        self.print_expr_cond_paren(
1467            expr,
1468            fixup.needs_par_as_let_scrutinee(expr),
1469            FixupContext::default(),
1470        );
1471    }
1472
1473    fn print_mac(&mut self, m: &ast::MacCall) {
1474        self.print_mac_common(
1475            Some(MacHeader::Path(&m.path)),
1476            true,
1477            None,
1478            m.args.delim,
1479            &m.args.tokens.clone(),
1480            true,
1481            m.span(),
1482        );
1483    }
1484
1485    fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
1486        enum AsmArg<'a> {
1487            Template(String),
1488            Operand(&'a InlineAsmOperand),
1489            ClobberAbi(Symbol),
1490            Options(InlineAsmOptions),
1491        }
1492
1493        let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
1494        args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
1495        for (abi, _) in &asm.clobber_abis {
1496            args.push(AsmArg::ClobberAbi(*abi));
1497        }
1498        if !asm.options.is_empty() {
1499            args.push(AsmArg::Options(asm.options));
1500        }
1501
1502        self.popen();
1503        self.commasep(Consistent, &args, |s, arg| match arg {
1504            AsmArg::Template(template) => s.print_string(template, ast::StrStyle::Cooked),
1505            AsmArg::Operand(op) => {
1506                let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
1507                    InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
1508                    InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
1509                };
1510                match op {
1511                    InlineAsmOperand::In { reg, expr } => {
1512                        s.word("in");
1513                        s.popen();
1514                        print_reg_or_class(s, reg);
1515                        s.pclose();
1516                        s.space();
1517                        s.print_expr(expr, FixupContext::default());
1518                    }
1519                    InlineAsmOperand::Out { reg, late, expr } => {
1520                        s.word(if *late { "lateout" } else { "out" });
1521                        s.popen();
1522                        print_reg_or_class(s, reg);
1523                        s.pclose();
1524                        s.space();
1525                        match expr {
1526                            Some(expr) => s.print_expr(expr, FixupContext::default()),
1527                            None => s.word("_"),
1528                        }
1529                    }
1530                    InlineAsmOperand::InOut { reg, late, expr } => {
1531                        s.word(if *late { "inlateout" } else { "inout" });
1532                        s.popen();
1533                        print_reg_or_class(s, reg);
1534                        s.pclose();
1535                        s.space();
1536                        s.print_expr(expr, FixupContext::default());
1537                    }
1538                    InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
1539                        s.word(if *late { "inlateout" } else { "inout" });
1540                        s.popen();
1541                        print_reg_or_class(s, reg);
1542                        s.pclose();
1543                        s.space();
1544                        s.print_expr(in_expr, FixupContext::default());
1545                        s.space();
1546                        s.word_space("=>");
1547                        match out_expr {
1548                            Some(out_expr) => s.print_expr(out_expr, FixupContext::default()),
1549                            None => s.word("_"),
1550                        }
1551                    }
1552                    InlineAsmOperand::Const { anon_const } => {
1553                        s.word("const");
1554                        s.space();
1555                        s.print_expr(&anon_const.value, FixupContext::default());
1556                    }
1557                    InlineAsmOperand::Sym { sym } => {
1558                        s.word("sym");
1559                        s.space();
1560                        if let Some(qself) = &sym.qself {
1561                            s.print_qpath(&sym.path, qself, true);
1562                        } else {
1563                            s.print_path(&sym.path, true, 0);
1564                        }
1565                    }
1566                    InlineAsmOperand::Label { block } => {
1567                        s.head("label");
1568                        s.print_block(block);
1569                    }
1570                }
1571            }
1572            AsmArg::ClobberAbi(abi) => {
1573                s.word("clobber_abi");
1574                s.popen();
1575                s.print_symbol(*abi, ast::StrStyle::Cooked);
1576                s.pclose();
1577            }
1578            AsmArg::Options(opts) => {
1579                s.word("options");
1580                s.popen();
1581                s.commasep(Inconsistent, &opts.human_readable_names(), |s, &opt| {
1582                    s.word(opt);
1583                });
1584                s.pclose();
1585            }
1586        });
1587        self.pclose();
1588    }
1589
1590    fn print_local_decl(&mut self, loc: &ast::Local) {
1591        self.print_pat(&loc.pat);
1592        if let Some(ty) = &loc.ty {
1593            self.word_space(":");
1594            self.print_type(ty);
1595        }
1596    }
1597
1598    fn print_name(&mut self, name: Symbol) {
1599        self.word(name.to_string());
1600        self.ann.post(self, AnnNode::Name(&name))
1601    }
1602
1603    fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
1604        self.word("<");
1605        self.print_type(&qself.ty);
1606        if qself.position > 0 {
1607            self.space();
1608            self.word_space("as");
1609            let depth = path.segments.len() - qself.position;
1610            self.print_path(path, false, depth);
1611        }
1612        self.word(">");
1613        for item_segment in &path.segments[qself.position..] {
1614            self.word("::");
1615            self.print_ident(item_segment.ident);
1616            if let Some(args) = &item_segment.args {
1617                self.print_generic_args(args, colons_before_params)
1618            }
1619        }
1620    }
1621
1622    fn print_pat(&mut self, pat: &ast::Pat) {
1623        self.maybe_print_comment(pat.span.lo());
1624        self.ann.pre(self, AnnNode::Pat(pat));
1625        /* Pat isn't normalized, but the beauty of it
1626        is that it doesn't matter */
1627        match &pat.kind {
1628            PatKind::Wild => self.word("_"),
1629            PatKind::Never => self.word("!"),
1630            PatKind::Ident(BindingMode(by_ref, mutbl), ident, sub) => {
1631                if mutbl.is_mut() {
1632                    self.word_nbsp("mut");
1633                }
1634                if let ByRef::Yes(rmutbl) = by_ref {
1635                    self.word_nbsp("ref");
1636                    if rmutbl.is_mut() {
1637                        self.word_nbsp("mut");
1638                    }
1639                }
1640                self.print_ident(*ident);
1641                if let Some(p) = sub {
1642                    self.space();
1643                    self.word_space("@");
1644                    self.print_pat(p);
1645                }
1646            }
1647            PatKind::TupleStruct(qself, path, elts) => {
1648                if let Some(qself) = qself {
1649                    self.print_qpath(path, qself, true);
1650                } else {
1651                    self.print_path(path, true, 0);
1652                }
1653                self.popen();
1654                self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1655                self.pclose();
1656            }
1657            PatKind::Or(pats) => {
1658                self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
1659            }
1660            PatKind::Path(None, path) => {
1661                self.print_path(path, true, 0);
1662            }
1663            PatKind::Path(Some(qself), path) => {
1664                self.print_qpath(path, qself, false);
1665            }
1666            PatKind::Struct(qself, path, fields, etc) => {
1667                if let Some(qself) = qself {
1668                    self.print_qpath(path, qself, true);
1669                } else {
1670                    self.print_path(path, true, 0);
1671                }
1672                self.nbsp();
1673                self.word("{");
1674                let empty = fields.is_empty() && *etc == ast::PatFieldsRest::None;
1675                if !empty {
1676                    self.space();
1677                }
1678                self.commasep_cmnt(
1679                    Consistent,
1680                    fields,
1681                    |s, f| {
1682                        s.cbox(INDENT_UNIT);
1683                        if !f.is_shorthand {
1684                            s.print_ident(f.ident);
1685                            s.word_nbsp(":");
1686                        }
1687                        s.print_pat(&f.pat);
1688                        s.end();
1689                    },
1690                    |f| f.pat.span,
1691                );
1692                if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc {
1693                    if !fields.is_empty() {
1694                        self.word_space(",");
1695                    }
1696                    self.word("..");
1697                    if let ast::PatFieldsRest::Recovered(_) = etc {
1698                        self.word("/* recovered parse error */");
1699                    }
1700                }
1701                if !empty {
1702                    self.space();
1703                }
1704                self.word("}");
1705            }
1706            PatKind::Tuple(elts) => {
1707                self.popen();
1708                self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1709                if elts.len() == 1 {
1710                    self.word(",");
1711                }
1712                self.pclose();
1713            }
1714            PatKind::Box(inner) => {
1715                self.word("box ");
1716                self.print_pat(inner);
1717            }
1718            PatKind::Deref(inner) => {
1719                self.word("deref!");
1720                self.popen();
1721                self.print_pat(inner);
1722                self.pclose();
1723            }
1724            PatKind::Ref(inner, mutbl) => {
1725                self.word("&");
1726                if mutbl.is_mut() {
1727                    self.word("mut ");
1728                }
1729                if let PatKind::Ident(ast::BindingMode::MUT, ..) = inner.kind {
1730                    self.popen();
1731                    self.print_pat(inner);
1732                    self.pclose();
1733                } else {
1734                    self.print_pat(inner);
1735                }
1736            }
1737            PatKind::Expr(e) => self.print_expr(e, FixupContext::default()),
1738            PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
1739                if let Some(e) = begin {
1740                    self.print_expr(e, FixupContext::default());
1741                }
1742                match end_kind {
1743                    RangeEnd::Included(RangeSyntax::DotDotDot) => self.word("..."),
1744                    RangeEnd::Included(RangeSyntax::DotDotEq) => self.word("..="),
1745                    RangeEnd::Excluded => self.word(".."),
1746                }
1747                if let Some(e) = end {
1748                    self.print_expr(e, FixupContext::default());
1749                }
1750            }
1751            PatKind::Guard(subpat, condition) => {
1752                self.popen();
1753                self.print_pat(subpat);
1754                self.space();
1755                self.word_space("if");
1756                self.print_expr(condition, FixupContext::default());
1757                self.pclose();
1758            }
1759            PatKind::Slice(elts) => {
1760                self.word("[");
1761                self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1762                self.word("]");
1763            }
1764            PatKind::Rest => self.word(".."),
1765            PatKind::Paren(inner) => {
1766                self.popen();
1767                self.print_pat(inner);
1768                self.pclose();
1769            }
1770            PatKind::MacCall(m) => self.print_mac(m),
1771            PatKind::Err(_) => {
1772                self.popen();
1773                self.word("/*ERROR*/");
1774                self.pclose();
1775            }
1776        }
1777        self.ann.post(self, AnnNode::Pat(pat))
1778    }
1779
1780    fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
1781        match &explicit_self.node {
1782            SelfKind::Value(m) => {
1783                self.print_mutability(*m, false);
1784                self.word("self")
1785            }
1786            SelfKind::Region(lt, m) => {
1787                self.word("&");
1788                self.print_opt_lifetime(lt);
1789                self.print_mutability(*m, false);
1790                self.word("self")
1791            }
1792            SelfKind::Pinned(lt, m) => {
1793                self.word("&");
1794                self.print_opt_lifetime(lt);
1795                self.word("pin ");
1796                self.print_mutability(*m, true);
1797                self.word("self")
1798            }
1799            SelfKind::Explicit(typ, m) => {
1800                self.print_mutability(*m, false);
1801                self.word("self");
1802                self.word_space(":");
1803                self.print_type(typ)
1804            }
1805        }
1806    }
1807
1808    fn print_coroutine_kind(&mut self, coroutine_kind: ast::CoroutineKind) {
1809        match coroutine_kind {
1810            ast::CoroutineKind::Gen { .. } => {
1811                self.word_nbsp("gen");
1812            }
1813            ast::CoroutineKind::Async { .. } => {
1814                self.word_nbsp("async");
1815            }
1816            ast::CoroutineKind::AsyncGen { .. } => {
1817                self.word_nbsp("async");
1818                self.word_nbsp("gen");
1819            }
1820        }
1821    }
1822
1823    pub fn print_type_bounds(&mut self, bounds: &[ast::GenericBound]) {
1824        let mut first = true;
1825        for bound in bounds {
1826            if first {
1827                first = false;
1828            } else {
1829                self.nbsp();
1830                self.word_space("+");
1831            }
1832
1833            match bound {
1834                GenericBound::Trait(tref) => {
1835                    self.print_poly_trait_ref(tref);
1836                }
1837                GenericBound::Outlives(lt) => self.print_lifetime(*lt),
1838                GenericBound::Use(args, _) => {
1839                    self.word("use");
1840                    self.word("<");
1841                    self.commasep(Inconsistent, args, |s, arg| match arg {
1842                        ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0),
1843                        ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
1844                    });
1845                    self.word(">")
1846                }
1847            }
1848        }
1849    }
1850
1851    fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
1852        self.print_name(lifetime.ident.name)
1853    }
1854
1855    fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
1856        for (i, bound) in bounds.iter().enumerate() {
1857            if i != 0 {
1858                self.word(" + ");
1859            }
1860            match bound {
1861                ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
1862                _ => {
1863                    panic!("expected a lifetime bound, found a trait bound")
1864                }
1865            }
1866        }
1867    }
1868
1869    fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
1870        if generic_params.is_empty() {
1871            return;
1872        }
1873
1874        self.word("<");
1875
1876        self.commasep(Inconsistent, generic_params, |s, param| {
1877            s.print_outer_attributes_inline(&param.attrs);
1878
1879            match &param.kind {
1880                ast::GenericParamKind::Lifetime => {
1881                    let lt = ast::Lifetime { id: param.id, ident: param.ident };
1882                    s.print_lifetime(lt);
1883                    if !param.bounds.is_empty() {
1884                        s.word_nbsp(":");
1885                        s.print_lifetime_bounds(&param.bounds)
1886                    }
1887                }
1888                ast::GenericParamKind::Type { default } => {
1889                    s.print_ident(param.ident);
1890                    if !param.bounds.is_empty() {
1891                        s.word_nbsp(":");
1892                        s.print_type_bounds(&param.bounds);
1893                    }
1894                    if let Some(default) = default {
1895                        s.space();
1896                        s.word_space("=");
1897                        s.print_type(default)
1898                    }
1899                }
1900                ast::GenericParamKind::Const { ty, default, .. } => {
1901                    s.word_space("const");
1902                    s.print_ident(param.ident);
1903                    s.space();
1904                    s.word_space(":");
1905                    s.print_type(ty);
1906                    if !param.bounds.is_empty() {
1907                        s.word_nbsp(":");
1908                        s.print_type_bounds(&param.bounds);
1909                    }
1910                    if let Some(default) = default {
1911                        s.space();
1912                        s.word_space("=");
1913                        s.print_expr(&default.value, FixupContext::default());
1914                    }
1915                }
1916            }
1917        });
1918
1919        self.word(">");
1920    }
1921
1922    pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
1923        match mutbl {
1924            ast::Mutability::Mut => self.word_nbsp("mut"),
1925            ast::Mutability::Not => {
1926                if print_const {
1927                    self.word_nbsp("const");
1928                }
1929            }
1930        }
1931    }
1932
1933    fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
1934        self.print_mutability(mt.mutbl, print_const);
1935        self.print_type(&mt.ty)
1936    }
1937
1938    fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
1939        self.ibox(INDENT_UNIT);
1940
1941        self.print_outer_attributes_inline(&input.attrs);
1942
1943        match input.ty.kind {
1944            ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
1945            _ => {
1946                if let Some(eself) = input.to_self() {
1947                    self.print_explicit_self(&eself);
1948                } else {
1949                    let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
1950                        ident.name == kw::Empty
1951                    } else {
1952                        false
1953                    };
1954                    if !invalid {
1955                        self.print_pat(&input.pat);
1956                        self.word(":");
1957                        self.space();
1958                    }
1959                    self.print_type(&input.ty);
1960                }
1961            }
1962        }
1963        self.end();
1964    }
1965
1966    fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
1967        if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
1968            self.space_if_not_bol();
1969            self.ibox(INDENT_UNIT);
1970            self.word_space("->");
1971            self.print_type(ty);
1972            self.end();
1973            self.maybe_print_comment(ty.span.lo());
1974        }
1975    }
1976
1977    fn print_ty_fn(
1978        &mut self,
1979        ext: ast::Extern,
1980        safety: ast::Safety,
1981        decl: &ast::FnDecl,
1982        name: Option<Ident>,
1983        generic_params: &[ast::GenericParam],
1984    ) {
1985        self.ibox(INDENT_UNIT);
1986        self.print_formal_generic_params(generic_params);
1987        let generics = ast::Generics::default();
1988        let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() };
1989        self.print_fn(decl, header, name, &generics);
1990        self.end();
1991    }
1992
1993    fn print_fn_header_info(&mut self, header: ast::FnHeader) {
1994        self.print_constness(header.constness);
1995        header.coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
1996        self.print_safety(header.safety);
1997
1998        match header.ext {
1999            ast::Extern::None => {}
2000            ast::Extern::Implicit(_) => {
2001                self.word_nbsp("extern");
2002            }
2003            ast::Extern::Explicit(abi, _) => {
2004                self.word_nbsp("extern");
2005                self.print_token_literal(abi.as_token_lit(), abi.span);
2006                self.nbsp();
2007            }
2008        }
2009
2010        self.word("fn")
2011    }
2012
2013    fn print_safety(&mut self, s: ast::Safety) {
2014        match s {
2015            ast::Safety::Default => {}
2016            ast::Safety::Safe(_) => self.word_nbsp("safe"),
2017            ast::Safety::Unsafe(_) => self.word_nbsp("unsafe"),
2018        }
2019    }
2020
2021    fn print_constness(&mut self, s: ast::Const) {
2022        match s {
2023            ast::Const::No => {}
2024            ast::Const::Yes(_) => self.word_nbsp("const"),
2025        }
2026    }
2027
2028    fn print_is_auto(&mut self, s: ast::IsAuto) {
2029        match s {
2030            ast::IsAuto::Yes => self.word_nbsp("auto"),
2031            ast::IsAuto::No => {}
2032        }
2033    }
2034
2035    fn print_meta_item_lit(&mut self, lit: &ast::MetaItemLit) {
2036        self.print_token_literal(lit.as_token_lit(), lit.span)
2037    }
2038
2039    fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) {
2040        self.maybe_print_comment(span.lo());
2041        self.word(token_lit.to_string())
2042    }
2043
2044    fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
2045        self.print_string(sym.as_str(), style);
2046    }
2047
2048    fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) -> bool {
2049        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
2050    }
2051
2052    fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
2053        self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
2054    }
2055
2056    fn print_attribute(&mut self, attr: &ast::Attribute) {
2057        self.print_attribute_inline(attr, false);
2058    }
2059
2060    fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {
2061        match item {
2062            ast::MetaItemInner::MetaItem(mi) => self.print_meta_item(mi),
2063            ast::MetaItemInner::Lit(lit) => self.print_meta_item_lit(lit),
2064        }
2065    }
2066
2067    fn print_meta_item(&mut self, item: &ast::MetaItem) {
2068        self.ibox(INDENT_UNIT);
2069        match &item.kind {
2070            ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
2071            ast::MetaItemKind::NameValue(value) => {
2072                self.print_path(&item.path, false, 0);
2073                self.space();
2074                self.word_space("=");
2075                self.print_meta_item_lit(value);
2076            }
2077            ast::MetaItemKind::List(items) => {
2078                self.print_path(&item.path, false, 0);
2079                self.popen();
2080                self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
2081                self.pclose();
2082            }
2083        }
2084        self.end();
2085    }
2086
2087    pub(crate) fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
2088        Self::to_string(|s| s.print_type_bounds(bounds))
2089    }
2090
2091    pub(crate) fn where_bound_predicate_to_string(
2092        &self,
2093        where_bound_predicate: &ast::WhereBoundPredicate,
2094    ) -> String {
2095        Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
2096    }
2097
2098    pub(crate) fn tt_to_string(&self, tt: &TokenTree) -> String {
2099        Self::to_string(|s| {
2100            s.print_tt(tt, false);
2101        })
2102    }
2103
2104    pub(crate) fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
2105        Self::to_string(|s| s.print_path_segment(p, false))
2106    }
2107
2108    pub(crate) fn meta_list_item_to_string(&self, li: &ast::MetaItemInner) -> String {
2109        Self::to_string(|s| s.print_meta_list_item(li))
2110    }
2111
2112    pub(crate) fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
2113        Self::to_string(|s| s.print_attribute(attr))
2114    }
2115}