@@ -9,7 +9,10 @@ use crate::formats::item_type::ItemType;
99use crate :: visit_lib:: LibEmbargoVisitor ;
1010
1111use 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 ;
1316use rustc_data_structures:: thin_vec:: ThinVec ;
1417use rustc_hir as hir;
1518use rustc_hir:: def:: { DefKind , Res } ;
@@ -504,10 +507,44 @@ pub(super) fn render_macro_arms<'a>(
504507/// as part of an item declaration.
505508pub ( super ) fn render_macro_matcher ( tcx : TyCtxt < ' _ > , matcher : & TokenTree ) -> String {
506509 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) ,
510542 }
543+ printer. end ( ) ;
544+ printer. break_offset_if_not_bol ( 0 , -4 ) ;
545+ printer. word ( ")" ) ;
546+ printer. end ( ) ;
547+ printer. s . eof ( )
511548}
512549
513550/// 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
551588 if reparsed_tree. eq_unspanned ( matcher) { Some ( snippet) } else { None }
552589}
553590
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+
554689pub ( super ) fn display_macro_source (
555690 cx : & mut DocContext < ' _ > ,
556691 name : Symbol ,
0 commit comments