ra_ap_syntax_bridge/
to_parser_input.rs

1//! Convert macro-by-example tokens which are specific to macro expansion into a
2//! format that works for our parser.
3
4use std::fmt;
5use std::hash::Hash;
6
7use rustc_hash::FxHashMap;
8use span::{Edition, SpanData};
9use syntax::{SyntaxKind, SyntaxKind::*, T};
10
11pub fn to_parser_input<Ctx: Copy + fmt::Debug + PartialEq + Eq + Hash>(
12    buffer: tt::TokenTreesView<'_, SpanData<Ctx>>,
13    span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
14) -> parser::Input {
15    let mut res = parser::Input::default();
16
17    let mut current = buffer.cursor();
18    let mut syntax_context_to_edition_cache = FxHashMap::default();
19
20    while !current.eof() {
21        let tt = current.token_tree();
22
23        // Check if it is lifetime
24        if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt {
25            if punct.char == '\'' {
26                current.bump();
27                match current.token_tree() {
28                    Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => {
29                        res.push(LIFETIME_IDENT);
30                        current.bump();
31                        continue;
32                    }
33                    _ => panic!("Next token must be ident"),
34                }
35            }
36        }
37
38        match tt {
39            Some(tt::TokenTree::Leaf(leaf)) => {
40                match leaf {
41                    tt::Leaf::Literal(lit) => {
42                        let kind = match lit.kind {
43                            tt::LitKind::Byte => SyntaxKind::BYTE,
44                            tt::LitKind::Char => SyntaxKind::CHAR,
45                            tt::LitKind::Integer => SyntaxKind::INT_NUMBER,
46                            tt::LitKind::Float => SyntaxKind::FLOAT_NUMBER,
47                            tt::LitKind::Str | tt::LitKind::StrRaw(_) => SyntaxKind::STRING,
48                            tt::LitKind::ByteStr | tt::LitKind::ByteStrRaw(_) => {
49                                SyntaxKind::BYTE_STRING
50                            }
51                            tt::LitKind::CStr | tt::LitKind::CStrRaw(_) => SyntaxKind::C_STRING,
52                            tt::LitKind::Err(_) => SyntaxKind::ERROR,
53                        };
54                        res.push(kind);
55
56                        if kind == FLOAT_NUMBER && !lit.symbol.as_str().ends_with('.') {
57                            // Tag the token as joint if it is float with a fractional part
58                            // we use this jointness to inform the parser about what token split
59                            // event to emit when we encounter a float literal in a field access
60                            res.was_joint();
61                        }
62                    }
63                    tt::Leaf::Ident(ident) => {
64                        let edition = *syntax_context_to_edition_cache
65                            .entry(ident.span.ctx)
66                            .or_insert_with(|| span_to_edition(ident.span.ctx));
67                        match ident.sym.as_str() {
68                            "_" => res.push(T![_]),
69                            i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
70                            _ if ident.is_raw.yes() => res.push(IDENT),
71                            text => match SyntaxKind::from_keyword(text, edition) {
72                                Some(kind) => res.push(kind),
73                                None => {
74                                    let contextual_keyword =
75                                        SyntaxKind::from_contextual_keyword(text, edition)
76                                            .unwrap_or(SyntaxKind::IDENT);
77                                    res.push_ident(contextual_keyword);
78                                }
79                            },
80                        }
81                    }
82                    tt::Leaf::Punct(punct) => {
83                        let kind = SyntaxKind::from_char(punct.char)
84                            .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
85                        res.push(kind);
86                        if punct.spacing == tt::Spacing::Joint {
87                            res.was_joint();
88                        }
89                    }
90                }
91                current.bump();
92            }
93            Some(tt::TokenTree::Subtree(subtree)) => {
94                if let Some(kind) = match subtree.delimiter.kind {
95                    tt::DelimiterKind::Parenthesis => Some(T!['(']),
96                    tt::DelimiterKind::Brace => Some(T!['{']),
97                    tt::DelimiterKind::Bracket => Some(T!['[']),
98                    tt::DelimiterKind::Invisible => None,
99                } {
100                    res.push(kind);
101                }
102                current.bump();
103            }
104            None => {
105                let subtree = current.end();
106                if let Some(kind) = match subtree.delimiter.kind {
107                    tt::DelimiterKind::Parenthesis => Some(T![')']),
108                    tt::DelimiterKind::Brace => Some(T!['}']),
109                    tt::DelimiterKind::Bracket => Some(T![']']),
110                    tt::DelimiterKind::Invisible => None,
111                } {
112                    res.push(kind);
113                }
114            }
115        };
116    }
117
118    res
119}