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,12 +184,36 @@ 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 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+
192214 for ( i, lhs) in lhses. iter ( ) . enumerate ( ) {
215+ let mut parser = Cow :: Borrowed ( & base_parser) ;
216+
193217 // try each arm's matchers
194218 let lhs_tt = match * lhs {
195219 mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
@@ -202,7 +226,7 @@ fn generic_extension<'cx>(
202226 // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
203227 let mut gated_spans_snaphot = mem:: take ( & mut * cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
204228
205- match parse_tt ( cx , lhs_tt , arg . clone ( ) ) {
229+ match parse_tt ( & mut parser , lhs_tt ) {
206230 Success ( named_matches) => {
207231 // The matcher was `Success(..)`ful.
208232 // Merge the gated spans from parsing the matcher with the pre-existing ones.
@@ -232,11 +256,11 @@ fn generic_extension<'cx>(
232256
233257 if cx. trace_macros ( ) {
234258 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) ;
236260 }
237261
238262 let directory = Directory {
239- path : Cow :: from ( cx. current_expansion . module . directory . as_path ( ) ) ,
263+ path : cx. current_expansion . module . directory . clone ( ) ,
240264 ownership : cx. current_expansion . directory_ownership ,
241265 } ;
242266 let mut p = Parser :: new ( cx. parse_sess ( ) , tts, Some ( directory) , true , false , None ) ;
@@ -269,6 +293,7 @@ fn generic_extension<'cx>(
269293 // Restore to the state before snapshotting and maybe try again.
270294 mem:: swap ( & mut gated_spans_snaphot, & mut cx. parse_sess . gated_spans . spans . borrow_mut ( ) ) ;
271295 }
296+ drop ( base_parser) ;
272297
273298 let ( token, label) = best_failure. expect ( "ran no matchers" ) ;
274299 let span = token. span . substitute_dummy ( sp) ;
@@ -286,7 +311,9 @@ fn generic_extension<'cx>(
286311 mbe:: TokenTree :: Delimited ( _, ref delim) => & delim. tts [ ..] ,
287312 _ => continue ,
288313 } ;
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) {
290317 Success ( _) => {
291318 if comma_span. is_dummy ( ) {
292319 err. note ( "you might be missing a comma" ) ;
@@ -368,7 +395,8 @@ pub fn compile_declarative_macro(
368395 ) ,
369396 ] ;
370397
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) {
372400 Success ( m) => m,
373401 Failure ( token, msg) => {
374402 let s = parse_failure_msg ( & token) ;
@@ -1184,14 +1212,16 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
11841212 }
11851213}
11861214
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 > {
11901220 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 ,
11931223 } ;
1194- parse ( cx . parse_sess ( ) , tts, mtch , Some ( directory) , true )
1224+ Parser :: new ( sess , tts, Some ( directory) , true , true , rustc_parse :: MACRO_ARGUMENTS )
11951225}
11961226
11971227/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
0 commit comments