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,12 +184,36 @@ 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 base parser. However, the
195
+ // parser is not mutated on many of the iterations, particularly when
196
+ // dealing with 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 base_parser = base_parser_from_cx ( & cx. current_expansion , & cx. parse_sess , arg. clone ( ) ) ;
213
+
192
214
for ( i, lhs) in lhses. iter ( ) . enumerate ( ) {
215
+ let mut parser = Cow :: Borrowed ( & base_parser) ;
216
+
193
217
// try each arm's matchers
194
218
let lhs_tt = match * lhs {
195
219
mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
@@ -202,7 +226,7 @@ fn generic_extension<'cx>(
202
226
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
203
227
let mut gated_spans_snaphot = mem:: take ( & mut * cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
204
228
205
- match parse_tt ( cx , lhs_tt , arg . clone ( ) ) {
229
+ match parse_tt ( & mut parser , lhs_tt ) {
206
230
Success ( named_matches) => {
207
231
// The matcher was `Success(..)`ful.
208
232
// Merge the gated spans from parsing the matcher with the pre-existing ones.
@@ -232,11 +256,11 @@ fn generic_extension<'cx>(
232
256
233
257
if cx. trace_macros ( ) {
234
258
let msg = format ! ( "to `{}`" , pprust:: tts_to_string( tts. clone( ) ) ) ;
235
- trace_macros_note ( cx , sp, msg) ;
259
+ trace_macros_note ( & mut cx . expansions , sp, msg) ;
236
260
}
237
261
238
262
let directory = Directory {
239
- path : Cow :: from ( cx. current_expansion . module . directory . as_path ( ) ) ,
263
+ path : cx. current_expansion . module . directory . clone ( ) ,
240
264
ownership : cx. current_expansion . directory_ownership ,
241
265
} ;
242
266
let mut p = Parser :: new ( cx. parse_sess ( ) , tts, Some ( directory) , true , false , None ) ;
@@ -269,6 +293,7 @@ fn generic_extension<'cx>(
269
293
// Restore to the state before snapshotting and maybe try again.
270
294
mem:: swap ( & mut gated_spans_snaphot, & mut cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
271
295
}
296
+ drop ( base_parser) ;
272
297
273
298
let ( token, label) = best_failure. expect ( "ran no matchers" ) ;
274
299
let span = token. span . substitute_dummy ( sp) ;
@@ -286,7 +311,9 @@ fn generic_extension<'cx>(
286
311
mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
287
312
_ => continue ,
288
313
} ;
289
- match parse_tt ( cx, lhs_tt, arg. clone ( ) ) {
314
+ let base_parser =
315
+ base_parser_from_cx ( & cx. current_expansion , & cx. parse_sess , arg. clone ( ) ) ;
316
+ match parse_tt ( & mut Cow :: Borrowed ( & base_parser) , lhs_tt) {
290
317
Success ( _) => {
291
318
if comma_span. is_dummy ( ) {
292
319
err. note ( "you might be missing a comma" ) ;
@@ -368,7 +395,8 @@ pub fn compile_declarative_macro(
368
395
) ,
369
396
] ;
370
397
371
- let argument_map = match parse ( sess, body, & argument_gram, None , true ) {
398
+ let base_parser = Parser :: new ( sess, body, None , true , true , rustc_parse:: MACRO_ARGUMENTS ) ;
399
+ let argument_map = match parse_tt ( & mut Cow :: Borrowed ( & base_parser) , & argument_gram) {
372
400
Success ( m) => m,
373
401
Failure ( token, msg) => {
374
402
let s = parse_failure_msg ( & token) ;
@@ -1184,14 +1212,16 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
1184
1212
}
1185
1213
}
1186
1214
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
1215
+ fn base_parser_from_cx < ' cx > (
1216
+ current_expansion : & ' cx ExpansionData ,
1217
+ sess : & ' cx ParseSess ,
1218
+ tts : TokenStream ,
1219
+ ) -> Parser < ' cx > {
1190
1220
let directory = Directory {
1191
- path : Cow :: from ( cx . current_expansion . module . directory . as_path ( ) ) ,
1192
- ownership : cx . current_expansion . directory_ownership ,
1221
+ path : current_expansion. module . directory . clone ( ) ,
1222
+ ownership : current_expansion. directory_ownership ,
1193
1223
} ;
1194
- parse ( cx . parse_sess ( ) , tts, mtch , Some ( directory) , true )
1224
+ Parser :: new ( sess , tts, Some ( directory) , true , true , rustc_parse :: MACRO_ARGUMENTS )
1195
1225
}
1196
1226
1197
1227
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
0 commit comments