Skip to content

Commit 3c4ad7e

Browse files
committed
more flat tests
1 parent 4e446e7 commit 3c4ad7e

File tree

2 files changed

+364
-12
lines changed

2 files changed

+364
-12
lines changed

crates/proc-macro-api/src/legacy_protocol/msg/flat.rs

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

crates/tt/src/storage.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,18 +381,12 @@ pub struct TopSubtree {
381381

382382
impl TopSubtree {
383383
pub fn empty(span: DelimSpan) -> Self {
384-
Self {
385-
repr: TopSubtreeRepr::SpanStorage96(Box::new([TokenTree::Subtree {
386-
len: 0,
387-
delim_kind: DelimiterKind::Invisible,
388-
open_span: SpanStorage96::new(span.open.range, 0),
389-
close_span: SpanStorage96::new(span.close.range, 1),
390-
}])),
391-
span_parts: Box::new([
392-
CompressedSpanPart::from_span(&span.open),
393-
CompressedSpanPart::from_span(&span.close),
394-
]),
395-
}
384+
TopSubtreeBuilder::new(crate::Delimiter {
385+
kind: DelimiterKind::Invisible,
386+
open: span.open,
387+
close: span.close,
388+
})
389+
.build()
396390
}
397391

398392
pub fn invisible_from_leaves<const N: usize>(

0 commit comments

Comments
 (0)