@@ -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 ,
@@ -658,6 +698,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
658
698
self . parse_ast_fragment ( tok_result, fragment_kind, & mac. path , span)
659
699
}
660
700
SyntaxExtensionKind :: LegacyBang ( expander) => {
701
+ if self . reduce_expansion_growth_limit ( mac. args . inner_tokens ( ) . len ( ) ) . is_err ( ) {
702
+ return ExpandResult :: Ready ( fragment_kind. dummy ( span) ) ;
703
+ }
704
+ let prev = self . cx . current_expansion . prior_type_ascription ;
705
+ self . cx . current_expansion . prior_type_ascription = mac. prior_type_ascription ;
661
706
let tok_result = expander. expand ( self . cx , span, mac. args . tokens . clone ( ) ) ;
662
707
let result = if let Some ( result) = fragment_kind. make_from ( tok_result) {
663
708
result
@@ -1979,6 +2024,7 @@ pub struct ExpansionConfig<'feat> {
1979
2024
pub crate_name : String ,
1980
2025
pub features : & ' feat Features ,
1981
2026
pub recursion_limit : Limit ,
2027
+ pub expansion_growth_limit : Limit ,
1982
2028
pub trace_mac : bool ,
1983
2029
/// If false, strip `#[test]` nodes
1984
2030
pub should_test : bool ,
@@ -1994,6 +2040,7 @@ impl ExpansionConfig<'_> {
1994
2040
crate_name,
1995
2041
features,
1996
2042
recursion_limit : Limit :: new ( 1024 ) ,
2043
+ expansion_growth_limit : Limit :: new ( 1500 ) ,
1997
2044
trace_mac : false ,
1998
2045
should_test : false ,
1999
2046
span_debug : false ,
0 commit comments