rustc_ast_pretty/pprust/
state.rs

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