@@ -9,7 +9,10 @@ use crate::formats::item_type::ItemType;
9
9
use crate :: visit_lib:: LibEmbargoVisitor ;
10
10
11
11
use rustc_ast as ast;
12
- use rustc_ast:: tokenstream:: TokenTree ;
12
+ use rustc_ast:: token:: { self , BinOpToken , DelimToken } ;
13
+ use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
14
+ use rustc_ast_pretty:: pprust:: state:: State as Printer ;
15
+ use rustc_ast_pretty:: pprust:: PrintState ;
13
16
use rustc_data_structures:: thin_vec:: ThinVec ;
14
17
use rustc_hir as hir;
15
18
use rustc_hir:: def:: { DefKind , Res } ;
@@ -504,10 +507,44 @@ pub(super) fn render_macro_arms<'a>(
504
507
/// as part of an item declaration.
505
508
pub ( super ) fn render_macro_matcher ( tcx : TyCtxt < ' _ > , matcher : & TokenTree ) -> String {
506
509
if let Some ( snippet) = snippet_equal_to_token ( tcx, matcher) {
507
- snippet
508
- } else {
509
- rustc_ast_pretty:: pprust:: tt_to_string ( matcher)
510
+ // If the original source code is known, we display the matcher exactly
511
+ // as present in the source code.
512
+ return snippet;
513
+ }
514
+
515
+ // If the matcher is macro-generated or some other reason the source code
516
+ // snippet is not available, we attempt to nicely render the token tree.
517
+ let mut printer = Printer :: new ( ) ;
518
+
519
+ // If the inner ibox fits on one line, we get:
520
+ //
521
+ // macro_rules! macroname {
522
+ // (the matcher) => {...};
523
+ // }
524
+ //
525
+ // If the inner ibox gets wrapped, the cbox will break and get indented:
526
+ //
527
+ // macro_rules! macroname {
528
+ // (
529
+ // the matcher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!
531
+ // ) => {...};
532
+ // }
533
+ printer. cbox ( 8 ) ;
534
+ printer. word ( "(" ) ;
535
+ printer. zerobreak ( ) ;
536
+ printer. ibox ( 0 ) ;
537
+ match matcher {
538
+ TokenTree :: Delimited ( _span, _delim, tts) => print_tts ( & mut printer, tts) ,
539
+ // Matcher which is not a Delimited is unexpected and should've failed
540
+ // to compile, but we render whatever it is wrapped in parens.
541
+ TokenTree :: Token ( _) => print_tt ( & mut printer, matcher) ,
510
542
}
543
+ printer. end ( ) ;
544
+ printer. break_offset_if_not_bol ( 0 , -4 ) ;
545
+ printer. word ( ")" ) ;
546
+ printer. end ( ) ;
547
+ printer. s . eof ( )
511
548
}
512
549
513
550
/// Find the source snippet for this token's Span, reparse it, and return the
@@ -551,6 +588,104 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
551
588
if reparsed_tree. eq_unspanned ( matcher) { Some ( snippet) } else { None }
552
589
}
553
590
591
+ fn print_tt ( printer : & mut Printer < ' _ > , tt : & TokenTree ) {
592
+ match tt {
593
+ TokenTree :: Token ( token) => {
594
+ let token_str = printer. token_to_string ( token) ;
595
+ printer. word ( token_str) ;
596
+ if let token:: DocComment ( ..) = token. kind {
597
+ printer. hardbreak ( )
598
+ }
599
+ }
600
+ TokenTree :: Delimited ( _span, delim, tts) => {
601
+ let open_delim = printer. token_kind_to_string ( & token:: OpenDelim ( * delim) ) ;
602
+ printer. word ( open_delim) ;
603
+ if !tts. is_empty ( ) {
604
+ if * delim == DelimToken :: Brace {
605
+ printer. space ( ) ;
606
+ }
607
+ print_tts ( printer, tts) ;
608
+ if * delim == DelimToken :: Brace {
609
+ printer. space ( ) ;
610
+ }
611
+ }
612
+ let close_delim = printer. token_kind_to_string ( & token:: CloseDelim ( * delim) ) ;
613
+ printer. word ( close_delim) ;
614
+ }
615
+ }
616
+ }
617
+
618
+ fn print_tts ( printer : & mut Printer < ' _ > , tts : & TokenStream ) {
619
+ #[ derive( Copy , Clone , PartialEq ) ]
620
+ enum State {
621
+ Start ,
622
+ Dollar ,
623
+ DollarIdent ,
624
+ DollarIdentColon ,
625
+ DollarParen ,
626
+ DollarParenSep ,
627
+ Pound ,
628
+ PoundBang ,
629
+ Ident ,
630
+ Other ,
631
+ }
632
+
633
+ use State :: * ;
634
+
635
+ let mut state = Start ;
636
+ for tt in tts. trees ( ) {
637
+ let ( needs_space, next_state) = match & tt {
638
+ TokenTree :: Token ( tt) => match ( state, & tt. kind ) {
639
+ ( Dollar , token:: Ident ( ..) ) => ( false , DollarIdent ) ,
640
+ ( DollarIdent , token:: Colon ) => ( false , DollarIdentColon ) ,
641
+ ( DollarIdentColon , token:: Ident ( ..) ) => ( false , Other ) ,
642
+ (
643
+ DollarParen ,
644
+ token:: BinOp ( BinOpToken :: Plus | BinOpToken :: Star ) | token:: Question ,
645
+ ) => ( false , Other ) ,
646
+ ( DollarParen , _) => ( false , DollarParenSep ) ,
647
+ ( DollarParenSep , token:: BinOp ( BinOpToken :: Plus | BinOpToken :: Star ) ) => {
648
+ ( false , Other )
649
+ }
650
+ ( Pound , token:: Not ) => ( false , PoundBang ) ,
651
+ ( _, token:: Ident ( symbol, /* is_raw */ false ) )
652
+ if !usually_needs_space_between_keyword_and_open_delim ( * symbol) =>
653
+ {
654
+ ( true , Ident )
655
+ }
656
+ ( _, token:: Comma | token:: Semi ) => ( false , Other ) ,
657
+ ( _, token:: Dollar ) => ( true , Dollar ) ,
658
+ ( _, token:: Pound ) => ( true , Pound ) ,
659
+ ( _, _) => ( true , Other ) ,
660
+ } ,
661
+ TokenTree :: Delimited ( _, delim, _) => match ( state, delim) {
662
+ ( Dollar , DelimToken :: Paren ) => ( false , DollarParen ) ,
663
+ ( Pound | PoundBang , DelimToken :: Bracket ) => ( false , Other ) ,
664
+ ( Ident , DelimToken :: Paren | DelimToken :: Bracket ) => ( false , Other ) ,
665
+ ( _, _) => ( true , Other ) ,
666
+ } ,
667
+ } ;
668
+ if state != Start && needs_space {
669
+ printer. space ( ) ;
670
+ }
671
+ print_tt ( printer, & tt) ;
672
+ state = next_state;
673
+ }
674
+ }
675
+
676
+ // This rough subset of keywords is listed here to distinguish tokens resembling
677
+ // `f(0)` (no space between ident and paren) from tokens resembling `if let (0,
678
+ // 0) = x` (space between ident and paren).
679
+ fn usually_needs_space_between_keyword_and_open_delim ( symbol : Symbol ) -> bool {
680
+ match symbol. as_str ( ) {
681
+ "as" | "box" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern"
682
+ | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move"
683
+ | "mut" | "ref" | "return" | "static" | "struct" | "trait" | "type" | "unsafe" | "use"
684
+ | "where" | "while" | "yield" => true ,
685
+ _ => false ,
686
+ }
687
+ }
688
+
554
689
pub ( super ) fn display_macro_source (
555
690
cx : & mut DocContext < ' _ > ,
556
691
name : Symbol ,
0 commit comments