@@ -1412,4 +1412,362 @@ mod tests {
14121412 assert_eq ! ( read. close, SpanId ( u32 :: MAX ) ) ;
14131413 assert_eq ! ( read. tt, [ u32 :: MAX , u32 :: MAX ] ) ;
14141414 }
1415+
1416+ // ==================== FlatTree from_subtree / to_subtree_resolved Tests ====================
1417+
1418+ fn build_complex_subtree ( span : Span ) -> tt:: TopSubtree {
1419+ // Build: { foo(123u32, "hello") -> r#type }
1420+ let delimiter = tt:: Delimiter { kind : tt:: DelimiterKind :: Brace , open : span, close : span } ;
1421+ let mut builder = tt:: TopSubtreeBuilder :: new ( delimiter) ;
1422+
1423+ // Add ident "foo"
1424+ builder. push ( tt:: Leaf :: Ident ( tt:: Ident {
1425+ sym : intern:: Symbol :: intern ( "foo" ) ,
1426+ span,
1427+ is_raw : tt:: IdentIsRaw :: No ,
1428+ } ) ) ;
1429+
1430+ // Add nested parentheses with contents
1431+ builder. open ( tt:: DelimiterKind :: Parenthesis , span) ;
1432+ builder. push ( tt:: Leaf :: Literal ( tt:: Literal :: new ( "123" , span, tt:: LitKind :: Integer , "u32" ) ) ) ;
1433+ builder. push ( tt:: Leaf :: Punct ( tt:: Punct { char : ',' , spacing : tt:: Spacing :: Alone , span } ) ) ;
1434+ builder. push ( tt:: Leaf :: Literal ( tt:: Literal :: new ( "hello" , span, tt:: LitKind :: Str , "" ) ) ) ;
1435+ builder. close ( span) ;
1436+ builder. push ( tt:: Leaf :: Punct ( tt:: Punct { char : '-' , spacing : tt:: Spacing :: Joint , span } ) ) ;
1437+ builder. push ( tt:: Leaf :: Punct ( tt:: Punct { char : '>' , spacing : tt:: Spacing :: Alone , span } ) ) ;
1438+ builder. push ( tt:: Leaf :: Ident ( tt:: Ident {
1439+ sym : intern:: Symbol :: intern ( "type" ) ,
1440+ span,
1441+ is_raw : tt:: IdentIsRaw :: Yes ,
1442+ } ) ) ;
1443+
1444+ builder. build ( )
1445+ }
1446+
1447+ #[ test]
1448+ fn test_flattree_from_subtree_empty ( ) {
1449+ let span = make_test_span ( 0 , 10 ) ;
1450+ let subtree = tt:: TopSubtree :: empty ( tt:: DelimSpan { open : span, close : span } ) ;
1451+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1452+
1453+ let version = crate :: version:: CURRENT_API_VERSION ;
1454+ let flat = FlatTree :: from_subtree ( subtree. view ( ) , version, & mut span_data_table) ;
1455+
1456+ // Should have one subtree (the root) with no children
1457+ assert_eq ! ( flat. subtree. len( ) , 5 ) ;
1458+ assert ! ( flat. literal. is_empty( ) ) ;
1459+ assert ! ( flat. punct. is_empty( ) ) ;
1460+ assert ! ( flat. ident. is_empty( ) ) ;
1461+
1462+ let restored = flat. to_subtree_resolved ( version, & span_data_table) ;
1463+ assert_eq ! ( subtree, restored) ;
1464+ }
1465+
1466+ #[ test]
1467+ fn test_flattree_from_subtree_complex_roundtrip ( ) {
1468+ let span = make_test_span ( 5 , 15 ) ;
1469+ let subtree = build_complex_subtree ( span) ;
1470+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1471+
1472+ let version = crate :: version:: CURRENT_API_VERSION ;
1473+ let flat = FlatTree :: from_subtree ( subtree. view ( ) , version, & mut span_data_table) ;
1474+
1475+ // Verify FlatTree has nested subtrees
1476+ // Root subtree + nested parentheses = 2 subtrees (encoded as 5 u32s each with close span)
1477+ assert_eq ! ( flat. subtree. len( ) , 10 ) ;
1478+ assert ! ( !flat. literal. is_empty( ) ) ;
1479+ assert ! ( !flat. punct. is_empty( ) ) ;
1480+ assert ! ( !flat. ident. is_empty( ) ) ;
1481+
1482+ // Should not contain the r#
1483+ assert_eq ! ( flat. text, & [ "foo" , "type" , "123" , "u32" , "hello" ] ) ;
1484+
1485+ // Roundtrip
1486+ let restored = flat. to_subtree_resolved ( version, & span_data_table) ;
1487+ assert_eq ! ( subtree, restored) ;
1488+ }
1489+
1490+ #[ test]
1491+ fn test_flattree_from_subtree_raw_ident_legacy_version ( ) {
1492+ let span = make_test_span ( 5 , 15 ) ;
1493+ let subtree = build_complex_subtree ( span) ;
1494+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1495+
1496+ let version = crate :: version:: ENCODE_CLOSE_SPAN_VERSION ;
1497+ let flat = FlatTree :: from_subtree ( subtree. view ( ) , version, & mut span_data_table) ;
1498+
1499+ // Verify FlatTree has nested subtrees
1500+ // Root subtree + nested parentheses = 2 subtrees (encoded as 5 u32s each with close span)
1501+ assert_eq ! ( flat. subtree. len( ) , 10 ) ;
1502+ assert ! ( !flat. literal. is_empty( ) ) ;
1503+ assert ! ( !flat. punct. is_empty( ) ) ;
1504+ assert ! ( !flat. ident. is_empty( ) ) ;
1505+
1506+ // Should contain the r#
1507+ assert_eq ! ( flat. text, & [ "foo" , "r#type" , "123u32" , "\" hello\" " ] ) ;
1508+
1509+ // Roundtrip
1510+ let restored = flat. to_subtree_resolved ( version, & span_data_table) ;
1511+ assert_eq ! ( subtree, restored) ;
1512+ }
1513+
1514+ #[ test]
1515+ fn test_flattree_from_subtree_deeply_nested ( ) {
1516+ let span = make_test_span ( 0 , 10 ) ;
1517+ let delimiter = tt:: Delimiter { kind : tt:: DelimiterKind :: Brace , open : span, close : span } ;
1518+ let mut builder = tt:: TopSubtreeBuilder :: new ( delimiter) ;
1519+
1520+ // Create deeply nested structure: { ( [ a ] ) }
1521+ builder. open ( tt:: DelimiterKind :: Parenthesis , span) ;
1522+ builder. open ( tt:: DelimiterKind :: Bracket , span) ;
1523+ builder. push ( tt:: Leaf :: Ident ( tt:: Ident {
1524+ sym : intern:: Symbol :: intern ( "a" ) ,
1525+ span,
1526+ is_raw : tt:: IdentIsRaw :: No ,
1527+ } ) ) ;
1528+ builder. close ( span) ;
1529+ builder. close ( span) ;
1530+
1531+ let subtree = builder. build ( ) ;
1532+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1533+
1534+ let version = crate :: version:: CURRENT_API_VERSION ;
1535+ let flat = FlatTree :: from_subtree ( subtree. view ( ) , version, & mut span_data_table) ;
1536+
1537+ let restored = flat. to_subtree_resolved ( version, & span_data_table) ;
1538+ assert_eq ! ( subtree, restored) ;
1539+ }
1540+
1541+ #[ test]
1542+ fn test_flattree_from_subtree_multiple_spans ( ) {
1543+ let span1 = make_test_span ( 0 , 10 ) ;
1544+ let span2 = make_test_span ( 15 , 25 ) ;
1545+ let span3 = make_test_span ( 30 , 40 ) ;
1546+
1547+ let delimiter = tt:: Delimiter { kind : tt:: DelimiterKind :: Brace , open : span1, close : span3 } ;
1548+ let mut builder = tt:: TopSubtreeBuilder :: new ( delimiter) ;
1549+ builder. push ( tt:: Leaf :: Ident ( tt:: Ident {
1550+ sym : intern:: Symbol :: intern ( "x" ) ,
1551+ span : span2,
1552+ is_raw : tt:: IdentIsRaw :: No ,
1553+ } ) ) ;
1554+
1555+ let subtree = builder. build ( ) ;
1556+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1557+
1558+ let version = crate :: version:: CURRENT_API_VERSION ;
1559+ let flat = FlatTree :: from_subtree ( subtree. view ( ) , version, & mut span_data_table) ;
1560+
1561+ // All three different spans should be in the table
1562+ assert ! ( span_data_table. len( ) >= 2 ) ; // At least 2 unique spans (open/close may be same)
1563+
1564+ let restored = flat. to_subtree_resolved ( version, & span_data_table) ;
1565+ assert_eq ! ( subtree, restored) ;
1566+ }
1567+
1568+ // ==================== FlatTree TokenStream Tests ====================
1569+
1570+ #[ cfg( feature = "sysroot-abi" ) ]
1571+ fn build_complex_tokenstream ( span : Span ) -> proc_macro_srv:: TokenStream < Span > {
1572+ use proc_macro_srv:: { DelimSpan , Group , Ident , Literal , Punct , TokenTree } ;
1573+
1574+ // Build: { foo(123u32, "hello") -> r#type }
1575+ let delim_span = DelimSpan { open : span, close : span, entire : span } ;
1576+
1577+ // Inner parentheses content
1578+ let inner_stream = proc_macro_srv:: TokenStream :: new ( vec ! [
1579+ TokenTree :: Literal ( Literal {
1580+ symbol: intern:: Symbol :: intern( "123" ) ,
1581+ span,
1582+ kind: proc_macro_srv:: LitKind :: Integer ,
1583+ suffix: Some ( intern:: Symbol :: intern( "u32" ) ) ,
1584+ } ) ,
1585+ TokenTree :: Punct ( Punct { ch: b',' , joint: false , span } ) ,
1586+ TokenTree :: Literal ( Literal {
1587+ symbol: intern:: Symbol :: intern( "hello" ) ,
1588+ span,
1589+ kind: proc_macro_srv:: LitKind :: Str ,
1590+ suffix: None ,
1591+ } ) ,
1592+ ] ) ;
1593+
1594+ // Build outer brace content
1595+ let outer_stream = proc_macro_srv:: TokenStream :: new ( vec ! [
1596+ TokenTree :: Ident ( Ident { sym: intern:: Symbol :: intern( "foo" ) , span, is_raw: false } ) ,
1597+ TokenTree :: Group ( Group {
1598+ delimiter: proc_macro_srv:: Delimiter :: Parenthesis ,
1599+ stream: Some ( inner_stream) ,
1600+ span: delim_span,
1601+ } ) ,
1602+ TokenTree :: Punct ( Punct { ch: b'-' , joint: true , span } ) ,
1603+ TokenTree :: Punct ( Punct { ch: b'>' , joint: false , span } ) ,
1604+ TokenTree :: Ident ( Ident { sym: intern:: Symbol :: intern( "type" ) , span, is_raw: true } ) ,
1605+ ] ) ;
1606+
1607+ proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1608+ delimiter: proc_macro_srv:: Delimiter :: Brace ,
1609+ stream: Some ( outer_stream) ,
1610+ span: delim_span,
1611+ } ) ] )
1612+ }
1613+
1614+ #[ cfg( feature = "sysroot-abi" ) ]
1615+ #[ test]
1616+ fn test_flattree_from_tokenstream_empty ( ) {
1617+ use proc_macro_srv:: { DelimSpan , Group , TokenTree } ;
1618+
1619+ let span = make_test_span ( 0 , 10 ) ;
1620+ let delim_span = DelimSpan { open : span, close : span, entire : span } ;
1621+ let tokenstream = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1622+ delimiter: proc_macro_srv:: Delimiter :: Brace ,
1623+ stream: None ,
1624+ span: delim_span,
1625+ } ) ] ) ;
1626+
1627+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1628+ let version = crate :: version:: CURRENT_API_VERSION ;
1629+ let flat =
1630+ FlatTree :: from_tokenstream ( tokenstream. clone ( ) , version, span, & mut span_data_table) ;
1631+
1632+ // Should have one subtree (the root group) with no children
1633+ assert_eq ! ( flat. subtree. len( ) , 5 ) ;
1634+ assert ! ( flat. literal. is_empty( ) ) ;
1635+ assert ! ( flat. punct. is_empty( ) ) ;
1636+ assert ! ( flat. ident. is_empty( ) ) ;
1637+
1638+ // Roundtrip
1639+ let restored = flat. to_tokenstream_resolved ( version, & span_data_table, |a, _| a) ;
1640+ assert_eq ! ( tokenstream. to_string( ) , restored. to_string( ) ) ;
1641+ }
1642+
1643+ #[ cfg( feature = "sysroot-abi" ) ]
1644+ #[ test]
1645+ fn test_flattree_from_tokenstream_complex_roundtrip ( ) {
1646+ let span = make_test_span ( 5 , 15 ) ;
1647+ let tokenstream = build_complex_tokenstream ( span) ;
1648+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1649+
1650+ let version = crate :: version:: CURRENT_API_VERSION ;
1651+ let flat =
1652+ FlatTree :: from_tokenstream ( tokenstream. clone ( ) , version, span, & mut span_data_table) ;
1653+
1654+ // Verify FlatTree has content
1655+ assert_eq ! ( flat. subtree. len( ) , 10 ) ;
1656+ assert ! ( !flat. literal. is_empty( ) ) ;
1657+ assert ! ( !flat. punct. is_empty( ) ) ;
1658+ assert ! ( !flat. ident. is_empty( ) ) ;
1659+
1660+ // Should not contain the r#
1661+ assert_eq ! ( flat. text, & [ "foo" , "type" , "123" , "u32" , "hello" ] ) ;
1662+
1663+ // Roundtrip
1664+ let restored = flat. to_tokenstream_resolved ( version, & span_data_table, |a, _| a) ;
1665+ assert_eq ! ( tokenstream. to_string( ) , restored. to_string( ) ) ;
1666+ }
1667+
1668+ #[ cfg( feature = "sysroot-abi" ) ]
1669+ #[ test]
1670+ fn test_flattree_from_tokenstream_legacy_version ( ) {
1671+ let span = make_test_span ( 5 , 15 ) ;
1672+ let tokenstream = build_complex_tokenstream ( span) ;
1673+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1674+
1675+ let version = crate :: version:: ENCODE_CLOSE_SPAN_VERSION ;
1676+ let flat =
1677+ FlatTree :: from_tokenstream ( tokenstream. clone ( ) , version, span, & mut span_data_table) ;
1678+
1679+ // Verify FlatTree has content
1680+ assert_eq ! ( flat. subtree. len( ) , 10 ) ;
1681+ assert ! ( !flat. literal. is_empty( ) ) ;
1682+ assert ! ( !flat. punct. is_empty( ) ) ;
1683+ assert ! ( !flat. ident. is_empty( ) ) ;
1684+
1685+ // Should contain the r#
1686+ assert_eq ! ( flat. text, & [ "foo" , "r#type" , "123u32" , "\" hello\" " ] ) ;
1687+
1688+ // Roundtrip
1689+ let restored = flat. to_tokenstream_resolved ( version, & span_data_table, |a, _| a) ;
1690+ assert_eq ! ( tokenstream. to_string( ) , restored. to_string( ) ) ;
1691+ }
1692+
1693+ #[ cfg( feature = "sysroot-abi" ) ]
1694+ #[ test]
1695+ fn test_flattree_from_tokenstream_multiple_spans ( ) {
1696+ use proc_macro_srv:: { DelimSpan , Group , Ident , Punct , TokenTree } ;
1697+
1698+ let span1 = make_test_span ( 0 , 10 ) ;
1699+ let span2 = make_test_span ( 15 , 25 ) ;
1700+ let span3 = make_test_span ( 30 , 40 ) ;
1701+
1702+ let delim_span = DelimSpan { open : span1, close : span3, entire : span1 } ;
1703+ let inner_stream = proc_macro_srv:: TokenStream :: new ( vec ! [
1704+ TokenTree :: Ident ( Ident {
1705+ sym: intern:: Symbol :: intern( "x" ) ,
1706+ span: span2,
1707+ is_raw: false ,
1708+ } ) ,
1709+ TokenTree :: Punct ( Punct { ch: b'+' , joint: false , span: span3 } ) ,
1710+ ] ) ;
1711+
1712+ let tokenstream = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1713+ delimiter: proc_macro_srv:: Delimiter :: Brace ,
1714+ stream: Some ( inner_stream) ,
1715+ span: delim_span,
1716+ } ) ] ) ;
1717+
1718+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1719+ let version = crate :: version:: CURRENT_API_VERSION ;
1720+ let flat =
1721+ FlatTree :: from_tokenstream ( tokenstream. clone ( ) , version, span1, & mut span_data_table) ;
1722+
1723+ // Multiple different spans should be in the table
1724+ assert ! ( span_data_table. len( ) >= 2 ) ;
1725+
1726+ // Roundtrip
1727+ let restored = flat. to_tokenstream_resolved ( version, & span_data_table, |a, _| a) ;
1728+ assert_eq ! ( tokenstream. to_string( ) , restored. to_string( ) ) ;
1729+ }
1730+
1731+ #[ cfg( feature = "sysroot-abi" ) ]
1732+ #[ test]
1733+ fn test_flattree_from_tokenstream_deeply_nested ( ) {
1734+ use proc_macro_srv:: { DelimSpan , Group , Ident , TokenTree } ;
1735+
1736+ let span = make_test_span ( 0 , 10 ) ;
1737+ let delim_span = DelimSpan { open : span, close : span, entire : span } ;
1738+
1739+ // Create deeply nested structure: { ( [ a ] ) }
1740+ let innermost = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Ident ( Ident {
1741+ sym: intern:: Symbol :: intern( "a" ) ,
1742+ span,
1743+ is_raw: false ,
1744+ } ) ] ) ;
1745+
1746+ let bracket = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1747+ delimiter: proc_macro_srv:: Delimiter :: Bracket ,
1748+ stream: Some ( innermost) ,
1749+ span: delim_span,
1750+ } ) ] ) ;
1751+
1752+ let paren = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1753+ delimiter: proc_macro_srv:: Delimiter :: Parenthesis ,
1754+ stream: Some ( bracket) ,
1755+ span: delim_span,
1756+ } ) ] ) ;
1757+
1758+ let tokenstream = proc_macro_srv:: TokenStream :: new ( vec ! [ TokenTree :: Group ( Group {
1759+ delimiter: proc_macro_srv:: Delimiter :: Brace ,
1760+ stream: Some ( paren) ,
1761+ span: delim_span,
1762+ } ) ] ) ;
1763+
1764+ let mut span_data_table = SpanDataIndexMap :: default ( ) ;
1765+ let version = crate :: version:: CURRENT_API_VERSION ;
1766+ let flat =
1767+ FlatTree :: from_tokenstream ( tokenstream. clone ( ) , version, span, & mut span_data_table) ;
1768+
1769+ // Roundtrip
1770+ let restored = flat. to_tokenstream_resolved ( version, & span_data_table, |a, _| a) ;
1771+ assert_eq ! ( tokenstream. to_string( ) , restored. to_string( ) ) ;
1772+ }
14151773}
0 commit comments