1- use crate :: base:: { DummyResult , ExtCtxt , MacResult , TTMacroExpander } ;
1+ use crate :: base:: { DummyResult , ExpansionData , ExtCtxt , MacResult , TTMacroExpander } ;
22use crate :: base:: { SyntaxExtension , SyntaxExtensionKind } ;
33use crate :: expand:: { ensure_complete_parse, parse_ast_fragment, AstFragment , AstFragmentKind } ;
44use crate :: mbe;
55use crate :: mbe:: macro_check;
6- use crate :: mbe:: macro_parser:: parse ;
6+ use crate :: mbe:: macro_parser:: parse_tt ;
77use crate :: mbe:: macro_parser:: { Error , Failure , Success } ;
8- use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq , NamedParseResult } ;
8+ use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq } ;
99use crate :: mbe:: transcribe:: transcribe;
1010
1111use rustc_ast_pretty:: pprust;
@@ -166,9 +166,9 @@ impl TTMacroExpander for MacroRulesMacroExpander {
166166 }
167167}
168168
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 ) {
170170 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) ;
172172}
173173
174174/// Given `lhses` and `rhses`, this is the new macro we create
@@ -184,11 +184,33 @@ fn generic_extension<'cx>(
184184) -> Box < dyn MacResult + ' cx > {
185185 if cx. trace_macros ( ) {
186186 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) ;
188188 }
189189
190190 // Which arm's failure should we report? (the one furthest along)
191191 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+
192214 for ( i, lhs) in lhses. iter ( ) . enumerate ( ) {
193215 // try each arm's matchers
194216 let lhs_tt = match * lhs {
@@ -202,7 +224,7 @@ fn generic_extension<'cx>(
202224 // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
203225 let mut gated_spans_snaphot = mem:: take ( & mut * cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
204226
205- match parse_tt ( cx , lhs_tt , arg . clone ( ) ) {
227+ match parse_tt ( & mut Cow :: Borrowed ( & parser ) , lhs_tt ) {
206228 Success ( named_matches) => {
207229 // The matcher was `Success(..)`ful.
208230 // Merge the gated spans from parsing the matcher with the pre-existing ones.
@@ -232,7 +254,7 @@ fn generic_extension<'cx>(
232254
233255 if cx. trace_macros ( ) {
234256 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) ;
236258 }
237259
238260 let directory = Directory {
@@ -269,6 +291,7 @@ fn generic_extension<'cx>(
269291 // Restore to the state before snapshotting and maybe try again.
270292 mem:: swap ( & mut gated_spans_snaphot, & mut cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
271293 }
294+ drop ( parser) ;
272295
273296 let ( token, label) = best_failure. expect ( "ran no matchers" ) ;
274297 let span = token. span . substitute_dummy ( sp) ;
@@ -286,7 +309,8 @@ fn generic_extension<'cx>(
286309 mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
287310 _ => continue ,
288311 } ;
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) {
290314 Success ( _) => {
291315 if comma_span. is_dummy ( ) {
292316 err. note ( "you might be missing a comma" ) ;
@@ -368,7 +392,8 @@ pub fn compile_declarative_macro(
368392 ) ,
369393 ] ;
370394
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) {
372397 Success ( m) => m,
373398 Failure ( token, msg) => {
374399 let s = parse_failure_msg ( & token) ;
@@ -1184,14 +1209,16 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
11841209 }
11851210}
11861211
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 > {
11901217 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 ,
11931220 } ;
1194- parse ( cx . parse_sess ( ) , tts, mtch , Some ( directory) , true )
1221+ Parser :: new ( sess , tts, Some ( directory) , true , true , rustc_parse :: MACRO_ARGUMENTS )
11951222}
11961223
11971224/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
0 commit comments