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