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