5 use crate::pp::Breaks::{Consistent, Inconsistent};
6 use crate::pp::{self, Breaks};
9 use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
10 use rustc_ast::tokenstream::{TokenStream, TokenTree};
11 use rustc_ast::util::classify;
12 use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
13 use rustc_ast::util::parser;
14 use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
15 use rustc_ast::{attr, BindingAnnotation, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
16 use rustc_ast::{GenericArg, GenericBound, SelfKind, TraitBoundModifier};
17 use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
18 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
19 use rustc_span::edition::Edition;
20 use rustc_span::source_map::{SourceMap, Spanned};
21 use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
22 use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
24 use rustc_ast::attr::AttrIdGenerator;
27 pub use self::delimited::IterDelimited;
29 pub enum MacHeader<'a> {
31 Keyword(&'static str),
34 pub enum AnnNode<'a> {
37 Block(&'a ast::Block),
42 Crate(&'a ast::Crate),
46 fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
47 fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
50 #[derive(Copy, Clone)]
53 impl PpAnn for NoAnn {}
55 pub struct Comments<'a> {
57 comments: Vec<Comment>,
61 impl<'a> Comments<'a> {
62 pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
63 let comments = gather_comments(sm, filename, input);
64 Comments { sm, comments, current: 0 }
67 // FIXME: This shouldn't probably clone lmao
68 pub fn next(&self) -> Option<Comment> {
69 self.comments.get(self.current).cloned()
72 pub fn trailing_comment(
74 span: rustc_span::Span,
75 next_pos: Option<BytePos>,
76 ) -> Option<Comment> {
77 if let Some(cmnt) = self.next() {
78 if cmnt.style != CommentStyle::Trailing {
81 let span_line = self.sm.lookup_char_pos(span.hi());
82 let comment_line = self.sm.lookup_char_pos(cmnt.pos);
83 let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
84 if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
93 pub struct State<'a> {
95 comments: Option<Comments<'a>>,
96 ann: &'a (dyn PpAnn + 'a),
99 pub(crate) const INDENT_UNIT: isize = 4;
101 /// Requires you to pass an input filename and reader so that
102 /// it can scan the input text for comments to copy forward.
103 pub fn print_crate<'a>(
114 State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
116 if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
117 // We need to print `#![no_std]` (and its feature gate) so that
118 // compiling pretty-printed source won't inject libstd again.
119 // However, we don't want these attributes in the AST because
120 // of the feature gate, so we fake them up here.
122 // `#![feature(prelude_import)]`
123 let fake_attr = attr::mk_attr_nested_word(
125 ast::AttrStyle::Inner,
130 s.print_attribute(&fake_attr);
132 // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
133 // root, so this is not needed, and actually breaks things.
134 if edition.is_rust_2015() {
136 let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
137 s.print_attribute(&fake_attr);
141 s.print_inner_attributes(&krate.attrs);
142 for item in &krate.items {
145 s.print_remaining_comments();
146 s.ann.post(&mut s, AnnNode::Crate(krate));
150 /// This makes printed token streams look slightly nicer,
151 /// and also addresses some specific regressions described in #63896 and #73345.
152 fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
153 if let TokenTree::Token(token, _) = prev {
154 if matches!(token.kind, token::Dot | token::Dollar) {
157 if let token::DocComment(comment_kind, ..) = token.kind {
158 return comment_kind != CommentKind::Line;
162 TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
163 TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
164 !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _))
166 TokenTree::Delimited(_, Delimiter::Bracket, _) => {
167 !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _))
169 TokenTree::Delimited(..) => true,
173 fn binop_to_string(op: BinOpToken) -> &'static str {
179 token::Percent => "%",
188 fn doc_comment_to_string(
189 comment_kind: CommentKind,
190 attr_style: ast::AttrStyle,
193 match (comment_kind, attr_style) {
194 (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
195 (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
196 (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
197 (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
201 pub fn literal_to_string(lit: token::Lit) -> String {
202 let token::Lit { kind, symbol, suffix } = lit;
203 let mut out = match kind {
204 token::Byte => format!("b'{symbol}'"),
205 token::Char => format!("'{symbol}'"),
206 token::Str => format!("\"{symbol}\""),
207 token::StrRaw(n) => {
208 format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
210 token::ByteStr => format!("b\"{symbol}\""),
211 token::ByteStrRaw(n) => {
212 format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
214 token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
217 if let Some(suffix) = suffix {
218 out.push_str(suffix.as_str())
224 impl std::ops::Deref for State<'_> {
225 type Target = pp::Printer;
226 fn deref(&self) -> &Self::Target {
231 impl std::ops::DerefMut for State<'_> {
232 fn deref_mut(&mut self) -> &mut Self::Target {
237 pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
238 fn comments(&mut self) -> &mut Option<Comments<'a>>;
239 fn print_ident(&mut self, ident: Ident);
240 fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
250 F: FnMut(&mut Self, &T),
253 if let Some((first, rest)) = elts.split_first() {
259 self.word_space(sep);
266 fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
268 F: FnMut(&mut Self, &T),
270 self.strsep(",", false, b, elts, op)
273 fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
274 let mut has_comment = false;
275 while let Some(cmnt) = self.next_comment() {
278 self.print_comment(&cmnt);
286 fn print_comment(&mut self, cmnt: &Comment) {
288 CommentStyle::Mixed => {
289 if !self.is_beginning_of_line() {
292 if let Some((last, lines)) = cmnt.lines.split_last() {
296 self.word(line.clone());
300 self.word(last.clone());
307 CommentStyle::Isolated => {
308 self.hardbreak_if_not_bol();
309 for line in &cmnt.lines {
310 // Don't print empty lines because they will end up as trailing
312 if !line.is_empty() {
313 self.word(line.clone());
318 CommentStyle::Trailing => {
319 if !self.is_beginning_of_line() {
322 if cmnt.lines.len() == 1 {
323 self.word(cmnt.lines[0].clone());
327 for line in &cmnt.lines {
328 if !line.is_empty() {
329 self.word(line.clone());
336 CommentStyle::BlankLine => {
337 // We need to do at least one, possibly two hardbreaks.
338 let twice = match self.last_token() {
339 Some(pp::Token::String(s)) => ";" == s,
340 Some(pp::Token::Begin(_)) => true,
341 Some(pp::Token::End) => true,
350 if let Some(cmnts) = self.comments() {
355 fn next_comment(&mut self) -> Option<Comment> {
356 self.comments().as_mut().and_then(|c| c.next())
359 fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
360 if let Some(cmnts) = self.comments() {
361 if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
362 self.print_comment(&cmnt);
367 fn print_remaining_comments(&mut self) {
368 // If there aren't any remaining comments, then we need to manually
369 // make sure there is a line break at the end.
370 if self.next_comment().is_none() {
373 while let Some(cmnt) = self.next_comment() {
374 self.print_comment(&cmnt)
378 fn print_meta_item_lit(&mut self, lit: &ast::MetaItemLit) {
379 self.print_token_literal(lit.as_token_lit(), lit.span)
382 fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) {
383 self.maybe_print_comment(span.lo());
384 self.word(token_lit.to_string())
387 fn print_string(&mut self, st: &str, style: ast::StrStyle) {
388 let st = match style {
389 ast::StrStyle::Cooked => format!("\"{}\"", st.escape_debug()),
390 ast::StrStyle::Raw(n) => {
391 format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
397 fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
398 self.print_string(sym.as_str(), style);
401 fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
402 self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
405 fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) -> bool {
406 self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
409 fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
410 self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
413 fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
414 self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
417 fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
418 self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
421 fn print_either_attributes(
423 attrs: &[ast::Attribute],
424 kind: ast::AttrStyle,
426 trailing_hardbreak: bool,
428 let mut printed = false;
430 if attr.style == kind {
431 self.print_attribute_inline(attr, is_inline);
438 if printed && trailing_hardbreak && !is_inline {
439 self.hardbreak_if_not_bol();
444 fn print_attribute(&mut self, attr: &ast::Attribute) {
445 self.print_attribute_inline(attr, false)
448 fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
450 self.hardbreak_if_not_bol();
452 self.maybe_print_comment(attr.span.lo());
454 ast::AttrKind::Normal(normal) => {
456 ast::AttrStyle::Inner => self.word("#!["),
457 ast::AttrStyle::Outer => self.word("#["),
459 self.print_attr_item(&normal.item, attr.span);
462 ast::AttrKind::DocComment(comment_kind, data) => {
463 self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
469 fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
472 AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
473 Some(MacHeader::Path(&item.path)),
482 self.print_path(&item.path, false, 0);
484 AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
485 self.print_path(&item.path, false, 0);
487 self.word_space("=");
488 let token_str = self.expr_to_string(expr);
489 self.word(token_str);
491 AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => {
492 self.print_path(&item.path, false, 0);
494 self.word_space("=");
495 let token_str = self.meta_item_lit_to_string(lit);
496 self.word(token_str);
502 fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
504 ast::NestedMetaItem::MetaItem(mi) => self.print_meta_item(mi),
505 ast::NestedMetaItem::Lit(lit) => self.print_meta_item_lit(lit),
509 fn print_meta_item(&mut self, item: &ast::MetaItem) {
510 self.ibox(INDENT_UNIT);
512 ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
513 ast::MetaItemKind::NameValue(value) => {
514 self.print_path(&item.path, false, 0);
516 self.word_space("=");
517 self.print_meta_item_lit(value);
519 ast::MetaItemKind::List(items) => {
520 self.print_path(&item.path, false, 0);
522 self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
529 /// This doesn't deserve to be called "pretty" printing, but it should be
530 /// meaning-preserving. A quick hack that might help would be to look at the
531 /// spans embedded in the TTs to decide where to put spaces and newlines.
532 /// But it'd be better to parse these according to the grammar of the
533 /// appropriate macro, transcribe back into the grammar we just parsed from,
534 /// and then pretty-print the resulting AST nodes (so, e.g., we print
535 /// expression arguments as expressions). It can be done! I think.
536 fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
538 TokenTree::Token(token, _) => {
539 let token_str = self.token_to_string_ext(token, convert_dollar_crate);
540 self.word(token_str);
541 if let token::DocComment(..) = token.kind {
545 TokenTree::Delimited(dspan, delim, tts) => {
546 self.print_mac_common(
552 convert_dollar_crate,
559 fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
560 let mut iter = tts.trees().peekable();
561 while let Some(tt) = iter.next() {
562 self.print_tt(tt, convert_dollar_crate);
563 if let Some(next) = iter.peek() {
564 if tt_prepend_space(next, tt) {
573 header: Option<MacHeader<'_>>,
575 ident: Option<Ident>,
578 convert_dollar_crate: bool,
581 if delim == Delimiter::Brace {
582 self.cbox(INDENT_UNIT);
585 Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
586 Some(MacHeader::Keyword(kw)) => self.word(kw),
592 if let Some(ident) = ident {
594 self.print_ident(ident);
597 Delimiter::Brace => {
598 if header.is_some() || has_bang || ident.is_some() {
606 self.print_tts(tts, convert_dollar_crate);
608 let empty = tts.is_empty();
609 self.bclose(span, empty);
612 let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
613 self.word(token_str);
615 self.print_tts(tts, convert_dollar_crate);
617 let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
618 self.word(token_str);
625 macro_def: &ast::MacroDef,
628 print_visibility: impl FnOnce(&mut Self),
630 let (kw, has_bang) = if macro_def.macro_rules {
631 ("macro_rules", true)
633 print_visibility(self);
636 self.print_mac_common(
637 Some(MacHeader::Keyword(kw)),
640 macro_def.body.delim.to_token(),
641 ¯o_def.body.tokens.clone(),
645 if macro_def.body.need_semicolon() {
650 fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
651 self.maybe_print_comment(path.span.lo());
653 for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
657 self.print_path_segment(segment, colons_before_params);
661 fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
662 if segment.ident.name != kw::PathRoot {
663 self.print_ident(segment.ident);
664 if let Some(args) = &segment.args {
665 self.print_generic_args(args, colons_before_params);
670 fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
672 // Outer-box is consistent.
673 self.cbox(INDENT_UNIT);
674 // Head-box is inconsistent.
676 // Keyword that starts the head.
682 fn bopen(&mut self) {
684 self.end(); // Close the head-box.
687 fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
688 let has_comment = self.maybe_print_comment(span.hi());
689 if !empty || has_comment {
690 self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
694 self.end(); // Close the outer-box.
698 fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
699 let close_box = true;
700 self.bclose_maybe_open(span, empty, close_box)
703 fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
704 if !self.is_beginning_of_line() {
705 self.break_offset(n, off)
707 if let Some(last_token) = self.last_token_still_buffered() {
708 if last_token.is_hardbreak_tok() {
709 // We do something pretty sketchy here: tuck the nonzero
710 // offset-adjustment we were going to deposit along with the
711 // break into the previous hardbreak.
712 self.replace_last_token_still_buffered(pp::Printer::hardbreak_tok_offset(off));
718 fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
720 token::NtExpr(e) => self.expr_to_string(e),
721 token::NtMeta(e) => self.attr_item_to_string(e),
722 token::NtTy(e) => self.ty_to_string(e),
723 token::NtPath(e) => self.path_to_string(e),
724 token::NtItem(e) => self.item_to_string(e),
725 token::NtBlock(e) => self.block_to_string(e),
726 token::NtStmt(e) => self.stmt_to_string(e),
727 token::NtPat(e) => self.pat_to_string(e),
728 token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(*e, *is_raw).to_string(),
729 token::NtLifetime(e) => e.to_string(),
730 token::NtLiteral(e) => self.expr_to_string(e),
731 token::NtVis(e) => self.vis_to_string(e),
735 /// Print the token kind precisely, without converting `$crate` into its respective crate name.
736 fn token_kind_to_string(&self, tok: &TokenKind) -> Cow<'static, str> {
737 self.token_kind_to_string_ext(tok, None)
740 fn token_kind_to_string_ext(
743 convert_dollar_crate: Option<Span>,
744 ) -> Cow<'static, str> {
746 token::Eq => "=".into(),
747 token::Lt => "<".into(),
748 token::Le => "<=".into(),
749 token::EqEq => "==".into(),
750 token::Ne => "!=".into(),
751 token::Ge => ">=".into(),
752 token::Gt => ">".into(),
753 token::Not => "!".into(),
754 token::Tilde => "~".into(),
755 token::OrOr => "||".into(),
756 token::AndAnd => "&&".into(),
757 token::BinOp(op) => binop_to_string(op).into(),
758 token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(),
760 /* Structural symbols */
761 token::At => "@".into(),
762 token::Dot => ".".into(),
763 token::DotDot => "..".into(),
764 token::DotDotDot => "...".into(),
765 token::DotDotEq => "..=".into(),
766 token::Comma => ",".into(),
767 token::Semi => ";".into(),
768 token::Colon => ":".into(),
769 token::ModSep => "::".into(),
770 token::RArrow => "->".into(),
771 token::LArrow => "<-".into(),
772 token::FatArrow => "=>".into(),
773 token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
774 token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
775 token::OpenDelim(Delimiter::Bracket) => "[".into(),
776 token::CloseDelim(Delimiter::Bracket) => "]".into(),
777 token::OpenDelim(Delimiter::Brace) => "{".into(),
778 token::CloseDelim(Delimiter::Brace) => "}".into(),
779 token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
782 token::Pound => "#".into(),
783 token::Dollar => "$".into(),
784 token::Question => "?".into(),
785 token::SingleQuote => "'".into(),
788 token::Literal(lit) => literal_to_string(lit).into(),
790 /* Name components */
791 token::Ident(s, is_raw) => {
792 IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string().into()
794 token::Lifetime(s) => s.to_string().into(),
797 token::DocComment(comment_kind, attr_style, data) => {
798 doc_comment_to_string(comment_kind, attr_style, data).into()
800 token::Eof => "<eof>".into(),
802 token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(),
806 /// Print the token precisely, without converting `$crate` into its respective crate name.
807 fn token_to_string(&self, token: &Token) -> Cow<'static, str> {
808 self.token_to_string_ext(token, false)
811 fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> Cow<'static, str> {
812 let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
813 self.token_kind_to_string_ext(&token.kind, convert_dollar_crate)
816 fn ty_to_string(&self, ty: &ast::Ty) -> String {
817 Self::to_string(|s| s.print_type(ty))
820 fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
821 Self::to_string(|s| s.print_type_bounds(bounds))
824 fn pat_to_string(&self, pat: &ast::Pat) -> String {
825 Self::to_string(|s| s.print_pat(pat))
828 fn expr_to_string(&self, e: &ast::Expr) -> String {
829 Self::to_string(|s| s.print_expr(e))
832 fn meta_item_lit_to_string(&self, lit: &ast::MetaItemLit) -> String {
833 Self::to_string(|s| s.print_meta_item_lit(lit))
836 fn tt_to_string(&self, tt: &TokenTree) -> String {
837 Self::to_string(|s| s.print_tt(tt, false))
840 fn tts_to_string(&self, tokens: &TokenStream) -> String {
841 Self::to_string(|s| s.print_tts(tokens, false))
844 fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
845 Self::to_string(|s| s.print_stmt(stmt))
848 fn item_to_string(&self, i: &ast::Item) -> String {
849 Self::to_string(|s| s.print_item(i))
852 fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
853 Self::to_string(|s| s.print_assoc_item(i))
856 fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
857 Self::to_string(|s| s.print_foreign_item(i))
860 fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
861 Self::to_string(|s| s.print_generic_params(generic_params))
864 fn path_to_string(&self, p: &ast::Path) -> String {
865 Self::to_string(|s| s.print_path(p, false, 0))
868 fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
869 Self::to_string(|s| s.print_path_segment(p, false))
872 fn vis_to_string(&self, v: &ast::Visibility) -> String {
873 Self::to_string(|s| s.print_visibility(v))
876 fn block_to_string(&self, blk: &ast::Block) -> String {
877 Self::to_string(|s| {
878 // Containing cbox, will be closed by `print_block` at `}`.
880 // Head-ibox, will be closed by `print_block` after `{`.
886 fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
887 Self::to_string(|s| s.print_meta_list_item(li))
890 fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
891 Self::to_string(|s| s.print_attr_item(ai, ai.path.span))
894 fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
895 Self::to_string(|s| s.print_attribute(attr))
898 fn param_to_string(&self, arg: &ast::Param) -> String {
899 Self::to_string(|s| s.print_param(arg, false))
902 fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
903 let mut printer = State::new();
909 impl<'a> PrintState<'a> for State<'a> {
910 fn comments(&mut self) -> &mut Option<Comments<'a>> {
914 fn print_ident(&mut self, ident: Ident) {
915 self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
916 self.ann.post(self, AnnNode::Ident(&ident))
919 fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
920 if colons_before_params {
925 ast::GenericArgs::AngleBracketed(data) => {
927 self.commasep(Inconsistent, &data.args, |s, arg| match arg {
928 ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
929 ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c),
934 ast::GenericArgs::Parenthesized(data) => {
936 self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
938 self.print_fn_ret_ty(&data.output);
945 pub fn new() -> State<'a> {
946 State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
949 pub(crate) fn commasep_cmnt<T, F, G>(
956 F: FnMut(&mut State<'_>, &T),
957 G: FnMut(&T) -> rustc_span::Span,
960 let len = elts.len();
963 self.maybe_print_comment(get_span(elt).hi());
968 self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
969 self.space_if_not_bol();
975 pub(crate) fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
976 self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
979 pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
980 if let Some(lt) = *lifetime {
981 self.print_lifetime(lt);
986 pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocConstraint) {
987 self.print_ident(constraint.ident);
988 constraint.gen_args.as_ref().map(|args| self.print_generic_args(args, false));
990 match &constraint.kind {
991 ast::AssocConstraintKind::Equality { term } => {
992 self.word_space("=");
994 Term::Ty(ty) => self.print_type(ty),
995 Term::Const(c) => self.print_expr_anon_const(c, &[]),
998 ast::AssocConstraintKind::Bound { bounds } => {
999 if !bounds.is_empty() {
1000 self.word_nbsp(":");
1001 self.print_type_bounds(bounds);
1007 pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
1009 GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
1010 GenericArg::Type(ty) => self.print_type(ty),
1011 GenericArg::Const(ct) => self.print_expr(&ct.value),
1015 pub fn print_type(&mut self, ty: &ast::Ty) {
1016 self.maybe_print_comment(ty.span.lo());
1019 ast::TyKind::Slice(ty) => {
1021 self.print_type(ty);
1024 ast::TyKind::Ptr(mt) => {
1026 self.print_mt(mt, true);
1028 ast::TyKind::Ref(lifetime, mt) => {
1030 self.print_opt_lifetime(lifetime);
1031 self.print_mt(mt, false);
1033 ast::TyKind::Never => {
1036 ast::TyKind::Tup(elts) => {
1038 self.commasep(Inconsistent, elts, |s, ty| s.print_type(ty));
1039 if elts.len() == 1 {
1044 ast::TyKind::Paren(typ) => {
1046 self.print_type(typ);
1049 ast::TyKind::BareFn(f) => {
1050 self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
1052 ast::TyKind::Path(None, path) => {
1053 self.print_path(path, false, 0);
1055 ast::TyKind::Path(Some(qself), path) => self.print_qpath(path, qself, false),
1056 ast::TyKind::TraitObject(bounds, syntax) => {
1057 if *syntax == ast::TraitObjectSyntax::Dyn {
1058 self.word_nbsp("dyn");
1060 self.print_type_bounds(bounds);
1062 ast::TyKind::ImplTrait(_, bounds) => {
1063 self.word_nbsp("impl");
1064 self.print_type_bounds(bounds);
1066 ast::TyKind::Array(ty, length) => {
1068 self.print_type(ty);
1070 self.print_expr(&length.value);
1073 ast::TyKind::Typeof(e) => {
1074 self.word("typeof(");
1075 self.print_expr(&e.value);
1078 ast::TyKind::Infer => {
1081 ast::TyKind::Err => {
1083 self.word("/*ERROR*/");
1086 ast::TyKind::ImplicitSelf => {
1089 ast::TyKind::MacCall(m) => {
1092 ast::TyKind::CVarArgs => {
1099 fn print_trait_ref(&mut self, t: &ast::TraitRef) {
1100 self.print_path(&t.path, false, 0)
1103 fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
1104 if !generic_params.is_empty() {
1106 self.print_generic_params(generic_params);
1111 fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
1112 self.print_formal_generic_params(&t.bound_generic_params);
1113 self.print_trait_ref(&t.trait_ref)
1116 pub(crate) fn print_stmt(&mut self, st: &ast::Stmt) {
1117 self.maybe_print_comment(st.span.lo());
1119 ast::StmtKind::Local(loc) => {
1120 self.print_outer_attributes(&loc.attrs);
1121 self.space_if_not_bol();
1122 self.ibox(INDENT_UNIT);
1123 self.word_nbsp("let");
1125 self.ibox(INDENT_UNIT);
1126 self.print_local_decl(loc);
1128 if let Some((init, els)) = loc.kind.init_else_opt() {
1130 self.word_space("=");
1131 self.print_expr(init);
1132 if let Some(els) = els {
1133 self.cbox(INDENT_UNIT);
1134 self.ibox(INDENT_UNIT);
1135 self.word(" else ");
1136 self.print_block(els);
1140 self.end(); // `let` ibox
1142 ast::StmtKind::Item(item) => self.print_item(item),
1143 ast::StmtKind::Expr(expr) => {
1144 self.space_if_not_bol();
1145 self.print_expr_outer_attr_style(expr, false);
1146 if classify::expr_requires_semi_to_be_stmt(expr) {
1150 ast::StmtKind::Semi(expr) => {
1151 self.space_if_not_bol();
1152 self.print_expr_outer_attr_style(expr, false);
1155 ast::StmtKind::Empty => {
1156 self.space_if_not_bol();
1159 ast::StmtKind::MacCall(mac) => {
1160 self.space_if_not_bol();
1161 self.print_outer_attributes(&mac.attrs);
1162 self.print_mac(&mac.mac);
1163 if mac.style == ast::MacStmtStyle::Semicolon {
1168 self.maybe_print_trailing_comment(st.span, None)
1171 pub(crate) fn print_block(&mut self, blk: &ast::Block) {
1172 self.print_block_with_attrs(blk, &[])
1175 pub(crate) fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
1176 self.print_block_maybe_unclosed(blk, &[], false)
1179 pub(crate) fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
1180 self.print_block_maybe_unclosed(blk, attrs, true)
1183 pub(crate) fn print_block_maybe_unclosed(
1186 attrs: &[ast::Attribute],
1190 BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
1191 BlockCheckMode::Default => (),
1193 self.maybe_print_comment(blk.span.lo());
1194 self.ann.pre(self, AnnNode::Block(blk));
1197 let has_attrs = self.print_inner_attributes(attrs);
1199 for (i, st) in blk.stmts.iter().enumerate() {
1201 ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
1202 self.maybe_print_comment(st.span.lo());
1203 self.space_if_not_bol();
1204 self.print_expr_outer_attr_style(expr, false);
1205 self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
1207 _ => self.print_stmt(st),
1211 let empty = !has_attrs && blk.stmts.is_empty();
1212 self.bclose_maybe_open(blk.span, empty, close_box);
1213 self.ann.post(self, AnnNode::Block(blk))
1216 /// Print a `let pat = expr` expression.
1217 pub(crate) fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) {
1219 self.print_pat(pat);
1221 self.word_space("=");
1222 let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
1223 self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
1226 pub(crate) fn print_mac(&mut self, m: &ast::MacCall) {
1227 self.print_mac_common(
1228 Some(MacHeader::Path(&m.path)),
1231 m.args.delim.to_token(),
1232 &m.args.tokens.clone(),
1238 fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
1241 Operand(&'a InlineAsmOperand),
1243 Options(InlineAsmOptions),
1246 let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
1247 args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
1248 for (abi, _) in &asm.clobber_abis {
1249 args.push(AsmArg::ClobberAbi(*abi));
1251 if !asm.options.is_empty() {
1252 args.push(AsmArg::Options(asm.options));
1256 self.commasep(Consistent, &args, |s, arg| match arg {
1257 AsmArg::Template(template) => s.print_string(template, ast::StrStyle::Cooked),
1258 AsmArg::Operand(op) => {
1259 let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
1260 InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
1261 InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
1264 InlineAsmOperand::In { reg, expr } => {
1267 print_reg_or_class(s, reg);
1272 InlineAsmOperand::Out { reg, late, expr } => {
1273 s.word(if *late { "lateout" } else { "out" });
1275 print_reg_or_class(s, reg);
1279 Some(expr) => s.print_expr(expr),
1280 None => s.word("_"),
1283 InlineAsmOperand::InOut { reg, late, expr } => {
1284 s.word(if *late { "inlateout" } else { "inout" });
1286 print_reg_or_class(s, reg);
1291 InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
1292 s.word(if *late { "inlateout" } else { "inout" });
1294 print_reg_or_class(s, reg);
1297 s.print_expr(in_expr);
1301 Some(out_expr) => s.print_expr(out_expr),
1302 None => s.word("_"),
1305 InlineAsmOperand::Const { anon_const } => {
1308 s.print_expr(&anon_const.value);
1310 InlineAsmOperand::Sym { sym } => {
1313 if let Some(qself) = &sym.qself {
1314 s.print_qpath(&sym.path, qself, true);
1316 s.print_path(&sym.path, true, 0);
1321 AsmArg::ClobberAbi(abi) => {
1322 s.word("clobber_abi");
1324 s.print_symbol(*abi, ast::StrStyle::Cooked);
1327 AsmArg::Options(opts) => {
1330 let mut options = vec![];
1331 if opts.contains(InlineAsmOptions::PURE) {
1332 options.push("pure");
1334 if opts.contains(InlineAsmOptions::NOMEM) {
1335 options.push("nomem");
1337 if opts.contains(InlineAsmOptions::READONLY) {
1338 options.push("readonly");
1340 if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
1341 options.push("preserves_flags");
1343 if opts.contains(InlineAsmOptions::NORETURN) {
1344 options.push("noreturn");
1346 if opts.contains(InlineAsmOptions::NOSTACK) {
1347 options.push("nostack");
1349 if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
1350 options.push("att_syntax");
1352 if opts.contains(InlineAsmOptions::RAW) {
1353 options.push("raw");
1355 if opts.contains(InlineAsmOptions::MAY_UNWIND) {
1356 options.push("may_unwind");
1358 s.commasep(Inconsistent, &options, |s, &opt| {
1367 pub(crate) fn print_local_decl(&mut self, loc: &ast::Local) {
1368 self.print_pat(&loc.pat);
1369 if let Some(ty) = &loc.ty {
1370 self.word_space(":");
1371 self.print_type(ty);
1375 pub(crate) fn print_name(&mut self, name: Symbol) {
1376 self.word(name.to_string());
1377 self.ann.post(self, AnnNode::Name(&name))
1380 fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
1382 self.print_type(&qself.ty);
1383 if qself.position > 0 {
1385 self.word_space("as");
1386 let depth = path.segments.len() - qself.position;
1387 self.print_path(path, false, depth);
1390 for item_segment in &path.segments[qself.position..] {
1392 self.print_ident(item_segment.ident);
1393 if let Some(args) = &item_segment.args {
1394 self.print_generic_args(args, colons_before_params)
1399 pub(crate) fn print_pat(&mut self, pat: &ast::Pat) {
1400 self.maybe_print_comment(pat.span.lo());
1401 self.ann.pre(self, AnnNode::Pat(pat));
1402 /* Pat isn't normalized, but the beauty of it
1403 is that it doesn't matter */
1405 PatKind::Wild => self.word("_"),
1406 PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
1407 if *by_ref == ByRef::Yes {
1408 self.word_nbsp("ref");
1411 self.word_nbsp("mut");
1413 self.print_ident(*ident);
1414 if let Some(p) = sub {
1416 self.word_space("@");
1420 PatKind::TupleStruct(qself, path, elts) => {
1421 if let Some(qself) = qself {
1422 self.print_qpath(path, qself, true);
1424 self.print_path(path, true, 0);
1427 self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1430 PatKind::Or(pats) => {
1431 self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
1433 PatKind::Path(None, path) => {
1434 self.print_path(path, true, 0);
1436 PatKind::Path(Some(qself), path) => {
1437 self.print_qpath(path, qself, false);
1439 PatKind::Struct(qself, path, fields, etc) => {
1440 if let Some(qself) = qself {
1441 self.print_qpath(path, qself, true);
1443 self.print_path(path, true, 0);
1447 let empty = fields.is_empty() && !etc;
1455 s.cbox(INDENT_UNIT);
1456 if !f.is_shorthand {
1457 s.print_ident(f.ident);
1460 s.print_pat(&f.pat);
1466 if !fields.is_empty() {
1467 self.word_space(",");
1476 PatKind::Tuple(elts) => {
1478 self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1479 if elts.len() == 1 {
1484 PatKind::Box(inner) => {
1486 self.print_pat(inner);
1488 PatKind::Ref(inner, mutbl) => {
1493 if let PatKind::Ident(ast::BindingAnnotation::MUT, ..) = inner.kind {
1495 self.print_pat(inner);
1498 self.print_pat(inner);
1501 PatKind::Lit(e) => self.print_expr(e),
1502 PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
1503 if let Some(e) = begin {
1507 RangeEnd::Included(RangeSyntax::DotDotDot) => self.word("..."),
1508 RangeEnd::Included(RangeSyntax::DotDotEq) => self.word("..="),
1509 RangeEnd::Excluded => self.word(".."),
1511 if let Some(e) = end {
1515 PatKind::Slice(elts) => {
1517 self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
1520 PatKind::Rest => self.word(".."),
1521 PatKind::Paren(inner) => {
1523 self.print_pat(inner);
1526 PatKind::MacCall(m) => self.print_mac(m),
1528 self.ann.post(self, AnnNode::Pat(pat))
1531 fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
1532 match &explicit_self.node {
1533 SelfKind::Value(m) => {
1534 self.print_mutability(*m, false);
1537 SelfKind::Region(lt, m) => {
1539 self.print_opt_lifetime(lt);
1540 self.print_mutability(*m, false);
1543 SelfKind::Explicit(typ, m) => {
1544 self.print_mutability(*m, false);
1546 self.word_space(":");
1547 self.print_type(typ)
1552 pub(crate) fn print_asyncness(&mut self, asyncness: ast::Async) {
1553 if asyncness.is_async() {
1554 self.word_nbsp("async");
1558 pub fn print_type_bounds(&mut self, bounds: &[ast::GenericBound]) {
1559 let mut first = true;
1560 for bound in bounds {
1565 self.word_space("+");
1569 GenericBound::Trait(tref, modifier) => {
1571 TraitBoundModifier::None => {}
1572 TraitBoundModifier::Maybe => {
1575 TraitBoundModifier::MaybeConst => {
1576 self.word_space("~const");
1578 TraitBoundModifier::MaybeConstMaybe => {
1579 self.word_space("~const");
1583 self.print_poly_trait_ref(tref);
1585 GenericBound::Outlives(lt) => self.print_lifetime(*lt),
1590 pub(crate) fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
1591 self.print_name(lifetime.ident.name)
1594 pub(crate) fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
1595 for (i, bound) in bounds.iter().enumerate() {
1600 ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
1606 pub(crate) fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
1607 if generic_params.is_empty() {
1613 self.commasep(Inconsistent, generic_params, |s, param| {
1614 s.print_outer_attributes_inline(¶m.attrs);
1617 ast::GenericParamKind::Lifetime => {
1618 let lt = ast::Lifetime { id: param.id, ident: param.ident };
1619 s.print_lifetime(lt);
1620 if !param.bounds.is_empty() {
1622 s.print_lifetime_bounds(¶m.bounds)
1625 ast::GenericParamKind::Type { default } => {
1626 s.print_ident(param.ident);
1627 if !param.bounds.is_empty() {
1629 s.print_type_bounds(¶m.bounds);
1631 if let Some(default) = default {
1634 s.print_type(default)
1637 ast::GenericParamKind::Const { ty, default, .. } => {
1638 s.word_space("const");
1639 s.print_ident(param.ident);
1643 if !param.bounds.is_empty() {
1645 s.print_type_bounds(¶m.bounds);
1647 if let Some(default) = default {
1650 s.print_expr(&default.value);
1659 pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
1661 ast::Mutability::Mut => self.word_nbsp("mut"),
1662 ast::Mutability::Not => {
1664 self.word_nbsp("const");
1670 pub(crate) fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
1671 self.print_mutability(mt.mutbl, print_const);
1672 self.print_type(&mt.ty)
1675 pub(crate) fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
1676 self.ibox(INDENT_UNIT);
1678 self.print_outer_attributes_inline(&input.attrs);
1680 match input.ty.kind {
1681 ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
1683 if let Some(eself) = input.to_self() {
1684 self.print_explicit_self(&eself);
1686 let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
1687 ident.name == kw::Empty
1692 self.print_pat(&input.pat);
1696 self.print_type(&input.ty);
1703 pub(crate) fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
1704 if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
1705 self.space_if_not_bol();
1706 self.ibox(INDENT_UNIT);
1707 self.word_space("->");
1708 self.print_type(ty);
1710 self.maybe_print_comment(ty.span.lo());
1714 pub(crate) fn print_ty_fn(
1717 unsafety: ast::Unsafe,
1719 name: Option<Ident>,
1720 generic_params: &[ast::GenericParam],
1722 self.ibox(INDENT_UNIT);
1723 self.print_formal_generic_params(generic_params);
1724 let generics = ast::Generics {
1726 where_clause: ast::WhereClause {
1727 has_where_token: false,
1728 predicates: Vec::new(),
1733 let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
1734 self.print_fn(decl, header, name, &generics);
1738 pub(crate) fn print_fn_header_info(&mut self, header: ast::FnHeader) {
1739 self.print_constness(header.constness);
1740 self.print_asyncness(header.asyncness);
1741 self.print_unsafety(header.unsafety);
1744 ast::Extern::None => {}
1745 ast::Extern::Implicit(_) => {
1746 self.word_nbsp("extern");
1748 ast::Extern::Explicit(abi, _) => {
1749 self.word_nbsp("extern");
1750 self.print_token_literal(abi.as_token_lit(), abi.span);
1758 pub(crate) fn print_unsafety(&mut self, s: ast::Unsafe) {
1760 ast::Unsafe::No => {}
1761 ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"),
1765 pub(crate) fn print_constness(&mut self, s: ast::Const) {
1767 ast::Const::No => {}
1768 ast::Const::Yes(_) => self.word_nbsp("const"),
1772 pub(crate) fn print_is_auto(&mut self, s: ast::IsAuto) {
1774 ast::IsAuto::Yes => self.word_nbsp("auto"),
1775 ast::IsAuto::No => {}