1use std::{fmt, hash::Hash};
4
5use intern::Symbol;
6use rustc_hash::{FxHashMap, FxHashSet};
7use span::{Edition, SpanAnchor, SpanData, SpanMap};
8use stdx::{format_to, never};
9use syntax::{
10 ast::{self, make::tokens::doc_comment},
11 format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement,
12 SyntaxKind::{self, *},
13 SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T,
14};
15use tt::{buffer::Cursor, token_to_literal};
16
17pub mod prettify_macro_expansion;
18mod to_parser_input;
19pub use to_parser_input::to_parser_input;
20pub use ::parser::TopEntryPoint;
22
23#[cfg(test)]
24mod tests;
25
26pub trait SpanMapper<S> {
27 fn span_for(&self, range: TextRange) -> S;
28}
29
30impl<S> SpanMapper<SpanData<S>> for SpanMap<S>
31where
32 SpanData<S>: Copy,
33{
34 fn span_for(&self, range: TextRange) -> SpanData<S> {
35 self.span_at(range.start())
36 }
37}
38
39impl<S: Copy, SM: SpanMapper<S>> SpanMapper<S> for &SM {
40 fn span_for(&self, range: TextRange) -> S {
41 SM::span_for(self, range)
42 }
43}
44
45pub mod dummy_test_span_utils {
47
48 use span::{Span, SyntaxContextId};
49
50 use super::*;
51
52 pub const DUMMY: Span = Span {
53 range: TextRange::empty(TextSize::new(0)),
54 anchor: span::SpanAnchor {
55 file_id: span::EditionedFileId::new(
56 span::FileId::from_raw(0xe4e4e),
57 span::Edition::CURRENT,
58 ),
59 ast_id: span::ROOT_ERASED_FILE_AST_ID,
60 },
61 ctx: SyntaxContextId::root(Edition::CURRENT),
62 };
63
64 pub struct DummyTestSpanMap;
65
66 impl SpanMapper<Span> for DummyTestSpanMap {
67 fn span_for(&self, range: syntax::TextRange) -> Span {
68 Span {
69 range,
70 anchor: span::SpanAnchor {
71 file_id: span::EditionedFileId::new(
72 span::FileId::from_raw(0xe4e4e),
73 span::Edition::CURRENT,
74 ),
75 ast_id: span::ROOT_ERASED_FILE_AST_ID,
76 },
77 ctx: SyntaxContextId::root(Edition::CURRENT),
78 }
79 }
80 }
81}
82
83#[derive(Copy, Clone, PartialEq, Eq)]
85pub enum DocCommentDesugarMode {
86 Mbe,
88 ProcMacro,
90}
91
92pub fn syntax_node_to_token_tree<Ctx, SpanMap>(
95 node: &SyntaxNode,
96 map: SpanMap,
97 span: SpanData<Ctx>,
98 mode: DocCommentDesugarMode,
99) -> tt::TopSubtree<SpanData<Ctx>>
100where
101 SpanData<Ctx>: Copy + fmt::Debug,
102 SpanMap: SpanMapper<SpanData<Ctx>>,
103{
104 let mut c = Converter::new(node, map, Default::default(), Default::default(), span, mode);
105 convert_tokens(&mut c)
106}
107
108pub fn syntax_node_to_token_tree_modified<Ctx, SpanMap>(
112 node: &SyntaxNode,
113 map: SpanMap,
114 append: FxHashMap<SyntaxElement, Vec<tt::Leaf<SpanData<Ctx>>>>,
115 remove: FxHashSet<SyntaxElement>,
116 call_site: SpanData<Ctx>,
117 mode: DocCommentDesugarMode,
118) -> tt::TopSubtree<SpanData<Ctx>>
119where
120 SpanMap: SpanMapper<SpanData<Ctx>>,
121 SpanData<Ctx>: Copy + fmt::Debug,
122{
123 let mut c = Converter::new(node, map, append, remove, call_site, mode);
124 convert_tokens(&mut c)
125}
126
127pub fn token_tree_to_syntax_node<Ctx>(
142 tt: &tt::TopSubtree<SpanData<Ctx>>,
143 entry_point: parser::TopEntryPoint,
144 span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
145 top_edition: Edition,
146) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
147where
148 Ctx: Copy + fmt::Debug + PartialEq + PartialEq + Eq + Hash,
149{
150 let buffer = tt.view().strip_invisible();
151 let parser_input = to_parser_input(buffer, span_to_edition);
152 let parser_output = entry_point.parse(&parser_input, top_edition);
154 let mut tree_sink = TtTreeSink::new(buffer.cursor());
155 for event in parser_output.iter() {
156 match event {
157 parser::Step::Token { kind, n_input_tokens: n_raw_tokens } => {
158 tree_sink.token(kind, n_raw_tokens)
159 }
160 parser::Step::FloatSplit { ends_in_dot: has_pseudo_dot } => {
161 tree_sink.float_split(has_pseudo_dot)
162 }
163 parser::Step::Enter { kind } => tree_sink.start_node(kind),
164 parser::Step::Exit => tree_sink.finish_node(),
165 parser::Step::Error { msg } => tree_sink.error(msg.to_owned()),
166 }
167 }
168 tree_sink.finish()
169}
170
171pub fn parse_to_token_tree<Ctx>(
174 edition: Edition,
175 anchor: SpanAnchor,
176 ctx: Ctx,
177 text: &str,
178) -> Option<tt::TopSubtree<SpanData<Ctx>>>
179where
180 SpanData<Ctx>: Copy + fmt::Debug,
181 Ctx: Copy,
182{
183 let lexed = parser::LexedStr::new(edition, text);
184 if lexed.errors().next().is_some() {
185 return None;
186 }
187 let mut conv =
188 RawConverter { lexed, anchor, pos: 0, ctx, mode: DocCommentDesugarMode::ProcMacro };
189 Some(convert_tokens(&mut conv))
190}
191
192pub fn parse_to_token_tree_static_span<S>(
194 edition: Edition,
195 span: S,
196 text: &str,
197) -> Option<tt::TopSubtree<S>>
198where
199 S: Copy + fmt::Debug,
200{
201 let lexed = parser::LexedStr::new(edition, text);
202 if lexed.errors().next().is_some() {
203 return None;
204 }
205 let mut conv =
206 StaticRawConverter { lexed, pos: 0, span, mode: DocCommentDesugarMode::ProcMacro };
207 Some(convert_tokens(&mut conv))
208}
209
210fn convert_tokens<S, C>(conv: &mut C) -> tt::TopSubtree<S>
211where
212 C: TokenConverter<S>,
213 S: Copy + fmt::Debug,
214 C::Token: fmt::Debug,
215{
216 let mut builder =
217 tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site()));
218
219 while let Some((token, abs_range)) = conv.bump() {
220 let delimiter = builder.expected_delimiter().map(|it| it.kind);
221 let tt = match token.as_leaf() {
222 Some(leaf) => leaf.clone(),
223 None => match token.kind(conv) {
224 COMMENT => {
226 let span = conv.span_for(abs_range);
227 conv.convert_doc_comment(&token, span, &mut builder);
228 continue;
229 }
230 kind if kind.is_punct() && kind != UNDERSCORE => {
231 let expected = match delimiter {
232 Some(tt::DelimiterKind::Parenthesis) => Some(T![')']),
233 Some(tt::DelimiterKind::Brace) => Some(T!['}']),
234 Some(tt::DelimiterKind::Bracket) => Some(T![']']),
235 Some(tt::DelimiterKind::Invisible) | None => None,
236 };
237
238 if matches!(expected, Some(expected) if expected == kind) {
241 builder.close(conv.span_for(abs_range));
242 continue;
243 }
244
245 let delim = match kind {
246 T!['('] => Some(tt::DelimiterKind::Parenthesis),
247 T!['{'] => Some(tt::DelimiterKind::Brace),
248 T!['['] => Some(tt::DelimiterKind::Bracket),
249 _ => None,
250 };
251
252 if let Some(kind) = delim {
254 builder.open(kind, conv.span_for(abs_range));
255 continue;
256 }
257
258 let spacing = match conv.peek().map(|next| next.kind(conv)) {
259 Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint,
260 _ => tt::Spacing::Alone,
261 };
262 let Some(char) = token.to_char(conv) else {
263 panic!("Token from lexer must be single char: token = {token:#?}")
264 };
265 tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) })
266 }
267 kind => {
268 macro_rules! make_ident {
269 () => {
270 tt::Ident {
271 span: conv.span_for(abs_range),
272 sym: Symbol::intern(&token.to_text(conv)),
273 is_raw: tt::IdentIsRaw::No,
274 }
275 .into()
276 };
277 }
278 let leaf: tt::Leaf<_> = match kind {
279 k if k.is_any_identifier() => {
280 let text = token.to_text(conv);
281 tt::Ident::new(&text, conv.span_for(abs_range)).into()
282 }
283 UNDERSCORE => make_ident!(),
284 k if k.is_literal() => {
285 let text = token.to_text(conv);
286 let span = conv.span_for(abs_range);
287 token_to_literal(&text, span).into()
288 }
289 LIFETIME_IDENT => {
290 let apostrophe = tt::Leaf::from(tt::Punct {
291 char: '\'',
292 spacing: tt::Spacing::Joint,
293 span: conv
294 .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))),
295 });
296 builder.push(apostrophe);
297
298 let ident = tt::Leaf::from(tt::Ident {
299 sym: Symbol::intern(&token.to_text(conv)[1..]),
300 span: conv.span_for(TextRange::new(
301 abs_range.start() + TextSize::of('\''),
302 abs_range.end(),
303 )),
304 is_raw: tt::IdentIsRaw::No,
305 });
306 builder.push(ident);
307 continue;
308 }
309 _ => continue,
310 };
311
312 leaf
313 }
314 },
315 };
316
317 builder.push(tt);
318 }
319
320 builder.flatten_unclosed_subtrees();
324
325 builder.build_skip_top_subtree()
326}
327
328fn is_single_token_op(kind: SyntaxKind) -> bool {
329 matches!(
330 kind,
331 EQ | L_ANGLE
332 | R_ANGLE
333 | BANG
334 | AMP
335 | PIPE
336 | TILDE
337 | AT
338 | DOT
339 | COMMA
340 | SEMICOLON
341 | COLON
342 | POUND
343 | DOLLAR
344 | QUESTION
345 | PLUS
346 | MINUS
347 | STAR
348 | SLASH
349 | PERCENT
350 | CARET
351 | LIFETIME_IDENT
354 )
355}
356
357pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (Symbol, tt::LitKind) {
364 match mode {
365 DocCommentDesugarMode::Mbe => {
366 let mut num_of_hashes = 0;
367 let mut count = 0;
368 for ch in text.chars() {
369 count = match ch {
370 '"' => 1,
371 '#' if count > 0 => count + 1,
372 _ => 0,
373 };
374 num_of_hashes = num_of_hashes.max(count);
375 }
376
377 (Symbol::intern(text), tt::LitKind::StrRaw(num_of_hashes))
379 }
380 DocCommentDesugarMode::ProcMacro => {
382 (Symbol::intern(&format_smolstr!("{}", text.escape_debug())), tt::LitKind::Str)
383 }
384 }
385}
386
387fn convert_doc_comment<S: Copy>(
388 token: &syntax::SyntaxToken,
389 span: S,
390 mode: DocCommentDesugarMode,
391 builder: &mut tt::TopSubtreeBuilder<S>,
392) {
393 let Some(comment) = ast::Comment::cast(token.clone()) else { return };
394 let Some(doc) = comment.kind().doc else { return };
395
396 let mk_ident = |s: &str| {
397 tt::Leaf::from(tt::Ident { sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No })
398 };
399
400 let mk_punct =
401 |c: char| tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, span });
402
403 let mk_doc_literal = |comment: &ast::Comment| {
404 let prefix_len = comment.prefix().len();
405 let mut text = &comment.text()[prefix_len..];
406
407 if comment.kind().shape == ast::CommentShape::Block {
409 text = &text[0..text.len() - 2];
410 }
411 let (text, kind) = desugar_doc_comment_text(text, mode);
412 let lit = tt::Literal { symbol: text, span, kind, suffix: None };
413
414 tt::Leaf::from(lit)
415 };
416
417 let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)];
419
420 builder.push(mk_punct('#'));
422 if let ast::CommentPlacement::Inner = doc {
423 builder.push(mk_punct('!'));
424 }
425 builder.open(tt::DelimiterKind::Bracket, span);
426 builder.extend(meta_tkns);
427 builder.close(span);
428}
429
430struct RawConverter<'a, Ctx> {
432 lexed: parser::LexedStr<'a>,
433 pos: usize,
434 anchor: SpanAnchor,
435 ctx: Ctx,
436 mode: DocCommentDesugarMode,
437}
438struct StaticRawConverter<'a, S> {
440 lexed: parser::LexedStr<'a>,
441 pos: usize,
442 span: S,
443 mode: DocCommentDesugarMode,
444}
445
446trait SrcToken<Ctx, S> {
447 fn kind(&self, ctx: &Ctx) -> SyntaxKind;
448
449 fn to_char(&self, ctx: &Ctx) -> Option<char>;
450
451 fn to_text(&self, ctx: &Ctx) -> SmolStr;
452
453 fn as_leaf(&self) -> Option<&tt::Leaf<S>> {
454 None
455 }
456}
457
458trait TokenConverter<S>: Sized {
459 type Token: SrcToken<Self, S>;
460
461 fn convert_doc_comment(
462 &self,
463 token: &Self::Token,
464 span: S,
465 builder: &mut tt::TopSubtreeBuilder<S>,
466 );
467
468 fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
469
470 fn peek(&self) -> Option<Self::Token>;
471
472 fn span_for(&self, range: TextRange) -> S;
473
474 fn call_site(&self) -> S;
475}
476
477impl<S, Ctx> SrcToken<RawConverter<'_, Ctx>, S> for usize {
478 fn kind(&self, ctx: &RawConverter<'_, Ctx>) -> SyntaxKind {
479 ctx.lexed.kind(*self)
480 }
481
482 fn to_char(&self, ctx: &RawConverter<'_, Ctx>) -> Option<char> {
483 ctx.lexed.text(*self).chars().next()
484 }
485
486 fn to_text(&self, ctx: &RawConverter<'_, Ctx>) -> SmolStr {
487 ctx.lexed.text(*self).into()
488 }
489}
490
491impl<S: Copy> SrcToken<StaticRawConverter<'_, S>, S> for usize {
492 fn kind(&self, ctx: &StaticRawConverter<'_, S>) -> SyntaxKind {
493 ctx.lexed.kind(*self)
494 }
495
496 fn to_char(&self, ctx: &StaticRawConverter<'_, S>) -> Option<char> {
497 ctx.lexed.text(*self).chars().next()
498 }
499
500 fn to_text(&self, ctx: &StaticRawConverter<'_, S>) -> SmolStr {
501 ctx.lexed.text(*self).into()
502 }
503}
504
505impl<Ctx: Copy> TokenConverter<SpanData<Ctx>> for RawConverter<'_, Ctx>
506where
507 SpanData<Ctx>: Copy,
508{
509 type Token = usize;
510
511 fn convert_doc_comment(
512 &self,
513 &token: &usize,
514 span: SpanData<Ctx>,
515 builder: &mut tt::TopSubtreeBuilder<SpanData<Ctx>>,
516 ) {
517 let text = self.lexed.text(token);
518 convert_doc_comment(&doc_comment(text), span, self.mode, builder);
519 }
520
521 fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
522 if self.pos == self.lexed.len() {
523 return None;
524 }
525 let token = self.pos;
526 self.pos += 1;
527 let range = self.lexed.text_range(token);
528 let range = TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?);
529
530 Some((token, range))
531 }
532
533 fn peek(&self) -> Option<Self::Token> {
534 if self.pos == self.lexed.len() {
535 return None;
536 }
537 Some(self.pos)
538 }
539
540 fn span_for(&self, range: TextRange) -> SpanData<Ctx> {
541 SpanData { range, anchor: self.anchor, ctx: self.ctx }
542 }
543
544 fn call_site(&self) -> SpanData<Ctx> {
545 SpanData { range: TextRange::empty(0.into()), anchor: self.anchor, ctx: self.ctx }
546 }
547}
548
549impl<S> TokenConverter<S> for StaticRawConverter<'_, S>
550where
551 S: Copy,
552{
553 type Token = usize;
554
555 fn convert_doc_comment(&self, &token: &usize, span: S, builder: &mut tt::TopSubtreeBuilder<S>) {
556 let text = self.lexed.text(token);
557 convert_doc_comment(&doc_comment(text), span, self.mode, builder);
558 }
559
560 fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
561 if self.pos == self.lexed.len() {
562 return None;
563 }
564 let token = self.pos;
565 self.pos += 1;
566 let range = self.lexed.text_range(token);
567 let range = TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?);
568
569 Some((token, range))
570 }
571
572 fn peek(&self) -> Option<Self::Token> {
573 if self.pos == self.lexed.len() {
574 return None;
575 }
576 Some(self.pos)
577 }
578
579 fn span_for(&self, _: TextRange) -> S {
580 self.span
581 }
582
583 fn call_site(&self) -> S {
584 self.span
585 }
586}
587
588struct Converter<SpanMap, S> {
589 current: Option<SyntaxToken>,
590 current_leaves: Vec<tt::Leaf<S>>,
591 preorder: PreorderWithTokens,
592 range: TextRange,
593 punct_offset: Option<(SyntaxToken, TextSize)>,
594 map: SpanMap,
596 append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>,
597 remove: FxHashSet<SyntaxElement>,
598 call_site: S,
599 mode: DocCommentDesugarMode,
600}
601
602impl<SpanMap, S> Converter<SpanMap, S> {
603 fn new(
604 node: &SyntaxNode,
605 map: SpanMap,
606 append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>,
607 remove: FxHashSet<SyntaxElement>,
608 call_site: S,
609 mode: DocCommentDesugarMode,
610 ) -> Self {
611 let mut this = Converter {
612 current: None,
613 preorder: node.preorder_with_tokens(),
614 range: node.text_range(),
615 punct_offset: None,
616 map,
617 append,
618 remove,
619 call_site,
620 current_leaves: vec![],
621 mode,
622 };
623 let first = this.next_token();
624 this.current = first;
625 this
626 }
627
628 fn next_token(&mut self) -> Option<SyntaxToken> {
629 while let Some(ev) = self.preorder.next() {
630 match ev {
631 WalkEvent::Enter(token) => {
632 if self.remove.contains(&token) {
633 match token {
634 syntax::NodeOrToken::Token(_) => {
635 continue;
636 }
637 node => {
638 self.preorder.skip_subtree();
639 if let Some(mut v) = self.append.remove(&node) {
640 v.reverse();
641 self.current_leaves.extend(v);
642 return None;
643 }
644 }
645 }
646 } else if let syntax::NodeOrToken::Token(token) = token {
647 return Some(token);
648 }
649 }
650 WalkEvent::Leave(ele) => {
651 if let Some(mut v) = self.append.remove(&ele) {
652 v.reverse();
653 self.current_leaves.extend(v);
654 return None;
655 }
656 }
657 }
658 }
659 None
660 }
661}
662
663#[derive(Debug)]
664enum SynToken<S> {
665 Ordinary(SyntaxToken),
666 Punct { token: SyntaxToken, offset: usize },
667 Leaf(tt::Leaf<S>),
668}
669
670impl<S> SynToken<S> {
671 fn token(&self) -> &SyntaxToken {
672 match self {
673 SynToken::Ordinary(it) | SynToken::Punct { token: it, offset: _ } => it,
674 SynToken::Leaf(_) => unreachable!(),
675 }
676 }
677}
678
679impl<SpanMap, S> SrcToken<Converter<SpanMap, S>, S> for SynToken<S> {
680 fn kind(&self, _ctx: &Converter<SpanMap, S>) -> SyntaxKind {
681 match self {
682 SynToken::Ordinary(token) => token.kind(),
683 SynToken::Punct { token, offset: i } => {
684 SyntaxKind::from_char(token.text().chars().nth(*i).unwrap()).unwrap()
685 }
686 SynToken::Leaf(_) => {
687 never!();
688 SyntaxKind::ERROR
689 }
690 }
691 }
692 fn to_char(&self, _ctx: &Converter<SpanMap, S>) -> Option<char> {
693 match self {
694 SynToken::Ordinary(_) => None,
695 SynToken::Punct { token: it, offset: i } => it.text().chars().nth(*i),
696 SynToken::Leaf(_) => None,
697 }
698 }
699 fn to_text(&self, _ctx: &Converter<SpanMap, S>) -> SmolStr {
700 match self {
701 SynToken::Ordinary(token) | SynToken::Punct { token, offset: _ } => token.text().into(),
702 SynToken::Leaf(_) => {
703 never!();
704 "".into()
705 }
706 }
707 }
708 fn as_leaf(&self) -> Option<&tt::Leaf<S>> {
709 match self {
710 SynToken::Ordinary(_) | SynToken::Punct { .. } => None,
711 SynToken::Leaf(it) => Some(it),
712 }
713 }
714}
715
716impl<S, SpanMap> TokenConverter<S> for Converter<SpanMap, S>
717where
718 S: Copy,
719 SpanMap: SpanMapper<S>,
720{
721 type Token = SynToken<S>;
722 fn convert_doc_comment(
723 &self,
724 token: &Self::Token,
725 span: S,
726 builder: &mut tt::TopSubtreeBuilder<S>,
727 ) {
728 convert_doc_comment(token.token(), span, self.mode, builder);
729 }
730
731 fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
732 if let Some((punct, offset)) = self.punct_offset.clone() {
733 if usize::from(offset) + 1 < punct.text().len() {
734 let offset = offset + TextSize::of('.');
735 let range = punct.text_range();
736 self.punct_offset = Some((punct.clone(), offset));
737 let range = TextRange::at(range.start() + offset, TextSize::of('.'));
738 return Some((
739 SynToken::Punct { token: punct, offset: u32::from(offset) as usize },
740 range,
741 ));
742 }
743 }
744
745 if let Some(leaf) = self.current_leaves.pop() {
746 if self.current_leaves.is_empty() {
747 self.current = self.next_token();
748 }
749 return Some((SynToken::Leaf(leaf), TextRange::empty(TextSize::new(0))));
750 }
751
752 let curr = self.current.clone()?;
753 if !self.range.contains_range(curr.text_range()) {
754 return None;
755 }
756
757 self.current = self.next_token();
758 let token = if curr.kind().is_punct() {
759 self.punct_offset = Some((curr.clone(), 0.into()));
760 let range = curr.text_range();
761 let range = TextRange::at(range.start(), TextSize::of('.'));
762 (SynToken::Punct { token: curr, offset: 0_usize }, range)
763 } else {
764 self.punct_offset = None;
765 let range = curr.text_range();
766 (SynToken::Ordinary(curr), range)
767 };
768
769 Some(token)
770 }
771
772 fn peek(&self) -> Option<Self::Token> {
773 if let Some((punct, mut offset)) = self.punct_offset.clone() {
774 offset += TextSize::of('.');
775 if usize::from(offset) < punct.text().len() {
776 return Some(SynToken::Punct { token: punct, offset: usize::from(offset) });
777 }
778 }
779
780 let curr = self.current.clone()?;
781 if !self.range.contains_range(curr.text_range()) {
782 return None;
783 }
784
785 let token = if curr.kind().is_punct() {
786 SynToken::Punct { token: curr, offset: 0_usize }
787 } else {
788 SynToken::Ordinary(curr)
789 };
790 Some(token)
791 }
792
793 fn span_for(&self, range: TextRange) -> S {
794 self.map.span_for(range)
795 }
796 fn call_site(&self) -> S {
797 self.call_site
798 }
799}
800
801struct TtTreeSink<'a, Ctx>
802where
803 SpanData<Ctx>: Copy,
804{
805 buf: String,
806 cursor: Cursor<'a, SpanData<Ctx>>,
807 text_pos: TextSize,
808 inner: SyntaxTreeBuilder,
809 token_map: SpanMap<Ctx>,
810}
811
812impl<'a, Ctx> TtTreeSink<'a, Ctx>
813where
814 SpanData<Ctx>: Copy,
815{
816 fn new(cursor: Cursor<'a, SpanData<Ctx>>) -> Self {
817 TtTreeSink {
818 buf: String::new(),
819 cursor,
820 text_pos: 0.into(),
821 inner: SyntaxTreeBuilder::default(),
822 token_map: SpanMap::empty(),
823 }
824 }
825
826 fn finish(mut self) -> (Parse<SyntaxNode>, SpanMap<Ctx>) {
827 self.token_map.finish();
828 (self.inner.finish(), self.token_map)
829 }
830}
831
832fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> {
833 let texts = match d {
834 tt::DelimiterKind::Parenthesis => "()",
835 tt::DelimiterKind::Brace => "{}",
836 tt::DelimiterKind::Bracket => "[]",
837 tt::DelimiterKind::Invisible => return None,
838 };
839
840 let idx = closing as usize;
841 Some(&texts[idx..texts.len() - (1 - idx)])
842}
843
844impl<Ctx> TtTreeSink<'_, Ctx>
845where
846 SpanData<Ctx>: Copy + fmt::Debug,
847 Ctx: PartialEq,
848{
849 fn float_split(&mut self, has_pseudo_dot: bool) {
852 let (text, span) = match self.cursor.token_tree() {
853 Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
854 symbol: text,
855 span,
856 kind: tt::LitKind::Float,
857 suffix: _,
858 }))) => (text.as_str(), *span),
859 tt => unreachable!("{tt:?}"),
860 };
861 match text.split_once('.') {
863 Some((left, right)) => {
864 assert!(!left.is_empty());
865
866 self.inner.start_node(SyntaxKind::NAME_REF);
867 self.inner.token(SyntaxKind::INT_NUMBER, left);
868 self.inner.finish_node();
869 self.token_map.push(self.text_pos + TextSize::of(left), span);
870
871 self.inner.finish_node();
873
874 self.inner.token(SyntaxKind::DOT, ".");
875 self.token_map.push(self.text_pos + TextSize::of(left) + TextSize::of("."), span);
876
877 if has_pseudo_dot {
878 assert!(right.is_empty(), "{left}.{right}");
879 } else {
880 assert!(!right.is_empty(), "{left}.{right}");
881 self.inner.start_node(SyntaxKind::NAME_REF);
882 self.inner.token(SyntaxKind::INT_NUMBER, right);
883 self.token_map.push(self.text_pos + TextSize::of(text), span);
884 self.inner.finish_node();
885
886 self.inner.finish_node();
888 }
889 self.text_pos += TextSize::of(text);
890 }
891 None => unreachable!(),
892 }
893 self.cursor.bump();
894 }
895
896 fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
897 if kind == LIFETIME_IDENT {
898 n_tokens = 2;
899 }
900
901 let mut last_two = self.cursor.peek_two_leaves();
902 let mut combined_span = None;
903 'tokens: for _ in 0..n_tokens {
904 let tmp: u8;
905 if self.cursor.eof() {
906 break;
907 }
908 last_two = self.cursor.peek_two_leaves();
909 let (text, span) = loop {
910 break match self.cursor.token_tree() {
911 Some(tt::TokenTree::Leaf(leaf)) => match leaf {
912 tt::Leaf::Ident(ident) => {
913 if ident.is_raw.yes() {
914 self.buf.push_str("r#");
915 self.text_pos += TextSize::of("r#");
916 }
917 let r = (ident.sym.as_str(), ident.span);
918 self.cursor.bump();
919 r
920 }
921 tt::Leaf::Punct(punct) => {
922 assert!(punct.char.is_ascii());
923 tmp = punct.char as u8;
924 let r = (
925 std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(),
926 punct.span,
927 );
928 self.cursor.bump();
929 r
930 }
931 tt::Leaf::Literal(lit) => {
932 let buf_l = self.buf.len();
933 format_to!(self.buf, "{lit}");
934 debug_assert_ne!(self.buf.len() - buf_l, 0);
935 self.text_pos += TextSize::new((self.buf.len() - buf_l) as u32);
936 combined_span = match combined_span {
937 None => Some(lit.span),
938 Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)),
939 };
940 self.cursor.bump();
941 continue 'tokens;
942 }
943 },
944 Some(tt::TokenTree::Subtree(subtree)) => {
945 self.cursor.bump();
946 match delim_to_str(subtree.delimiter.kind, false) {
947 Some(it) => (it, subtree.delimiter.open),
948 None => continue,
949 }
950 }
951 None => {
952 let parent = self.cursor.end();
953 match delim_to_str(parent.delimiter.kind, true) {
954 Some(it) => (it, parent.delimiter.close),
955 None => continue,
956 }
957 }
958 };
959 };
960 self.buf += text;
961 self.text_pos += TextSize::of(text);
962 combined_span = match combined_span {
963 None => Some(span),
964 Some(prev_span) => Some(Self::merge_spans(prev_span, span)),
965 }
966 }
967
968 self.token_map.push(self.text_pos, combined_span.expect("expected at least one token"));
969 self.inner.token(kind, self.buf.as_str());
970 self.buf.clear();
971 if let Some([tt::Leaf::Punct(curr), tt::Leaf::Punct(next)]) = last_two {
974 if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' {
980 self.inner.token(WHITESPACE, " ");
981 self.text_pos += TextSize::of(' ');
982 self.token_map.push(self.text_pos, curr.span);
983 }
984 }
985 }
986
987 fn start_node(&mut self, kind: SyntaxKind) {
988 self.inner.start_node(kind);
989 }
990
991 fn finish_node(&mut self) {
992 self.inner.finish_node();
993 }
994
995 fn error(&mut self, error: String) {
996 self.inner.error(error, self.text_pos)
997 }
998
999 fn merge_spans(a: SpanData<Ctx>, b: SpanData<Ctx>) -> SpanData<Ctx> {
1000 SpanData {
1003 range: if a.ctx == b.ctx && a.anchor == b.anchor {
1004 TextRange::new(
1005 std::cmp::min(a.range.start(), b.range.start()),
1006 std::cmp::max(a.range.end(), b.range.end()),
1007 )
1008 } else {
1009 a.range
1011 },
1012 anchor: a.anchor,
1013 ctx: a.ctx,
1014 }
1015 }
1016}