2023-06-28 18:11:01 +01:00
// SPDX-License-Identifier: GPL-2.0
use proc_macro ::{ Delimiter , Group , Ident , Spacing , Span , TokenTree } ;
fn concat ( tokens : & [ TokenTree ] , group_span : Span ) -> TokenTree {
let mut tokens = tokens . iter ( ) ;
let mut segments = Vec ::new ( ) ;
let mut span = None ;
loop {
match tokens . next ( ) {
None = > break ,
2023-11-17 20:39:59 -05:00
Some ( TokenTree ::Literal ( lit ) ) = > {
// Allow us to concat string literals by stripping quotes
let mut value = lit . to_string ( ) ;
if value . starts_with ( '"' ) & & value . ends_with ( '"' ) {
value . remove ( 0 ) ;
value . pop ( ) ;
}
segments . push ( ( value , lit . span ( ) ) ) ;
}
2023-06-28 18:11:01 +01:00
Some ( TokenTree ::Ident ( ident ) ) = > {
let mut value = ident . to_string ( ) ;
if value . starts_with ( " r# " ) {
value . replace_range ( 0 .. 2 , " " ) ;
}
segments . push ( ( value , ident . span ( ) ) ) ;
}
Some ( TokenTree ::Punct ( p ) ) if p . as_char ( ) = = ':' = > {
let Some ( TokenTree ::Ident ( ident ) ) = tokens . next ( ) else {
panic! ( " expected identifier as modifier " ) ;
} ;
let ( mut value , sp ) = segments . pop ( ) . expect ( " expected identifier before modifier " ) ;
match ident . to_string ( ) . as_str ( ) {
// Set the overall span of concatenated token as current span
" span " = > {
assert! (
span . is_none ( ) ,
" span modifier should only appear at most once "
) ;
span = Some ( sp ) ;
}
" lower " = > value = value . to_lowercase ( ) ,
" upper " = > value = value . to_uppercase ( ) ,
v = > panic! ( " unknown modifier ` {v} ` " ) ,
} ;
segments . push ( ( value , sp ) ) ;
}
_ = > panic! ( " unexpected token in paste segments " ) ,
} ;
}
let pasted : String = segments . into_iter ( ) . map ( | x | x . 0 ) . collect ( ) ;
TokenTree ::Ident ( Ident ::new ( & pasted , span . unwrap_or ( group_span ) ) )
}
pub ( crate ) fn expand ( tokens : & mut Vec < TokenTree > ) {
for token in tokens . iter_mut ( ) {
if let TokenTree ::Group ( group ) = token {
let delimiter = group . delimiter ( ) ;
let span = group . span ( ) ;
let mut stream : Vec < _ > = group . stream ( ) . into_iter ( ) . collect ( ) ;
// Find groups that looks like `[< A B C D >]`
if delimiter = = Delimiter ::Bracket
& & stream . len ( ) > = 3
& & matches! ( & stream [ 0 ] , TokenTree ::Punct ( p ) if p . as_char ( ) = = '<' )
& & matches! ( & stream [ stream . len ( ) - 1 ] , TokenTree ::Punct ( p ) if p . as_char ( ) = = '>' )
{
// Replace the group with concatenated token
* token = concat ( & stream [ 1 .. stream . len ( ) - 1 ] , span ) ;
} else {
// Recursively expand tokens inside the group
expand ( & mut stream ) ;
let mut group = Group ::new ( delimiter , stream . into_iter ( ) . collect ( ) ) ;
group . set_span ( span ) ;
* token = TokenTree ::Group ( group ) ;
}
}
}
// Path segments cannot contain invisible delimiter group, so remove them if any.
for i in ( 0 .. tokens . len ( ) . saturating_sub ( 3 ) ) . rev ( ) {
// Looking for a double colon
if matches! (
( & tokens [ i + 1 ] , & tokens [ i + 2 ] ) ,
( TokenTree ::Punct ( a ) , TokenTree ::Punct ( b ) )
if a . as_char ( ) = = ':' & & a . spacing ( ) = = Spacing ::Joint & & b . as_char ( ) = = ':'
) {
match & tokens [ i + 3 ] {
TokenTree ::Group ( group ) if group . delimiter ( ) = = Delimiter ::None = > {
tokens . splice ( i + 3 .. i + 4 , group . stream ( ) ) ;
}
_ = > ( ) ,
}
match & tokens [ i ] {
TokenTree ::Group ( group ) if group . delimiter ( ) = = Delimiter ::None = > {
tokens . splice ( i .. i + 1 , group . stream ( ) ) ;
}
_ = > ( ) ,
}
}
}
}