@@ -13,19 +13,28 @@ use rustc_errors::DiagnosticBuilder;
13
13
use rustc_errors:: { pluralize, PResult } ;
14
14
use rustc_span:: hygiene:: { LocalExpnId , Transparency } ;
15
15
use rustc_span:: symbol:: { sym, Ident , MacroRulesNormalizedIdent } ;
16
- use rustc_span:: Span ;
16
+ use rustc_span:: { Span , SyntaxContext } ;
17
17
18
18
use smallvec:: { smallvec, SmallVec } ;
19
19
use std:: mem;
20
20
21
21
// A Marker adds the given mark to the syntax context.
22
- struct Marker ( LocalExpnId , Transparency ) ;
22
+ struct Marker ( LocalExpnId , Transparency , FxHashMap < SyntaxContext , SyntaxContext > ) ;
23
23
24
24
impl MutVisitor for Marker {
25
25
const VISIT_TOKENS : bool = true ;
26
26
27
27
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) ;
29
38
}
30
39
}
31
40
@@ -123,7 +132,7 @@ pub(super) fn transcribe<'a>(
123
132
// again, and we are done transcribing.
124
133
let mut result: Vec < TokenTree > = Vec :: new ( ) ;
125
134
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 ( ) ) ;
127
136
128
137
loop {
129
138
// Look at the last frame on the stack.
0 commit comments