1
- use crate :: base:: { DummyResult , ExtCtxt , MacResult , TTMacroExpander } ;
1
+ use crate :: base:: { DummyResult , ExpansionData , ExtCtxt , MacResult , TTMacroExpander } ;
2
2
use crate :: base:: { SyntaxExtension , SyntaxExtensionKind } ;
3
3
use crate :: expand:: { ensure_complete_parse, parse_ast_fragment, AstFragment , AstFragmentKind } ;
4
4
use crate :: mbe;
5
5
use crate :: mbe:: macro_check;
6
- use crate :: mbe:: macro_parser:: parse ;
6
+ use crate :: mbe:: macro_parser:: parse_tt ;
7
7
use crate :: mbe:: macro_parser:: { Error , Failure , Success } ;
8
- use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq , NamedParseResult } ;
8
+ use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq } ;
9
9
use crate :: mbe:: transcribe:: transcribe;
10
10
11
11
use rustc_ast_pretty:: pprust;
@@ -166,9 +166,9 @@ impl TTMacroExpander for MacroRulesMacroExpander {
166
166
}
167
167
}
168
168
169
- fn trace_macros_note ( cx : & mut ExtCtxt < ' _ > , sp : Span , message : String ) {
169
+ fn trace_macros_note ( cx_expansions : & mut FxHashMap < Span , Vec < String > > , sp : Span , message : String ) {
170
170
let sp = sp. macro_backtrace ( ) . last ( ) . map ( |trace| trace. call_site ) . unwrap_or ( sp) ;
171
- cx . expansions . entry ( sp) . or_default ( ) . push ( message) ;
171
+ cx_expansions . entry ( sp) . or_default ( ) . push ( message) ;
172
172
}
173
173
174
174
/// Given `lhses` and `rhses`, this is the new macro we create
@@ -184,11 +184,33 @@ fn generic_extension<'cx>(
184
184
) -> Box < dyn MacResult + ' cx > {
185
185
if cx. trace_macros ( ) {
186
186
let msg = format ! ( "expanding `{}! {{ {} }}`" , name, pprust:: tts_to_string( arg. clone( ) ) ) ;
187
- trace_macros_note ( cx , sp, msg) ;
187
+ trace_macros_note ( & mut cx . expansions , sp, msg) ;
188
188
}
189
189
190
190
// Which arm's failure should we report? (the one furthest along)
191
191
let mut best_failure: Option < ( Token , & str ) > = None ;
192
+
193
+ // We create a base parser that can be used for the "black box" parts.
194
+ // Every iteration needs a fresh copy of that parser. However, the parser
195
+ // is not mutated on many of the iterations, particularly when dealing with
196
+ // macros like this:
197
+ //
198
+ // macro_rules! foo {
199
+ // ("a") => (A);
200
+ // ("b") => (B);
201
+ // ("c") => (C);
202
+ // // ... etc. (maybe hundreds more)
203
+ // }
204
+ //
205
+ // as seen in the `html5ever` benchmark. We use a `Cow` so that the base
206
+ // parser is only cloned when necessary (upon mutation). Furthermore, we
207
+ // reinitialize the `Cow` with the base parser at the start of every
208
+ // iteration, so that any mutated parsers are not reused. This is all quite
209
+ // hacky, but speeds up the `html5ever` benchmark significantly. (Issue
210
+ // 68836 suggests a more comprehensive but more complex change to deal with
211
+ // this situation.)
212
+ let parser = parser_from_cx ( & cx. current_expansion , & cx. parse_sess , arg. clone ( ) ) ;
213
+
192
214
for ( i, lhs) in lhses. iter ( ) . enumerate ( ) {
193
215
// try each arm's matchers
194
216
let lhs_tt = match * lhs {
@@ -202,7 +224,7 @@ fn generic_extension<'cx>(
202
224
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
203
225
let mut gated_spans_snaphot = mem:: take ( & mut * cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
204
226
205
- match parse_tt ( cx , lhs_tt , arg . clone ( ) ) {
227
+ match parse_tt ( & mut Cow :: Borrowed ( & parser ) , lhs_tt ) {
206
228
Success ( named_matches) => {
207
229
// The matcher was `Success(..)`ful.
208
230
// Merge the gated spans from parsing the matcher with the pre-existing ones.
@@ -232,7 +254,7 @@ fn generic_extension<'cx>(
232
254
233
255
if cx. trace_macros ( ) {
234
256
let msg = format ! ( "to `{}`" , pprust:: tts_to_string( tts. clone( ) ) ) ;
235
- trace_macros_note ( cx , sp, msg) ;
257
+ trace_macros_note ( & mut cx . expansions , sp, msg) ;
236
258
}
237
259
238
260
let directory = Directory {
@@ -269,6 +291,7 @@ fn generic_extension<'cx>(
269
291
// Restore to the state before snapshotting and maybe try again.
270
292
mem:: swap ( & mut gated_spans_snaphot, & mut cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
271
293
}
294
+ drop ( parser) ;
272
295
273
296
let ( token, label) = best_failure. expect ( "ran no matchers" ) ;
274
297
let span = token. span . substitute_dummy ( sp) ;
@@ -286,7 +309,8 @@ fn generic_extension<'cx>(
286
309
mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
287
310
_ => continue ,
288
311
} ;
289
- match parse_tt ( cx, lhs_tt, arg. clone ( ) ) {
312
+ let parser = parser_from_cx ( & cx. current_expansion , & cx. parse_sess , arg. clone ( ) ) ;
313
+ match parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs_tt) {
290
314
Success ( _) => {
291
315
if comma_span. is_dummy ( ) {
292
316
err. note ( "you might be missing a comma" ) ;
@@ -368,7 +392,8 @@ pub fn compile_declarative_macro(
368
392
) ,
369
393
] ;
370
394
371
- let argument_map = match parse ( sess, body, & argument_gram, None , true ) {
395
+ let parser = Parser :: new ( sess, body, None , true , true , rustc_parse:: MACRO_ARGUMENTS ) ;
396
+ let argument_map = match parse_tt ( & mut Cow :: Borrowed ( & parser) , & argument_gram) {
372
397
Success ( m) => m,
373
398
Failure ( token, msg) => {
374
399
let s = parse_failure_msg ( & token) ;
@@ -1184,14 +1209,16 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
1184
1209
}
1185
1210
}
1186
1211
1187
- /// Use this token tree as a matcher to parse given tts.
1188
- fn parse_tt ( cx : & ExtCtxt < ' _ > , mtch : & [ mbe:: TokenTree ] , tts : TokenStream ) -> NamedParseResult {
1189
- // `None` is because we're not interpolating
1212
+ fn parser_from_cx < ' cx > (
1213
+ current_expansion : & ' cx ExpansionData ,
1214
+ sess : & ' cx ParseSess ,
1215
+ tts : TokenStream ,
1216
+ ) -> Parser < ' cx > {
1190
1217
let directory = Directory {
1191
- path : Cow :: from ( cx . current_expansion . module . directory . as_path ( ) ) ,
1192
- ownership : cx . current_expansion . directory_ownership ,
1218
+ path : Cow :: from ( current_expansion. module . directory . as_path ( ) ) ,
1219
+ ownership : current_expansion. directory_ownership ,
1193
1220
} ;
1194
- parse ( cx . parse_sess ( ) , tts, mtch , Some ( directory) , true )
1221
+ Parser :: new ( sess , tts, Some ( directory) , true , true , rustc_parse :: MACRO_ARGUMENTS )
1195
1222
}
1196
1223
1197
1224
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
0 commit comments