22// Distributed under the MIT license that can be found in the LICENSE file.
33
44use proc_macro:: TokenStream ;
5+ use proc_macro2:: { Group , Ident , Punct , Spacing , TokenTree } ;
56use quote:: quote;
7+ use std:: collections:: HashSet ;
68use syn:: parse:: { Parse , ParseStream } ;
79use syn:: { Block , Error , ExprClosure , Pat , Result , Stmt , Token , parse_macro_input} ;
810
@@ -21,6 +23,98 @@ impl Parse for Lambda {
2123 }
2224}
2325
26+ fn is_punct ( token : Option < & TokenTree > , ch : char ) -> bool {
27+ matches ! ( token, Some ( TokenTree :: Punct ( punct) ) if punct. as_char( ) == ch)
28+ }
29+
30+ fn is_ident ( token : Option < & TokenTree > , name : & str ) -> bool {
31+ matches ! ( token, Some ( TokenTree :: Ident ( ident) ) if ident == name)
32+ }
33+
34+ fn is_lambda_macro ( name : Option < & TokenTree > , bang : Option < & TokenTree > ) -> bool {
35+ is_punct ( bang, '!' ) && ( is_ident ( name, "lambda" ) || is_ident ( name, "lambda_unsafe" ) )
36+ }
37+
38+ fn rewrite_nested_lambda (
39+ tokens : proc_macro2:: TokenStream ,
40+ names : & HashSet < String > ,
41+ ) -> Result < proc_macro2:: TokenStream > {
42+ let mut tokens = tokens. into_iter ( ) ;
43+ let mut out = Vec :: new ( ) ;
44+ if let Some ( TokenTree :: Group ( captures) ) = tokens. next ( ) {
45+ let mut group = Group :: new (
46+ captures. delimiter ( ) ,
47+ rewrite_captures ( captures. stream ( ) , names, true ) ?,
48+ ) ;
49+ group. set_span ( captures. span ( ) ) ;
50+ out. push ( TokenTree :: Group ( group) ) ;
51+ }
52+ out. extend ( tokens) ;
53+ Ok ( out. into_iter ( ) . collect ( ) )
54+ }
55+
56+ fn rewrite_captures (
57+ tokens : proc_macro2:: TokenStream ,
58+ names : & HashSet < String > ,
59+ in_capture_block : bool ,
60+ ) -> Result < proc_macro2:: TokenStream > {
61+ let tokens: Vec < TokenTree > = tokens. into_iter ( ) . collect ( ) ;
62+ let mut out = Vec :: new ( ) ;
63+ let mut in_params = false ;
64+ for ( i, token) in tokens. iter ( ) . enumerate ( ) {
65+ let prev = i. checked_sub ( 1 ) . map ( |j| & tokens[ j] ) ;
66+ let prev2 = i. checked_sub ( 2 ) . map ( |j| & tokens[ j] ) ;
67+ let next = tokens. get ( i + 1 ) ;
68+ let next2 = tokens. get ( i + 2 ) ;
69+ match token {
70+ TokenTree :: Group ( group) => {
71+ let stream = if is_lambda_macro ( prev2, prev) {
72+ rewrite_nested_lambda ( group. stream ( ) , names) ?
73+ } else {
74+ rewrite_captures ( group. stream ( ) , names, in_capture_block) ?
75+ } ;
76+ let mut rewritten = Group :: new ( group. delimiter ( ) , stream) ;
77+ rewritten. set_span ( group. span ( ) ) ;
78+ out. push ( TokenTree :: Group ( rewritten) ) ;
79+ }
80+ TokenTree :: Punct ( punct) if punct. as_char ( ) == '|' => {
81+ if in_params {
82+ in_params = false ;
83+ } else if is_punct ( next, '|' )
84+ || ( matches ! ( next, Some ( TokenTree :: Ident ( _) ) ) && is_punct ( next2, ':' ) )
85+ {
86+ in_params = true ;
87+ }
88+ out. push ( token. clone ( ) ) ;
89+ }
90+ TokenTree :: Ident ( ident) if names. contains ( & ident. to_string ( ) ) => {
91+ let before_colon = is_punct ( next, ':' ) && !is_punct ( next2, ':' ) ;
92+ let is_let =
93+ is_ident ( prev, "let" ) || ( is_ident ( prev, "mut" ) && is_ident ( prev2, "let" ) ) ;
94+ if !in_capture_block && ( is_let || ( in_params && before_colon) ) {
95+ return Err ( Error :: new (
96+ ident. span ( ) ,
97+ format ! ( "`{ident}` shadows a capture of the lambda" ) ,
98+ ) ) ;
99+ }
100+ let is_member = is_punct ( prev, '.' ) && !is_punct ( prev2, '.' ) ;
101+ let is_path = is_punct ( prev, ':' ) && is_punct ( prev2, ':' ) ;
102+ let is_label = is_punct ( prev, '\'' ) ;
103+ let is_macro = is_punct ( next, '!' ) && !is_punct ( next2, '=' ) ;
104+ if is_member || is_path || is_label || is_macro || before_colon {
105+ out. push ( token. clone ( ) ) ;
106+ } else {
107+ out. push ( TokenTree :: Ident ( Ident :: new ( "self" , ident. span ( ) ) ) ) ;
108+ out. push ( TokenTree :: Punct ( Punct :: new ( '.' , Spacing :: Alone ) ) ) ;
109+ out. push ( token. clone ( ) ) ;
110+ }
111+ }
112+ _ => out. push ( token. clone ( ) ) ,
113+ }
114+ }
115+ Ok ( out. into_iter ( ) . collect ( ) )
116+ }
117+
24118fn expand_lambda ( lambda : Lambda , is_unsafe : bool ) -> Result < proc_macro2:: TokenStream > {
25119 let mut names = Vec :: new ( ) ;
26120 let mut types = Vec :: new ( ) ;
@@ -35,7 +129,10 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
35129 "expected `let <name>: <type> = <init>;` for a capture" ,
36130 ) ) ;
37131 } ;
38- names. push ( & pat. pat ) ;
132+ let Pat :: Ident ( name) = & * pat. pat else {
133+ return Err ( Error :: new_spanned ( pat, "expected a name for a capture" ) ) ;
134+ } ;
135+ names. push ( & name. ident ) ;
39136 types. push ( & pat. ty ) ;
40137 inits. push ( & init. expr ) ;
41138 }
@@ -52,14 +149,16 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
52149
53150 let output = & lambda. closure . output ;
54151 let body = & lambda. closure . body ;
152+ let captured = names. iter ( ) . map ( |name| name. to_string ( ) ) . collect ( ) ;
153+ let body = rewrite_captures ( quote ! { #body } , & captured, false ) ?;
55154 let ( receiver, body, constructor) = if is_unsafe {
56155 (
57156 quote ! { & mut self } ,
58157 quote ! { unsafe { #body } } ,
59158 quote ! { from_lambda_unsafe } ,
60159 )
61160 } else {
62- ( quote ! { & self } , quote ! { # body } , quote ! { from_lambda } )
161+ ( quote ! { & self } , body, quote ! { from_lambda } )
63162 } ;
64163
65164 Ok ( quote ! { {
0 commit comments