| //! Spans are memory heavy, and we have a lot of token trees. Storing them straight |
| //! will waste a lot of memory. So instead we implement a clever compression mechanism: |
| //! |
| //! A `TopSubtree` has a list of [`CompressedSpanPart`], which are the parts of a span |
| //! that tend to be shared between tokens - namely, without the range. The main list |
| //! of token trees is kept in one of three versions, where we use the smallest version |
| //! we can for this tree: |
| //! |
| //! 1. In the most common version a span is just a `u32`. The bits are divided as follows: |
| //! there are 4 bits that index into the [`CompressedSpanPart`] list. 20 bits |
| //! store the range start, and 8 bits store the range length. In experiments, |
| //! this accounts for 75%-85% of the spans. |
| //! 2. In the second version a span is 64 bits. 32 bits for the range start, 16 bits |
| //! for the range length, and 16 bits for the span parts index. This is used in |
| //! less than 2% of all `TopSubtree`s, but they account for 15%-25% of the spans: |
| //! those are mostly token tree munchers, that generate a lot of `SyntaxContext`s |
| //! (because they recurse a lot), which is why they can't fit in the first version, |
| //! and tend to generate a lot of code. |
| //! 3. The third version is practically unused; 65,535 bytes for a token and 65,535 |
| //! unique span parts is more than enough for everybody. However, someone may still |
| //! create a macro that requires more, therefore we have this version as a backup: |
| //! it uses 96 bits, 32 for each of the range start, length and span parts index. |
| |
| use std::fmt; |
| |
| use intern::Symbol; |
| use rustc_hash::FxBuildHasher; |
| use span::{Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; |
| |
| use crate::{ |
| DelimSpan, DelimiterKind, IdentIsRaw, LitKind, Spacing, SubtreeView, TokenTreesReprRef, |
| TokenTreesView, TtIter, dispatch_ref, |
| }; |
| |
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
| pub(crate) struct CompressedSpanPart { |
| pub(crate) anchor: SpanAnchor, |
| pub(crate) ctx: SyntaxContext, |
| } |
| |
| impl CompressedSpanPart { |
| #[inline] |
| fn from_span(span: &Span) -> Self { |
| Self { anchor: span.anchor, ctx: span.ctx } |
| } |
| |
| #[inline] |
| fn recombine(&self, range: TextRange) -> Span { |
| Span { range, anchor: self.anchor, ctx: self.ctx } |
| } |
| } |
| |
| pub(crate) trait SpanStorage: Copy { |
| fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool; |
| |
| fn new(text_range: TextRange, span_parts_index: usize) -> Self; |
| |
| fn text_range(&self) -> TextRange; |
| |
| fn span_parts_index(&self) -> usize; |
| |
| #[inline] |
| fn span(&self, span_parts: &[CompressedSpanPart]) -> Span { |
| span_parts[self.span_parts_index()].recombine(self.text_range()) |
| } |
| } |
| |
| #[inline] |
| const fn n_bits_mask(n: u32) -> u32 { |
| (1 << n) - 1 |
| } |
| |
| #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
| pub(crate) struct SpanStorage32(u32); |
| |
| impl SpanStorage32 { |
| const SPAN_PARTS_BIT: u32 = 4; |
| const LEN_BITS: u32 = 8; |
| const OFFSET_BITS: u32 = 20; |
| } |
| |
| const _: () = assert!( |
| (SpanStorage32::SPAN_PARTS_BIT + SpanStorage32::LEN_BITS + SpanStorage32::OFFSET_BITS) |
| == u32::BITS |
| ); |
| |
| impl SpanStorage for SpanStorage32 { |
| #[inline] |
| fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool { |
| let offset = u32::from(text_range.start()); |
| let len = u32::from(text_range.len()); |
| let span_parts_index = span_parts_index as u32; |
| |
| offset <= n_bits_mask(Self::OFFSET_BITS) |
| && len <= n_bits_mask(Self::LEN_BITS) |
| && span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT) |
| } |
| |
| #[inline] |
| fn new(text_range: TextRange, span_parts_index: usize) -> Self { |
| let offset = u32::from(text_range.start()); |
| let len = u32::from(text_range.len()); |
| let span_parts_index = span_parts_index as u32; |
| |
| debug_assert!(offset <= n_bits_mask(Self::OFFSET_BITS)); |
| debug_assert!(len <= n_bits_mask(Self::LEN_BITS)); |
| debug_assert!(span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT)); |
| |
| Self( |
| (offset << (Self::LEN_BITS + Self::SPAN_PARTS_BIT)) |
| | (len << Self::SPAN_PARTS_BIT) |
| | span_parts_index, |
| ) |
| } |
| |
| #[inline] |
| fn text_range(&self) -> TextRange { |
| let offset = TextSize::new(self.0 >> (Self::SPAN_PARTS_BIT + Self::LEN_BITS)); |
| let len = TextSize::new((self.0 >> Self::SPAN_PARTS_BIT) & n_bits_mask(Self::LEN_BITS)); |
| TextRange::at(offset, len) |
| } |
| |
| #[inline] |
| fn span_parts_index(&self) -> usize { |
| (self.0 & n_bits_mask(Self::SPAN_PARTS_BIT)) as usize |
| } |
| } |
| |
| impl fmt::Debug for SpanStorage32 { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_struct("SpanStorage32") |
| .field("text_range", &self.text_range()) |
| .field("span_parts_index", &self.span_parts_index()) |
| .finish() |
| } |
| } |
| |
| #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
| pub(crate) struct SpanStorage64 { |
| offset: u32, |
| len_and_parts: u32, |
| } |
| |
| impl SpanStorage64 { |
| const SPAN_PARTS_BIT: u32 = 16; |
| const LEN_BITS: u32 = 16; |
| } |
| |
| const _: () = assert!((SpanStorage64::SPAN_PARTS_BIT + SpanStorage64::LEN_BITS) == u32::BITS); |
| |
| impl SpanStorage for SpanStorage64 { |
| #[inline] |
| fn can_hold(text_range: TextRange, span_parts_index: usize) -> bool { |
| let len = u32::from(text_range.len()); |
| let span_parts_index = span_parts_index as u32; |
| |
| len <= n_bits_mask(Self::LEN_BITS) && span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT) |
| } |
| |
| #[inline] |
| fn new(text_range: TextRange, span_parts_index: usize) -> Self { |
| let offset = u32::from(text_range.start()); |
| let len = u32::from(text_range.len()); |
| let span_parts_index = span_parts_index as u32; |
| |
| debug_assert!(len <= n_bits_mask(Self::LEN_BITS)); |
| debug_assert!(span_parts_index <= n_bits_mask(Self::SPAN_PARTS_BIT)); |
| |
| Self { offset, len_and_parts: (len << Self::SPAN_PARTS_BIT) | span_parts_index } |
| } |
| |
| #[inline] |
| fn text_range(&self) -> TextRange { |
| let offset = TextSize::new(self.offset); |
| let len = TextSize::new(self.len_and_parts >> Self::SPAN_PARTS_BIT); |
| TextRange::at(offset, len) |
| } |
| |
| #[inline] |
| fn span_parts_index(&self) -> usize { |
| (self.len_and_parts & n_bits_mask(Self::SPAN_PARTS_BIT)) as usize |
| } |
| } |
| |
| impl fmt::Debug for SpanStorage64 { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_struct("SpanStorage64") |
| .field("text_range", &self.text_range()) |
| .field("span_parts_index", &self.span_parts_index()) |
| .finish() |
| } |
| } |
| |
| impl From<SpanStorage32> for SpanStorage64 { |
| #[inline] |
| fn from(value: SpanStorage32) -> Self { |
| SpanStorage64::new(value.text_range(), value.span_parts_index()) |
| } |
| } |
| |
| #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
| pub(crate) struct SpanStorage96 { |
| offset: u32, |
| len: u32, |
| parts: u32, |
| } |
| |
| impl SpanStorage for SpanStorage96 { |
| #[inline] |
| fn can_hold(_text_range: TextRange, _span_parts_index: usize) -> bool { |
| true |
| } |
| |
| #[inline] |
| fn new(text_range: TextRange, span_parts_index: usize) -> Self { |
| let offset = u32::from(text_range.start()); |
| let len = u32::from(text_range.len()); |
| let span_parts_index = span_parts_index as u32; |
| |
| Self { offset, len, parts: span_parts_index } |
| } |
| |
| #[inline] |
| fn text_range(&self) -> TextRange { |
| let offset = TextSize::new(self.offset); |
| let len = TextSize::new(self.len); |
| TextRange::at(offset, len) |
| } |
| |
| #[inline] |
| fn span_parts_index(&self) -> usize { |
| self.parts as usize |
| } |
| } |
| |
| impl fmt::Debug for SpanStorage96 { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_struct("SpanStorage96") |
| .field("text_range", &self.text_range()) |
| .field("span_parts_index", &self.span_parts_index()) |
| .finish() |
| } |
| } |
| |
| impl From<SpanStorage32> for SpanStorage96 { |
| #[inline] |
| fn from(value: SpanStorage32) -> Self { |
| SpanStorage96::new(value.text_range(), value.span_parts_index()) |
| } |
| } |
| |
| impl From<SpanStorage64> for SpanStorage96 { |
| #[inline] |
| fn from(value: SpanStorage64) -> Self { |
| SpanStorage96::new(value.text_range(), value.span_parts_index()) |
| } |
| } |
| |
| // We don't use structs or enum nesting here to save padding. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub(crate) enum TokenTree<S> { |
| Literal { text_and_suffix: Symbol, span: S, kind: LitKind, suffix_len: u8 }, |
| Punct { char: char, spacing: Spacing, span: S }, |
| Ident { sym: Symbol, span: S, is_raw: IdentIsRaw }, |
| Subtree { len: u32, delim_kind: DelimiterKind, open_span: S, close_span: S }, |
| } |
| |
| impl<S: SpanStorage> TokenTree<S> { |
| #[inline] |
| pub(crate) fn first_span(&self) -> &S { |
| match self { |
| TokenTree::Literal { span, .. } => span, |
| TokenTree::Punct { span, .. } => span, |
| TokenTree::Ident { span, .. } => span, |
| TokenTree::Subtree { open_span, .. } => open_span, |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn last_span(&self) -> &S { |
| match self { |
| TokenTree::Literal { span, .. } => span, |
| TokenTree::Punct { span, .. } => span, |
| TokenTree::Ident { span, .. } => span, |
| TokenTree::Subtree { close_span, .. } => close_span, |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn to_api(&self, span_parts: &[CompressedSpanPart]) -> crate::TokenTree { |
| match self { |
| TokenTree::Literal { text_and_suffix, span, kind, suffix_len } => { |
| crate::TokenTree::Leaf(crate::Leaf::Literal(crate::Literal { |
| text_and_suffix: text_and_suffix.clone(), |
| span: span.span(span_parts), |
| kind: *kind, |
| suffix_len: *suffix_len, |
| })) |
| } |
| TokenTree::Punct { char, spacing, span } => { |
| crate::TokenTree::Leaf(crate::Leaf::Punct(crate::Punct { |
| char: *char, |
| spacing: *spacing, |
| span: span.span(span_parts), |
| })) |
| } |
| TokenTree::Ident { sym, span, is_raw } => { |
| crate::TokenTree::Leaf(crate::Leaf::Ident(crate::Ident { |
| sym: sym.clone(), |
| span: span.span(span_parts), |
| is_raw: *is_raw, |
| })) |
| } |
| TokenTree::Subtree { len, delim_kind, open_span, close_span } => { |
| crate::TokenTree::Subtree(crate::Subtree { |
| delimiter: crate::Delimiter { |
| open: open_span.span(span_parts), |
| close: close_span.span(span_parts), |
| kind: *delim_kind, |
| }, |
| len: *len, |
| }) |
| } |
| } |
| } |
| |
| #[inline] |
| fn convert<U: From<S>>(self) -> TokenTree<U> { |
| match self { |
| TokenTree::Literal { text_and_suffix, span, kind, suffix_len } => { |
| TokenTree::Literal { text_and_suffix, span: span.into(), kind, suffix_len } |
| } |
| TokenTree::Punct { char, spacing, span } => { |
| TokenTree::Punct { char, spacing, span: span.into() } |
| } |
| TokenTree::Ident { sym, span, is_raw } => { |
| TokenTree::Ident { sym, span: span.into(), is_raw } |
| } |
| TokenTree::Subtree { len, delim_kind, open_span, close_span } => TokenTree::Subtree { |
| len, |
| delim_kind, |
| open_span: open_span.into(), |
| close_span: close_span.into(), |
| }, |
| } |
| } |
| } |
| |
| // This is used a lot, make sure it doesn't grow unintentionally. |
| const _: () = { |
| assert!(size_of::<TokenTree<SpanStorage32>>() == 16); |
| assert!(size_of::<TokenTree<SpanStorage64>>() == 24); |
| assert!(size_of::<TokenTree<SpanStorage96>>() == 32); |
| }; |
| |
| #[rust_analyzer::macro_style(braces)] |
| macro_rules! dispatch { |
| ( |
| match $scrutinee:expr => $tt:ident => $body:expr |
| ) => { |
| match $scrutinee { |
| TopSubtreeRepr::SpanStorage32($tt) => $body, |
| TopSubtreeRepr::SpanStorage64($tt) => $body, |
| TopSubtreeRepr::SpanStorage96($tt) => $body, |
| } |
| }; |
| } |
| |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub(crate) enum TopSubtreeRepr { |
| SpanStorage32(Box<[TokenTree<SpanStorage32>]>), |
| SpanStorage64(Box<[TokenTree<SpanStorage64>]>), |
| SpanStorage96(Box<[TokenTree<SpanStorage96>]>), |
| } |
| |
| #[derive(Clone, PartialEq, Eq, Hash)] |
| pub struct TopSubtree { |
| repr: TopSubtreeRepr, |
| span_parts: Box<[CompressedSpanPart]>, |
| } |
| |
| impl TopSubtree { |
| pub fn empty(span: DelimSpan) -> Self { |
| Self { |
| repr: TopSubtreeRepr::SpanStorage96(Box::new([TokenTree::Subtree { |
| len: 0, |
| delim_kind: DelimiterKind::Invisible, |
| open_span: SpanStorage96::new(span.open.range, 0), |
| close_span: SpanStorage96::new(span.close.range, 1), |
| }])), |
| span_parts: Box::new([ |
| CompressedSpanPart::from_span(&span.open), |
| CompressedSpanPart::from_span(&span.close), |
| ]), |
| } |
| } |
| |
| pub fn invisible_from_leaves<const N: usize>( |
| delim_span: Span, |
| leaves: [crate::Leaf; N], |
| ) -> Self { |
| let mut builder = TopSubtreeBuilder::new(crate::Delimiter::invisible_spanned(delim_span)); |
| builder.extend(leaves); |
| builder.build() |
| } |
| |
| pub fn from_token_trees(delimiter: crate::Delimiter, token_trees: TokenTreesView<'_>) -> Self { |
| let mut builder = TopSubtreeBuilder::new(delimiter); |
| builder.extend_with_tt(token_trees); |
| builder.build() |
| } |
| |
| pub fn from_serialized(tt: Vec<crate::TokenTree>) -> Self { |
| let mut tt = tt.into_iter(); |
| let Some(crate::TokenTree::Subtree(top_subtree)) = tt.next() else { |
| panic!("first must always come the top subtree") |
| }; |
| let mut builder = TopSubtreeBuilder::new(top_subtree.delimiter); |
| for tt in tt { |
| builder.push_token_tree(tt); |
| } |
| builder.build() |
| } |
| |
| pub fn from_subtree(subtree: SubtreeView<'_>) -> Self { |
| let mut builder = TopSubtreeBuilder::new(subtree.top_subtree().delimiter); |
| builder.extend_with_tt(subtree.token_trees()); |
| builder.build() |
| } |
| |
| pub fn view(&self) -> SubtreeView<'_> { |
| let repr = match &self.repr { |
| TopSubtreeRepr::SpanStorage32(token_trees) => { |
| TokenTreesReprRef::SpanStorage32(token_trees) |
| } |
| TopSubtreeRepr::SpanStorage64(token_trees) => { |
| TokenTreesReprRef::SpanStorage64(token_trees) |
| } |
| TopSubtreeRepr::SpanStorage96(token_trees) => { |
| TokenTreesReprRef::SpanStorage96(token_trees) |
| } |
| }; |
| SubtreeView(TokenTreesView { repr, span_parts: &self.span_parts }) |
| } |
| |
| pub fn iter(&self) -> TtIter<'_> { |
| self.view().iter() |
| } |
| |
| pub fn top_subtree(&self) -> crate::Subtree { |
| self.view().top_subtree() |
| } |
| |
| pub fn set_top_subtree_delimiter_kind(&mut self, kind: DelimiterKind) { |
| dispatch! { |
| match &mut self.repr => tt => { |
| let TokenTree::Subtree { delim_kind, .. } = &mut tt[0] else { |
| unreachable!("the first token tree is always the top subtree"); |
| }; |
| *delim_kind = kind; |
| } |
| } |
| } |
| |
| fn ensure_can_hold(&mut self, range: TextRange) { |
| fn can_hold<S: SpanStorage>(_: &[TokenTree<S>], range: TextRange) -> bool { |
| S::can_hold(range, 0) |
| } |
| let can_hold = dispatch! { |
| match &self.repr => tt => can_hold(tt, range) |
| }; |
| if can_hold { |
| return; |
| } |
| |
| // Otherwise, we do something very junky: recreate the entire tree. Hopefully this should be rare. |
| let mut builder = TopSubtreeBuilder::new(self.top_subtree().delimiter); |
| builder.extend_with_tt(self.token_trees()); |
| builder.ensure_can_hold(range, 0); |
| *self = builder.build(); |
| } |
| |
| pub fn set_top_subtree_delimiter_span(&mut self, span: DelimSpan) { |
| self.ensure_can_hold(span.open.range); |
| self.ensure_can_hold(span.close.range); |
| fn do_it<S: SpanStorage>(tt: &mut [TokenTree<S>], span: DelimSpan) { |
| let TokenTree::Subtree { open_span, close_span, .. } = &mut tt[0] else { |
| unreachable!() |
| }; |
| *open_span = S::new(span.open.range, 0); |
| *close_span = S::new(span.close.range, 1); |
| } |
| dispatch! { |
| match &mut self.repr => tt => do_it(tt, span) |
| } |
| self.span_parts[0] = CompressedSpanPart::from_span(&span.open); |
| self.span_parts[1] = CompressedSpanPart::from_span(&span.close); |
| } |
| |
| /// Note: this cannot change spans. |
| pub fn set_token(&mut self, idx: usize, leaf: crate::Leaf) { |
| fn do_it<S: SpanStorage>( |
| tt: &mut [TokenTree<S>], |
| idx: usize, |
| span_parts: &[CompressedSpanPart], |
| leaf: crate::Leaf, |
| ) { |
| assert!( |
| !matches!(tt[idx], TokenTree::Subtree { .. }), |
| "`TopSubtree::set_token()` must be called on a leaf" |
| ); |
| let existing_span_compressed = *tt[idx].first_span(); |
| let existing_span = existing_span_compressed.span(span_parts); |
| assert_eq!( |
| *leaf.span(), |
| existing_span, |
| "`TopSubtree::set_token()` cannot change spans" |
| ); |
| match leaf { |
| crate::Leaf::Literal(leaf) => { |
| tt[idx] = TokenTree::Literal { |
| text_and_suffix: leaf.text_and_suffix, |
| span: existing_span_compressed, |
| kind: leaf.kind, |
| suffix_len: leaf.suffix_len, |
| } |
| } |
| crate::Leaf::Punct(leaf) => { |
| tt[idx] = TokenTree::Punct { |
| char: leaf.char, |
| spacing: leaf.spacing, |
| span: existing_span_compressed, |
| } |
| } |
| crate::Leaf::Ident(leaf) => { |
| tt[idx] = TokenTree::Ident { |
| sym: leaf.sym, |
| span: existing_span_compressed, |
| is_raw: leaf.is_raw, |
| } |
| } |
| } |
| } |
| dispatch! { |
| match &mut self.repr => tt => do_it(tt, idx, &self.span_parts, leaf) |
| } |
| } |
| |
| pub fn token_trees(&self) -> TokenTreesView<'_> { |
| self.view().token_trees() |
| } |
| |
| pub fn as_token_trees(&self) -> TokenTreesView<'_> { |
| self.view().as_token_trees() |
| } |
| |
| pub fn change_every_ast_id(&mut self, mut callback: impl FnMut(&mut span::ErasedFileAstId)) { |
| for span_part in &mut self.span_parts { |
| callback(&mut span_part.anchor.ast_id); |
| } |
| } |
| } |
| |
| #[rust_analyzer::macro_style(braces)] |
| macro_rules! dispatch_builder { |
| ( |
| match $scrutinee:expr => $tt:ident => $body:expr |
| ) => { |
| match $scrutinee { |
| TopSubtreeBuilderRepr::SpanStorage32($tt) => $body, |
| TopSubtreeBuilderRepr::SpanStorage64($tt) => $body, |
| TopSubtreeBuilderRepr::SpanStorage96($tt) => $body, |
| } |
| }; |
| } |
| |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| enum TopSubtreeBuilderRepr { |
| SpanStorage32(Vec<TokenTree<SpanStorage32>>), |
| SpanStorage64(Vec<TokenTree<SpanStorage64>>), |
| SpanStorage96(Vec<TokenTree<SpanStorage96>>), |
| } |
| |
| type FxIndexSet<K> = indexmap::IndexSet<K, FxBuildHasher>; |
| |
| /// In any tree, the first two subtree parts are reserved for the top subtree. |
| /// |
| /// We do it because `TopSubtree` exposes an API to modify the top subtree, therefore it's more convenient |
| /// this way, and it's unlikely to affect memory usage. |
| const RESERVED_SPAN_PARTS_LEN: usize = 2; |
| |
| #[derive(Debug, Clone)] |
| pub struct TopSubtreeBuilder { |
| unclosed_subtree_indices: Vec<usize>, |
| token_trees: TopSubtreeBuilderRepr, |
| span_parts: FxIndexSet<CompressedSpanPart>, |
| last_closed_subtree: Option<usize>, |
| /// We need to keep those because they are not inside `span_parts`, see [`RESERVED_SPAN_PARTS_LEN`]. |
| top_subtree_spans: DelimSpan, |
| } |
| |
| impl TopSubtreeBuilder { |
| pub fn new(top_delimiter: crate::Delimiter) -> Self { |
| let mut result = Self { |
| unclosed_subtree_indices: Vec::new(), |
| token_trees: TopSubtreeBuilderRepr::SpanStorage32(Vec::new()), |
| span_parts: FxIndexSet::default(), |
| last_closed_subtree: None, |
| top_subtree_spans: top_delimiter.delim_span(), |
| }; |
| result.ensure_can_hold(top_delimiter.open.range, 0); |
| result.ensure_can_hold(top_delimiter.close.range, 1); |
| fn push_first<S: SpanStorage>(tt: &mut Vec<TokenTree<S>>, top_delimiter: crate::Delimiter) { |
| tt.push(TokenTree::Subtree { |
| len: 0, |
| delim_kind: top_delimiter.kind, |
| open_span: S::new(top_delimiter.open.range, 0), |
| close_span: S::new(top_delimiter.close.range, 1), |
| }); |
| } |
| dispatch_builder! { |
| match &mut result.token_trees => tt => push_first(tt, top_delimiter) |
| } |
| result |
| } |
| |
| fn span_part_index(&mut self, part: CompressedSpanPart) -> usize { |
| self.span_parts.insert_full(part).0 + RESERVED_SPAN_PARTS_LEN |
| } |
| |
| fn switch_repr<T: SpanStorage, U: From<T>>(repr: &mut Vec<TokenTree<T>>) -> Vec<TokenTree<U>> { |
| let repr = std::mem::take(repr); |
| repr.into_iter().map(|tt| tt.convert()).collect() |
| } |
| |
| /// Ensures we have a representation that can hold these values. |
| fn ensure_can_hold(&mut self, text_range: TextRange, span_parts_index: usize) { |
| match &mut self.token_trees { |
| TopSubtreeBuilderRepr::SpanStorage32(token_trees) => { |
| if SpanStorage32::can_hold(text_range, span_parts_index) { |
| // Can hold. |
| } else if SpanStorage64::can_hold(text_range, span_parts_index) { |
| self.token_trees = |
| TopSubtreeBuilderRepr::SpanStorage64(Self::switch_repr(token_trees)); |
| } else { |
| self.token_trees = |
| TopSubtreeBuilderRepr::SpanStorage96(Self::switch_repr(token_trees)); |
| } |
| } |
| TopSubtreeBuilderRepr::SpanStorage64(token_trees) => { |
| if SpanStorage64::can_hold(text_range, span_parts_index) { |
| // Can hold. |
| } else { |
| self.token_trees = |
| TopSubtreeBuilderRepr::SpanStorage96(Self::switch_repr(token_trees)); |
| } |
| } |
| TopSubtreeBuilderRepr::SpanStorage96(_) => { |
| // Can hold anything. |
| } |
| } |
| } |
| |
| /// Not to be exposed, this assumes the subtree's children will be filled in immediately. |
| fn push_subtree(&mut self, subtree: crate::Subtree) { |
| let open_span_parts_index = |
| self.span_part_index(CompressedSpanPart::from_span(&subtree.delimiter.open)); |
| self.ensure_can_hold(subtree.delimiter.open.range, open_span_parts_index); |
| let close_span_parts_index = |
| self.span_part_index(CompressedSpanPart::from_span(&subtree.delimiter.close)); |
| self.ensure_can_hold(subtree.delimiter.close.range, close_span_parts_index); |
| fn do_it<S: SpanStorage>( |
| tt: &mut Vec<TokenTree<S>>, |
| open_span_parts_index: usize, |
| close_span_parts_index: usize, |
| subtree: crate::Subtree, |
| ) { |
| let open_span = S::new(subtree.delimiter.open.range, open_span_parts_index); |
| let close_span = S::new(subtree.delimiter.close.range, close_span_parts_index); |
| tt.push(TokenTree::Subtree { |
| len: subtree.len, |
| delim_kind: subtree.delimiter.kind, |
| open_span, |
| close_span, |
| }); |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, open_span_parts_index, close_span_parts_index, subtree) |
| } |
| } |
| |
| pub fn open(&mut self, delimiter_kind: DelimiterKind, open_span: Span) { |
| let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&open_span)); |
| self.ensure_can_hold(open_span.range, span_parts_index); |
| fn do_it<S: SpanStorage>( |
| token_trees: &mut Vec<TokenTree<S>>, |
| delimiter_kind: DelimiterKind, |
| range: TextRange, |
| span_parts_index: usize, |
| ) -> usize { |
| let open_span = S::new(range, span_parts_index); |
| token_trees.push(TokenTree::Subtree { |
| len: 0, |
| delim_kind: delimiter_kind, |
| open_span, |
| close_span: open_span, // Will be overwritten on close. |
| }); |
| token_trees.len() - 1 |
| } |
| let subtree_idx = dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, delimiter_kind, open_span.range, span_parts_index) |
| }; |
| self.unclosed_subtree_indices.push(subtree_idx); |
| } |
| |
| pub fn close(&mut self, close_span: Span) { |
| let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&close_span)); |
| let range = close_span.range; |
| self.ensure_can_hold(range, span_parts_index); |
| |
| let last_unclosed_index = self |
| .unclosed_subtree_indices |
| .pop() |
| .expect("attempt to close a `tt::Subtree` when none is open"); |
| fn do_it<S: SpanStorage>( |
| token_trees: &mut [TokenTree<S>], |
| last_unclosed_index: usize, |
| range: TextRange, |
| span_parts_index: usize, |
| ) { |
| let token_trees_len = token_trees.len(); |
| let TokenTree::Subtree { len, delim_kind: _, open_span: _, close_span } = |
| &mut token_trees[last_unclosed_index] |
| else { |
| unreachable!("unclosed token tree is always a subtree"); |
| }; |
| *len = (token_trees_len - last_unclosed_index - 1) as u32; |
| *close_span = S::new(range, span_parts_index); |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, last_unclosed_index, range, span_parts_index) |
| } |
| self.last_closed_subtree = Some(last_unclosed_index); |
| } |
| |
| /// You cannot call this consecutively, it will only work once after close. |
| pub fn remove_last_subtree_if_invisible(&mut self) { |
| let Some(last_subtree_idx) = self.last_closed_subtree else { return }; |
| fn do_it<S: SpanStorage>(tt: &mut Vec<TokenTree<S>>, last_subtree_idx: usize) { |
| if let TokenTree::Subtree { delim_kind: DelimiterKind::Invisible, .. } = |
| tt[last_subtree_idx] |
| { |
| tt.remove(last_subtree_idx); |
| } |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, last_subtree_idx) |
| } |
| self.last_closed_subtree = None; |
| } |
| |
| fn push_literal(&mut self, leaf: crate::Literal) { |
| let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); |
| let range = leaf.span.range; |
| self.ensure_can_hold(range, span_parts_index); |
| fn do_it<S: SpanStorage>( |
| tt: &mut Vec<TokenTree<S>>, |
| range: TextRange, |
| span_parts_index: usize, |
| leaf: crate::Literal, |
| ) { |
| tt.push(TokenTree::Literal { |
| text_and_suffix: leaf.text_and_suffix, |
| span: S::new(range, span_parts_index), |
| kind: leaf.kind, |
| suffix_len: leaf.suffix_len, |
| }) |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) |
| } |
| } |
| |
| fn push_punct(&mut self, leaf: crate::Punct) { |
| let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); |
| let range = leaf.span.range; |
| self.ensure_can_hold(range, span_parts_index); |
| fn do_it<S: SpanStorage>( |
| tt: &mut Vec<TokenTree<S>>, |
| range: TextRange, |
| span_parts_index: usize, |
| leaf: crate::Punct, |
| ) { |
| tt.push(TokenTree::Punct { |
| char: leaf.char, |
| spacing: leaf.spacing, |
| span: S::new(range, span_parts_index), |
| }) |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) |
| } |
| } |
| |
| fn push_ident(&mut self, leaf: crate::Ident) { |
| let span_parts_index = self.span_part_index(CompressedSpanPart::from_span(&leaf.span)); |
| let range = leaf.span.range; |
| self.ensure_can_hold(range, span_parts_index); |
| fn do_it<S: SpanStorage>( |
| tt: &mut Vec<TokenTree<S>>, |
| range: TextRange, |
| span_parts_index: usize, |
| leaf: crate::Ident, |
| ) { |
| tt.push(TokenTree::Ident { |
| sym: leaf.sym, |
| span: S::new(range, span_parts_index), |
| is_raw: leaf.is_raw, |
| }) |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt, range, span_parts_index, leaf) |
| } |
| } |
| |
| pub fn push(&mut self, leaf: crate::Leaf) { |
| match leaf { |
| crate::Leaf::Literal(leaf) => self.push_literal(leaf), |
| crate::Leaf::Punct(leaf) => self.push_punct(leaf), |
| crate::Leaf::Ident(leaf) => self.push_ident(leaf), |
| } |
| } |
| |
| fn push_token_tree(&mut self, tt: crate::TokenTree) { |
| match tt { |
| crate::TokenTree::Leaf(leaf) => self.push(leaf), |
| crate::TokenTree::Subtree(subtree) => self.push_subtree(subtree), |
| } |
| } |
| |
| pub fn extend(&mut self, leaves: impl IntoIterator<Item = crate::Leaf>) { |
| leaves.into_iter().for_each(|leaf| self.push(leaf)); |
| } |
| |
| pub fn extend_with_tt(&mut self, tt: TokenTreesView<'_>) { |
| fn do_it<S: SpanStorage>( |
| this: &mut TopSubtreeBuilder, |
| tt: &[TokenTree<S>], |
| span_parts: &[CompressedSpanPart], |
| ) { |
| for tt in tt { |
| this.push_token_tree(tt.to_api(span_parts)); |
| } |
| } |
| dispatch_ref! { |
| match tt.repr => tt_repr => do_it(self, tt_repr, tt.span_parts) |
| } |
| } |
| |
| /// Like [`Self::extend_with_tt()`], but makes sure the new tokens will never be |
| /// joint with whatever comes after them. |
| pub fn extend_with_tt_alone(&mut self, tt: TokenTreesView<'_>) { |
| self.extend_with_tt(tt); |
| fn do_it<S: SpanStorage>(tt: &mut [TokenTree<S>]) { |
| if let Some(TokenTree::Punct { spacing, .. }) = tt.last_mut() { |
| *spacing = Spacing::Alone; |
| } |
| } |
| if !tt.is_empty() { |
| dispatch_builder! { |
| match &mut self.token_trees => tt => do_it(tt) |
| } |
| } |
| } |
| |
| pub fn expected_delimiters(&self) -> impl Iterator<Item = DelimiterKind> { |
| self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| { |
| dispatch_builder! { |
| match &self.token_trees => tt => { |
| let TokenTree::Subtree { delim_kind, .. } = tt[subtree_idx] else { |
| unreachable!("unclosed token tree is always a subtree") |
| }; |
| delim_kind |
| } |
| } |
| }) |
| } |
| |
| /// Builds, and remove the top subtree if it has only one subtree child. |
| pub fn build_skip_top_subtree(mut self) -> TopSubtree { |
| fn remove_first_if_needed<S: SpanStorage>( |
| tt: &mut Vec<TokenTree<S>>, |
| top_delim_span: &mut DelimSpan, |
| span_parts: &FxIndexSet<CompressedSpanPart>, |
| ) { |
| let tt_len = tt.len(); |
| let Some(TokenTree::Subtree { len, open_span, close_span, .. }) = tt.get_mut(1) else { |
| return; |
| }; |
| if (*len as usize) != (tt_len - 2) { |
| // Subtree does not cover the whole tree (minus 2; itself, and the top span). |
| return; |
| } |
| |
| // Now we need to adjust the spans, because we assume that the first two spans are always reserved. |
| let top_open_span = span_parts |
| .get_index(open_span.span_parts_index() - RESERVED_SPAN_PARTS_LEN) |
| .unwrap() |
| .recombine(open_span.text_range()); |
| let top_close_span = span_parts |
| .get_index(close_span.span_parts_index() - RESERVED_SPAN_PARTS_LEN) |
| .unwrap() |
| .recombine(close_span.text_range()); |
| *top_delim_span = DelimSpan { open: top_open_span, close: top_close_span }; |
| // Can't remove the top spans from the map, as maybe they're used by other things as well. |
| // Now we need to reencode the spans, because their parts index changed: |
| *open_span = S::new(open_span.text_range(), 0); |
| *close_span = S::new(close_span.text_range(), 1); |
| |
| tt.remove(0); |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => remove_first_if_needed(tt, &mut self.top_subtree_spans, &self.span_parts) |
| } |
| self.build() |
| } |
| |
| pub fn build(mut self) -> TopSubtree { |
| assert!( |
| self.unclosed_subtree_indices.is_empty(), |
| "attempt to build an unbalanced `TopSubtreeBuilder`" |
| ); |
| fn finish_top_len<S: SpanStorage>(tt: &mut [TokenTree<S>]) { |
| let total_len = tt.len() as u32; |
| let TokenTree::Subtree { len, .. } = &mut tt[0] else { |
| unreachable!("first token tree is always a subtree"); |
| }; |
| *len = total_len - 1; |
| } |
| dispatch_builder! { |
| match &mut self.token_trees => tt => finish_top_len(tt) |
| } |
| |
| let span_parts = [ |
| CompressedSpanPart::from_span(&self.top_subtree_spans.open), |
| CompressedSpanPart::from_span(&self.top_subtree_spans.close), |
| ] |
| .into_iter() |
| .chain(self.span_parts.iter().copied()) |
| .collect(); |
| |
| let repr = match self.token_trees { |
| TopSubtreeBuilderRepr::SpanStorage32(tt) => { |
| TopSubtreeRepr::SpanStorage32(tt.into_boxed_slice()) |
| } |
| TopSubtreeBuilderRepr::SpanStorage64(tt) => { |
| TopSubtreeRepr::SpanStorage64(tt.into_boxed_slice()) |
| } |
| TopSubtreeBuilderRepr::SpanStorage96(tt) => { |
| TopSubtreeRepr::SpanStorage96(tt.into_boxed_slice()) |
| } |
| }; |
| |
| TopSubtree { repr, span_parts } |
| } |
| |
| pub fn restore_point(&self) -> SubtreeBuilderRestorePoint { |
| let token_trees_len = dispatch_builder! { |
| match &self.token_trees => tt => tt.len() |
| }; |
| SubtreeBuilderRestorePoint { |
| unclosed_subtree_indices_len: self.unclosed_subtree_indices.len(), |
| token_trees_len, |
| last_closed_subtree: self.last_closed_subtree, |
| } |
| } |
| |
| pub fn restore(&mut self, restore_point: SubtreeBuilderRestorePoint) { |
| self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len); |
| dispatch_builder! { |
| match &mut self.token_trees => tt => tt.truncate(restore_point.token_trees_len) |
| } |
| self.last_closed_subtree = restore_point.last_closed_subtree; |
| } |
| } |
| |
| #[derive(Clone, Copy)] |
| pub struct SubtreeBuilderRestorePoint { |
| unclosed_subtree_indices_len: usize, |
| token_trees_len: usize, |
| last_closed_subtree: Option<usize>, |
| } |