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