@@ -26,6 +26,7 @@ use tokenstream::{TokenStream, TokenTree};
2626use tokenstream;
2727
2828use std:: { cmp, fmt} ;
29+ use std:: mem;
2930use rustc_data_structures:: sync:: { Lrc , Lock } ;
3031
3132#[ derive( Clone , RustcEncodable , RustcDecodable , PartialEq , Eq , Hash , Debug , Copy ) ]
@@ -88,6 +89,12 @@ impl Lit {
8889 ByteStr ( _) | ByteStrRaw ( ..) => "byte string"
8990 }
9091 }
92+
93+ // See comments in `interpolated_to_tokenstream` for why we care about
94+ // *probably* equal here rather than actual equality
95+ fn probably_equal_for_proc_macro ( & self , other : & Lit ) -> bool {
96+ mem:: discriminant ( self ) == mem:: discriminant ( other)
97+ }
9198}
9299
93100pub ( crate ) fn ident_can_begin_expr ( ident : ast:: Ident , is_raw : bool ) -> bool {
@@ -530,14 +537,6 @@ impl Token {
530537 // stream they came from. Here we attempt to extract these
531538 // lossless token streams before we fall back to the
532539 // stringification.
533- //
534- // During early phases of the compiler, though, the AST could
535- // get modified directly (e.g. attributes added or removed) and
536- // the internal cache of tokens my not be invalidated or
537- // updated. Consequently if the "lossless" token stream
538- // disagrees with our actuall stringification (which has
539- // historically been much more battle-tested) then we go with
540- // the lossy stream anyway (losing span information).
541540 let mut tokens = None ;
542541
543542 match nt. 0 {
@@ -569,13 +568,96 @@ impl Token {
569568 let source = pprust:: token_to_string ( self ) ;
570569 parse_stream_from_source_str ( FileName :: MacroExpansion , source, sess, Some ( span) )
571570 } ) ;
571+
572+ // During early phases of the compiler the AST could get modified
573+ // directly (e.g. attributes added or removed) and the internal cache
574+ // of tokens my not be invalidated or updated. Consequently if the
575+ // "lossless" token stream disagrees with our actual stringification
576+ // (which has historically been much more battle-tested) then we go
577+ // with the lossy stream anyway (losing span information).
578+ //
579+ // Note that the comparison isn't `==` here to avoid comparing spans,
580+ // but it *also* is a "probable" equality which is a pretty weird
581+ // definition. We mostly want to catch actual changes to the AST
582+ // like a `#[cfg]` being processed or some weird `macro_rules!`
583+ // expansion.
584+ //
585+ // What we *don't* want to catch is the fact that a user-defined
586+ // literal like `0xf` is stringified as `15`, causing the cached token
587+ // stream to not be literal `==` token-wise (ignoring spans) to the
588+ // token stream we got from stringification.
589+ //
590+ // Instead the "probably equal" check here is "does each token
591+ // recursively have the same discriminant?" We basically don't look at
592+ // the token values here and assume that such fine grained modifications
593+ // of token streams doesn't happen.
572594 if let Some ( tokens) = tokens {
573- if tokens. eq_unspanned ( & tokens_for_real) {
595+ if tokens. probably_equal_for_proc_macro ( & tokens_for_real) {
574596 return tokens
575597 }
576598 }
577599 return tokens_for_real
578600 }
601+
602+ // See comments in `interpolated_to_tokenstream` for why we care about
603+ // *probably* equal here rather than actual equality
604+ pub fn probably_equal_for_proc_macro ( & self , other : & Token ) -> bool {
605+ if mem:: discriminant ( self ) != mem:: discriminant ( other) {
606+ return false
607+ }
608+ match ( self , other) {
609+ ( & Eq , & Eq ) |
610+ ( & Lt , & Lt ) |
611+ ( & Le , & Le ) |
612+ ( & EqEq , & EqEq ) |
613+ ( & Ne , & Ne ) |
614+ ( & Ge , & Ge ) |
615+ ( & Gt , & Gt ) |
616+ ( & AndAnd , & AndAnd ) |
617+ ( & OrOr , & OrOr ) |
618+ ( & Not , & Not ) |
619+ ( & Tilde , & Tilde ) |
620+ ( & At , & At ) |
621+ ( & Dot , & Dot ) |
622+ ( & DotDot , & DotDot ) |
623+ ( & DotDotDot , & DotDotDot ) |
624+ ( & DotDotEq , & DotDotEq ) |
625+ ( & DotEq , & DotEq ) |
626+ ( & Comma , & Comma ) |
627+ ( & Semi , & Semi ) |
628+ ( & Colon , & Colon ) |
629+ ( & ModSep , & ModSep ) |
630+ ( & RArrow , & RArrow ) |
631+ ( & LArrow , & LArrow ) |
632+ ( & FatArrow , & FatArrow ) |
633+ ( & Pound , & Pound ) |
634+ ( & Dollar , & Dollar ) |
635+ ( & Question , & Question ) |
636+ ( & Whitespace , & Whitespace ) |
637+ ( & Comment , & Comment ) |
638+ ( & Eof , & Eof ) => true ,
639+
640+ ( & BinOp ( a) , & BinOp ( b) ) |
641+ ( & BinOpEq ( a) , & BinOpEq ( b) ) => a == b,
642+
643+ ( & OpenDelim ( a) , & OpenDelim ( b) ) |
644+ ( & CloseDelim ( a) , & CloseDelim ( b) ) => a == b,
645+
646+ ( & DocComment ( a) , & DocComment ( b) ) |
647+ ( & Shebang ( a) , & Shebang ( b) ) => a == b,
648+
649+ ( & Lifetime ( a) , & Lifetime ( b) ) => a. name == b. name ,
650+ ( & Ident ( a, b) , & Ident ( c, d) ) => a. name == c. name && b == d,
651+
652+ ( & Literal ( ref a, b) , & Literal ( ref c, d) ) => {
653+ b == d && a. probably_equal_for_proc_macro ( c)
654+ }
655+
656+ ( & Interpolated ( _) , & Interpolated ( _) ) => false ,
657+
658+ _ => panic ! ( "forgot to add a token?" ) ,
659+ }
660+ }
579661}
580662
581663#[ derive( Clone , RustcEncodable , RustcDecodable , Eq , Hash ) ]
0 commit comments