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