@@ -621,6 +621,30 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
621
621
self . cx . trace_macros_diag ( ) ;
622
622
}
623
623
624
+ fn error_expansion_growth_limit ( & mut self ) {
625
+ let expn_data = self . cx . current_expansion . id . expn_data ( ) ;
626
+ let suggested_limit = match self . cx . ecfg . expansion_growth_limit {
627
+ Limit ( 0 ) => Limit ( 2 ) ,
628
+ limit => limit * 2 ,
629
+ } ;
630
+
631
+ self . cx
632
+ . struct_span_err (
633
+ expn_data. call_site ,
634
+ & format ! (
635
+ "expansion grow limit reached while expanding `{}`" ,
636
+ expn_data. kind. descr( )
637
+ ) ,
638
+ )
639
+ . help ( & format ! (
640
+ "consider increasing the expansion grow limit by adding a \
641
+ `#![expansion_growth_limit = \" {}\" ]` attribute to your crate (`{}`)",
642
+ suggested_limit, self . cx. ecfg. crate_name,
643
+ ) )
644
+ . emit ( ) ;
645
+ self . cx . trace_macros_diag ( ) ;
646
+ }
647
+
624
648
/// A macro's expansion does not fit in this fragment kind.
625
649
/// For example, a non-type macro in a type position.
626
650
fn error_wrong_fragment_kind ( & mut self , kind : AstFragmentKind , mac : & ast:: MacCall , span : Span ) {
@@ -629,6 +653,22 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
629
653
self . cx . trace_macros_diag ( ) ;
630
654
}
631
655
656
+ fn reduce_expansion_growth_limit ( & mut self , tokens_solved_size : usize ) -> Result < ( ) , ( ) > {
657
+ let expansion_limit =
658
+ self . cx . reduced_expansion_growth_limit . unwrap_or ( self . cx . ecfg . expansion_growth_limit ) ;
659
+ if !expansion_limit. value_within_limit ( tokens_solved_size) {
660
+ if self . cx . reduced_expansion_growth_limit . is_none ( ) {
661
+ self . error_expansion_growth_limit ( ) ;
662
+ }
663
+
664
+ // Reduce the recursion limit by half each time it triggers.
665
+ self . cx . reduced_recursion_limit = Some ( expansion_limit / 2 ) ;
666
+
667
+ return Err ( ( ) ) ;
668
+ }
669
+ Ok ( ( ) )
670
+ }
671
+
632
672
fn expand_invoc (
633
673
& mut self ,
634
674
invoc : Invocation ,
@@ -657,6 +697,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
657
697
self . parse_ast_fragment ( tok_result, fragment_kind, & mac. path , span)
658
698
}
659
699
SyntaxExtensionKind :: LegacyBang ( expander) => {
700
+ if self . reduce_expansion_growth_limit ( mac. args . inner_tokens ( ) . len ( ) ) . is_err ( ) {
701
+ return ExpandResult :: Ready ( fragment_kind. dummy ( span) ) ;
702
+ }
703
+ let prev = self . cx . current_expansion . prior_type_ascription ;
704
+ self . cx . current_expansion . prior_type_ascription = mac. prior_type_ascription ;
660
705
let tok_result = expander. expand ( self . cx , span, mac. args . tokens . clone ( ) ) ;
661
706
let result = if let Some ( result) = fragment_kind. make_from ( tok_result) {
662
707
result
@@ -1966,6 +2011,7 @@ pub struct ExpansionConfig<'feat> {
1966
2011
pub crate_name : String ,
1967
2012
pub features : Option < & ' feat Features > ,
1968
2013
pub recursion_limit : Limit ,
2014
+ pub expansion_growth_limit : Limit ,
1969
2015
pub trace_mac : bool ,
1970
2016
/// If false, strip `#[test]` nodes
1971
2017
pub should_test : bool ,
@@ -1981,6 +2027,7 @@ impl<'feat> ExpansionConfig<'feat> {
1981
2027
crate_name,
1982
2028
features : None ,
1983
2029
recursion_limit : Limit :: new ( 1024 ) ,
2030
+ expansion_growth_limit : Limit :: new ( 1500 ) ,
1984
2031
trace_mac : false ,
1985
2032
should_test : false ,
1986
2033
span_debug : false ,
0 commit comments