@@ -13,19 +13,28 @@ use rustc_errors::DiagnosticBuilder;
1313use rustc_errors:: { pluralize, PResult } ;
1414use rustc_span:: hygiene:: { LocalExpnId , Transparency } ;
1515use rustc_span:: symbol:: { sym, Ident , MacroRulesNormalizedIdent } ;
16- use rustc_span:: Span ;
16+ use rustc_span:: { Span , SyntaxContext } ;
1717
1818use smallvec:: { smallvec, SmallVec } ;
1919use std:: mem;
2020
2121// A Marker adds the given mark to the syntax context.
22- struct Marker ( LocalExpnId , Transparency ) ;
22+ struct Marker ( LocalExpnId , Transparency , FxHashMap < SyntaxContext , SyntaxContext > ) ;
2323
2424impl MutVisitor for Marker {
2525 const VISIT_TOKENS : bool = true ;
2626
2727 fn visit_span ( & mut self , span : & mut Span ) {
28- * span = span. apply_mark ( self . 0 . to_expn_id ( ) , self . 1 )
28+ // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
29+ // by itself. All tokens in a macro body typically have the same syntactic context, unless
30+ // it's some advanced case with macro-generated macros. So if we cache the marked version
31+ // of that context once, we'll typically have a 100% cache hit rate after that.
32+ let Marker ( expn_id, transparency, ref mut cache) = * self ;
33+ let data = span. data ( ) ;
34+ let marked_ctxt = * cache
35+ . entry ( data. ctxt )
36+ . or_insert_with ( || data. ctxt . apply_mark ( expn_id. to_expn_id ( ) , transparency) ) ;
37+ * span = data. with_ctxt ( marked_ctxt) ;
2938 }
3039}
3140
@@ -123,7 +132,7 @@ pub(super) fn transcribe<'a>(
123132 // again, and we are done transcribing.
124133 let mut result: Vec < TokenTree > = Vec :: new ( ) ;
125134 let mut result_stack = Vec :: new ( ) ;
126- let mut marker = Marker ( cx. current_expansion . id , transparency) ;
135+ let mut marker = Marker ( cx. current_expansion . id , transparency, Default :: default ( ) ) ;
127136
128137 loop {
129138 // Look at the last frame on the stack.
0 commit comments