diff options
Diffstat (limited to 'crates/ra_mbe/src/syntax_bridge.rs')
-rw-r--r-- | crates/ra_mbe/src/syntax_bridge.rs | 242 |
1 files changed, 171 insertions, 71 deletions
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 1de399fee..ea2cac069 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -2,25 +2,45 @@ | |||
2 | 2 | ||
3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; | 3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast, AstNode, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, | 5 | ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, |
6 | SyntaxTreeBuilder, TextRange, TextUnit, T, | 6 | SyntaxTreeBuilder, TextRange, TextUnit, T, |
7 | }; | 7 | }; |
8 | use rustc_hash::FxHashMap; | ||
8 | use std::iter::successors; | 9 | use std::iter::successors; |
9 | use tt::buffer::{Cursor, TokenBuffer}; | 10 | use tt::buffer::{Cursor, TokenBuffer}; |
10 | 11 | ||
11 | use crate::subtree_source::SubtreeTokenSource; | 12 | use crate::subtree_source::SubtreeTokenSource; |
12 | use crate::ExpandError; | 13 | use crate::ExpandError; |
13 | 14 | ||
15 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
16 | pub enum TokenTextRange { | ||
17 | Token(TextRange), | ||
18 | Delimiter(TextRange, TextRange), | ||
19 | } | ||
20 | |||
21 | impl TokenTextRange { | ||
22 | pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> { | ||
23 | match self { | ||
24 | TokenTextRange::Token(it) => Some(it), | ||
25 | TokenTextRange::Delimiter(open, close) => match kind { | ||
26 | T!['{'] | T!['('] | T!['['] => Some(open), | ||
27 | T!['}'] | T![')'] | T![']'] => Some(close), | ||
28 | _ => None, | ||
29 | }, | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
14 | /// Maps `tt::TokenId` to the relative range of the original token. | 34 | /// Maps `tt::TokenId` to the relative range of the original token. |
15 | #[derive(Debug, PartialEq, Eq, Default)] | 35 | #[derive(Debug, PartialEq, Eq, Default)] |
16 | pub struct TokenMap { | 36 | pub struct TokenMap { |
17 | /// Maps `tt::TokenId` to the *relative* source range. | 37 | /// Maps `tt::TokenId` to the *relative* source range. |
18 | entries: Vec<(tt::TokenId, TextRange)>, | 38 | entries: Vec<(tt::TokenId, TokenTextRange)>, |
19 | } | 39 | } |
20 | 40 | ||
21 | /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro | 41 | /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro |
22 | /// will consume). | 42 | /// will consume). |
23 | pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { | 43 | pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenMap)> { |
24 | syntax_node_to_token_tree(ast.syntax()) | 44 | syntax_node_to_token_tree(ast.syntax()) |
25 | } | 45 | } |
26 | 46 | ||
@@ -51,7 +71,7 @@ pub fn token_tree_to_syntax_node( | |||
51 | ) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { | 71 | ) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { |
52 | let tmp; | 72 | let tmp; |
53 | let tokens = match tt { | 73 | let tokens = match tt { |
54 | tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(), | 74 | tt::Subtree { delimiter: None, token_trees } => token_trees.as_slice(), |
55 | _ => { | 75 | _ => { |
56 | tmp = [tt.clone().into()]; | 76 | tmp = [tt.clone().into()]; |
57 | &tmp[..] | 77 | &tmp[..] |
@@ -71,17 +91,32 @@ pub fn token_tree_to_syntax_node( | |||
71 | 91 | ||
72 | impl TokenMap { | 92 | impl TokenMap { |
73 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { | 93 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { |
74 | let &(token_id, _) = self.entries.iter().find(|(_, range)| *range == relative_range)?; | 94 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { |
95 | TokenTextRange::Token(it) => *it == relative_range, | ||
96 | TokenTextRange::Delimiter(open, close) => { | ||
97 | *open == relative_range || *close == relative_range | ||
98 | } | ||
99 | })?; | ||
75 | Some(token_id) | 100 | Some(token_id) |
76 | } | 101 | } |
77 | 102 | ||
78 | pub fn range_by_token(&self, token_id: tt::TokenId) -> Option<TextRange> { | 103 | pub fn range_by_token(&self, token_id: tt::TokenId) -> Option<TokenTextRange> { |
79 | let &(_, range) = self.entries.iter().find(|(tid, _)| *tid == token_id)?; | 104 | let &(_, range) = self.entries.iter().find(|(tid, _)| *tid == token_id)?; |
80 | Some(range) | 105 | Some(range) |
81 | } | 106 | } |
82 | 107 | ||
83 | fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) { | 108 | fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) { |
84 | self.entries.push((token_id, relative_range)); | 109 | self.entries.push((token_id, TokenTextRange::Token(relative_range))); |
110 | } | ||
111 | |||
112 | fn insert_delim( | ||
113 | &mut self, | ||
114 | token_id: tt::TokenId, | ||
115 | open_relative_range: TextRange, | ||
116 | close_relative_range: TextRange, | ||
117 | ) { | ||
118 | self.entries | ||
119 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); | ||
85 | } | 120 | } |
86 | } | 121 | } |
87 | 122 | ||
@@ -121,7 +156,10 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
121 | token_trees.push(mk_punct('!')); | 156 | token_trees.push(mk_punct('!')); |
122 | } | 157 | } |
123 | token_trees.push(tt::TokenTree::from(tt::Subtree { | 158 | token_trees.push(tt::TokenTree::from(tt::Subtree { |
124 | delimiter: tt::Delimiter::Bracket, | 159 | delimiter: Some(tt::Delimiter { |
160 | kind: tt::DelimiterKind::Bracket, | ||
161 | id: tt::TokenId::unspecified(), | ||
162 | }), | ||
125 | token_trees: meta_tkns, | 163 | token_trees: meta_tkns, |
126 | })); | 164 | })); |
127 | 165 | ||
@@ -136,11 +174,15 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
136 | } | 174 | } |
137 | 175 | ||
138 | fn mk_punct(c: char) -> tt::TokenTree { | 176 | fn mk_punct(c: char) -> tt::TokenTree { |
139 | tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone })) | 177 | tt::TokenTree::from(tt::Leaf::from(tt::Punct { |
178 | char: c, | ||
179 | spacing: tt::Spacing::Alone, | ||
180 | id: tt::TokenId::unspecified(), | ||
181 | })) | ||
140 | } | 182 | } |
141 | 183 | ||
142 | fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { | 184 | fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { |
143 | let lit = tt::Literal { text: doc_comment_text(comment) }; | 185 | let lit = tt::Literal { text: doc_comment_text(comment), id: tt::TokenId::unspecified() }; |
144 | 186 | ||
145 | tt::TokenTree::from(tt::Leaf::from(lit)) | 187 | tt::TokenTree::from(tt::Leaf::from(lit)) |
146 | } | 188 | } |
@@ -156,7 +198,7 @@ impl Convertor { | |||
156 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { | 198 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { |
157 | // This tree is empty | 199 | // This tree is empty |
158 | if tt.first_child_or_token().is_none() { | 200 | if tt.first_child_or_token().is_none() { |
159 | return Some(tt::Subtree { token_trees: vec![], delimiter: tt::Delimiter::None }); | 201 | return Some(tt::Subtree { token_trees: vec![], delimiter: None }); |
160 | } | 202 | } |
161 | 203 | ||
162 | let first_child = tt.first_child_or_token()?; | 204 | let first_child = tt.first_child_or_token()?; |
@@ -173,7 +215,7 @@ impl Convertor { | |||
173 | .last() | 215 | .last() |
174 | .unwrap(); | 216 | .unwrap(); |
175 | if first_child.kind().is_trivia() { | 217 | if first_child.kind().is_trivia() { |
176 | return Some(tt::Subtree { token_trees: vec![], delimiter: tt::Delimiter::None }); | 218 | return Some(tt::Subtree { token_trees: vec![], delimiter: None }); |
177 | } | 219 | } |
178 | 220 | ||
179 | let last_child = successors(Some(last_child), |it| { | 221 | let last_child = successors(Some(last_child), |it| { |
@@ -186,12 +228,16 @@ impl Convertor { | |||
186 | .last() | 228 | .last() |
187 | .unwrap(); | 229 | .unwrap(); |
188 | 230 | ||
189 | let (delimiter, skip_first) = match (first_child.kind(), last_child.kind()) { | 231 | let (delimiter_kind, skip_first) = match (first_child.kind(), last_child.kind()) { |
190 | (T!['('], T![')']) => (tt::Delimiter::Parenthesis, true), | 232 | (T!['('], T![')']) => (Some(tt::DelimiterKind::Parenthesis), true), |
191 | (T!['{'], T!['}']) => (tt::Delimiter::Brace, true), | 233 | (T!['{'], T!['}']) => (Some(tt::DelimiterKind::Brace), true), |
192 | (T!['['], T![']']) => (tt::Delimiter::Bracket, true), | 234 | (T!['['], T![']']) => (Some(tt::DelimiterKind::Bracket), true), |
193 | _ => (tt::Delimiter::None, false), | 235 | _ => (None, false), |
194 | }; | 236 | }; |
237 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { | ||
238 | kind, | ||
239 | id: self.alloc_delim(first_child.text_range(), last_child.text_range()), | ||
240 | }); | ||
195 | 241 | ||
196 | let mut token_trees = Vec::new(); | 242 | let mut token_trees = Vec::new(); |
197 | let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); | 243 | let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); |
@@ -208,13 +254,8 @@ impl Convertor { | |||
208 | } else if token.kind().is_trivia() { | 254 | } else if token.kind().is_trivia() { |
209 | continue; | 255 | continue; |
210 | } else if token.kind().is_punct() { | 256 | } else if token.kind().is_punct() { |
211 | assert!( | 257 | // we need to pull apart joined punctuation tokens |
212 | token.text().len() == 1, | 258 | let last_spacing = match child_iter.peek() { |
213 | "Input ast::token punct must be single char." | ||
214 | ); | ||
215 | let char = token.text().chars().next().unwrap(); | ||
216 | |||
217 | let spacing = match child_iter.peek() { | ||
218 | Some(NodeOrToken::Token(token)) => { | 259 | Some(NodeOrToken::Token(token)) => { |
219 | if token.kind().is_punct() { | 260 | if token.kind().is_punct() { |
220 | tt::Spacing::Joint | 261 | tt::Spacing::Joint |
@@ -224,30 +265,47 @@ impl Convertor { | |||
224 | } | 265 | } |
225 | _ => tt::Spacing::Alone, | 266 | _ => tt::Spacing::Alone, |
226 | }; | 267 | }; |
227 | 268 | let spacing_iter = std::iter::repeat(tt::Spacing::Joint) | |
228 | token_trees.push(tt::Leaf::from(tt::Punct { char, spacing }).into()); | 269 | .take(token.text().len() - 1) |
270 | .chain(std::iter::once(last_spacing)); | ||
271 | for (char, spacing) in token.text().chars().zip(spacing_iter) { | ||
272 | token_trees.push( | ||
273 | tt::Leaf::from(tt::Punct { | ||
274 | char, | ||
275 | spacing, | ||
276 | id: self.alloc(token.text_range()), | ||
277 | }) | ||
278 | .into(), | ||
279 | ); | ||
280 | } | ||
229 | } else { | 281 | } else { |
230 | let child: tt::TokenTree = | 282 | macro_rules! make_leaf { |
231 | if token.kind() == T![true] || token.kind() == T![false] { | 283 | ($i:ident) => { |
232 | tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() | 284 | tt::$i { |
233 | } else if token.kind().is_keyword() | 285 | id: self.alloc(token.text_range()), |
234 | || token.kind() == IDENT | 286 | text: token.text().clone(), |
235 | || token.kind() == LIFETIME | 287 | } |
236 | { | 288 | .into() |
237 | let id = self.alloc(token.text_range()); | ||
238 | let text = token.text().clone(); | ||
239 | tt::Leaf::from(tt::Ident { text, id }).into() | ||
240 | } else if token.kind().is_literal() { | ||
241 | tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() | ||
242 | } else { | ||
243 | return None; | ||
244 | }; | 289 | }; |
245 | token_trees.push(child); | 290 | } |
291 | |||
292 | let child: tt::Leaf = match token.kind() { | ||
293 | T![true] | T![false] => make_leaf!(Literal), | ||
294 | IDENT | LIFETIME => make_leaf!(Ident), | ||
295 | k if k.is_keyword() => make_leaf!(Ident), | ||
296 | k if k.is_literal() => make_leaf!(Literal), | ||
297 | _ => return None, | ||
298 | }; | ||
299 | token_trees.push(child.into()); | ||
246 | } | 300 | } |
247 | } | 301 | } |
248 | NodeOrToken::Node(node) => { | 302 | NodeOrToken::Node(node) => { |
249 | let child = self.go(&node)?.into(); | 303 | let child_subtree = self.go(&node)?; |
250 | token_trees.push(child); | 304 | if child_subtree.delimiter.is_none() && node.kind() != SyntaxKind::TOKEN_TREE { |
305 | token_trees.extend(child_subtree.token_trees); | ||
306 | } else { | ||
307 | token_trees.push(child_subtree.into()); | ||
308 | } | ||
251 | } | 309 | } |
252 | }; | 310 | }; |
253 | } | 311 | } |
@@ -263,11 +321,26 @@ impl Convertor { | |||
263 | self.map.insert(token_id, relative_range); | 321 | self.map.insert(token_id, relative_range); |
264 | token_id | 322 | token_id |
265 | } | 323 | } |
324 | |||
325 | fn alloc_delim( | ||
326 | &mut self, | ||
327 | open_abs_range: TextRange, | ||
328 | close_abs_range: TextRange, | ||
329 | ) -> tt::TokenId { | ||
330 | let open_relative_range = open_abs_range - self.global_offset; | ||
331 | let close_relative_range = close_abs_range - self.global_offset; | ||
332 | let token_id = tt::TokenId(self.next_id); | ||
333 | self.next_id += 1; | ||
334 | |||
335 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
336 | token_id | ||
337 | } | ||
266 | } | 338 | } |
267 | 339 | ||
268 | struct TtTreeSink<'a> { | 340 | struct TtTreeSink<'a> { |
269 | buf: String, | 341 | buf: String, |
270 | cursor: Cursor<'a>, | 342 | cursor: Cursor<'a>, |
343 | open_delims: FxHashMap<tt::TokenId, TextUnit>, | ||
271 | text_pos: TextUnit, | 344 | text_pos: TextUnit, |
272 | inner: SyntaxTreeBuilder, | 345 | inner: SyntaxTreeBuilder, |
273 | token_map: TokenMap, | 346 | token_map: TokenMap, |
@@ -282,6 +355,7 @@ impl<'a> TtTreeSink<'a> { | |||
282 | TtTreeSink { | 355 | TtTreeSink { |
283 | buf: String::new(), | 356 | buf: String::new(), |
284 | cursor, | 357 | cursor, |
358 | open_delims: FxHashMap::default(), | ||
285 | text_pos: 0.into(), | 359 | text_pos: 0.into(), |
286 | inner: SyntaxTreeBuilder::default(), | 360 | inner: SyntaxTreeBuilder::default(), |
287 | roots: smallvec::SmallVec::new(), | 361 | roots: smallvec::SmallVec::new(), |
@@ -294,16 +368,16 @@ impl<'a> TtTreeSink<'a> { | |||
294 | } | 368 | } |
295 | } | 369 | } |
296 | 370 | ||
297 | fn delim_to_str(d: tt::Delimiter, closing: bool) -> SmolStr { | 371 | fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> SmolStr { |
298 | let texts = match d { | 372 | let texts = match d { |
299 | tt::Delimiter::Parenthesis => "()", | 373 | Some(tt::DelimiterKind::Parenthesis) => "()", |
300 | tt::Delimiter::Brace => "{}", | 374 | Some(tt::DelimiterKind::Brace) => "{}", |
301 | tt::Delimiter::Bracket => "[]", | 375 | Some(tt::DelimiterKind::Bracket) => "[]", |
302 | tt::Delimiter::None => "", | 376 | None => return "".into(), |
303 | }; | 377 | }; |
304 | 378 | ||
305 | let idx = closing as usize; | 379 | let idx = closing as usize; |
306 | let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; | 380 | let text = &texts[idx..texts.len() - (1 - idx)]; |
307 | text.into() | 381 | text.into() |
308 | } | 382 | } |
309 | 383 | ||
@@ -319,34 +393,49 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
319 | break; | 393 | break; |
320 | } | 394 | } |
321 | 395 | ||
322 | match self.cursor.token_tree() { | 396 | let text: SmolStr = match self.cursor.token_tree() { |
323 | Some(tt::TokenTree::Leaf(leaf)) => { | 397 | Some(tt::TokenTree::Leaf(leaf)) => { |
324 | // Mark the range if needed | 398 | // Mark the range if needed |
325 | if let tt::Leaf::Ident(ident) = leaf { | 399 | let id = match leaf { |
326 | if kind == IDENT { | 400 | tt::Leaf::Ident(ident) => ident.id, |
327 | let range = | 401 | tt::Leaf::Punct(punct) => punct.id, |
328 | TextRange::offset_len(self.text_pos, TextUnit::of_str(&ident.text)); | 402 | tt::Leaf::Literal(lit) => lit.id, |
329 | self.token_map.insert(ident.id, range); | 403 | }; |
330 | } | 404 | let text = SmolStr::new(format!("{}", leaf)); |
331 | } | 405 | let range = TextRange::offset_len(self.text_pos, TextUnit::of_str(&text)); |
332 | 406 | self.token_map.insert(id, range); | |
333 | self.cursor = self.cursor.bump(); | 407 | self.cursor = self.cursor.bump(); |
334 | self.buf += &format!("{}", leaf); | 408 | text |
335 | } | 409 | } |
336 | Some(tt::TokenTree::Subtree(subtree)) => { | 410 | Some(tt::TokenTree::Subtree(subtree)) => { |
337 | self.cursor = self.cursor.subtree().unwrap(); | 411 | self.cursor = self.cursor.subtree().unwrap(); |
338 | self.buf += &delim_to_str(subtree.delimiter, false); | 412 | if let Some(id) = subtree.delimiter.map(|it| it.id) { |
413 | self.open_delims.insert(id, self.text_pos); | ||
414 | } | ||
415 | delim_to_str(subtree.delimiter_kind(), false) | ||
339 | } | 416 | } |
340 | None => { | 417 | None => { |
341 | if let Some(parent) = self.cursor.end() { | 418 | if let Some(parent) = self.cursor.end() { |
342 | self.cursor = self.cursor.bump(); | 419 | self.cursor = self.cursor.bump(); |
343 | self.buf += &delim_to_str(parent.delimiter, true); | 420 | if let Some(id) = parent.delimiter.map(|it| it.id) { |
421 | if let Some(open_delim) = self.open_delims.get(&id) { | ||
422 | let open_range = | ||
423 | TextRange::offset_len(*open_delim, TextUnit::from_usize(1)); | ||
424 | let close_range = | ||
425 | TextRange::offset_len(self.text_pos, TextUnit::from_usize(1)); | ||
426 | self.token_map.insert_delim(id, open_range, close_range); | ||
427 | } | ||
428 | } | ||
429 | delim_to_str(parent.delimiter_kind(), true) | ||
430 | } else { | ||
431 | continue; | ||
344 | } | 432 | } |
345 | } | 433 | } |
346 | }; | 434 | }; |
435 | self.buf += &text; | ||
436 | self.text_pos += TextUnit::of_str(&text); | ||
347 | } | 437 | } |
348 | 438 | ||
349 | self.text_pos += TextUnit::of_str(&self.buf); | ||
350 | let text = SmolStr::new(self.buf.as_str()); | 439 | let text = SmolStr::new(self.buf.as_str()); |
351 | self.buf.clear(); | 440 | self.buf.clear(); |
352 | self.inner.token(kind, text); | 441 | self.inner.token(kind, text); |
@@ -387,13 +476,16 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
387 | #[cfg(test)] | 476 | #[cfg(test)] |
388 | mod tests { | 477 | mod tests { |
389 | use super::*; | 478 | use super::*; |
390 | use crate::tests::{create_rules, expand}; | 479 | use crate::tests::parse_macro; |
391 | use ra_parser::TokenSource; | 480 | use ra_parser::TokenSource; |
392 | use ra_syntax::algo::{insert_children, InsertPosition}; | 481 | use ra_syntax::{ |
482 | algo::{insert_children, InsertPosition}, | ||
483 | ast::AstNode, | ||
484 | }; | ||
393 | 485 | ||
394 | #[test] | 486 | #[test] |
395 | fn convert_tt_token_source() { | 487 | fn convert_tt_token_source() { |
396 | let rules = create_rules( | 488 | let expansion = parse_macro( |
397 | r#" | 489 | r#" |
398 | macro_rules! literals { | 490 | macro_rules! literals { |
399 | ($i:ident) => { | 491 | ($i:ident) => { |
@@ -406,8 +498,8 @@ mod tests { | |||
406 | } | 498 | } |
407 | } | 499 | } |
408 | "#, | 500 | "#, |
409 | ); | 501 | ) |
410 | let expansion = expand(&rules, "literals!(foo);"); | 502 | .expand_tt("literals!(foo);"); |
411 | let tts = &[expansion.into()]; | 503 | let tts = &[expansion.into()]; |
412 | let buffer = tt::buffer::TokenBuffer::new(tts); | 504 | let buffer = tt::buffer::TokenBuffer::new(tts); |
413 | let mut tt_src = SubtreeTokenSource::new(&buffer); | 505 | let mut tt_src = SubtreeTokenSource::new(&buffer); |
@@ -435,7 +527,7 @@ mod tests { | |||
435 | 527 | ||
436 | #[test] | 528 | #[test] |
437 | fn stmts_token_trees_to_expr_is_err() { | 529 | fn stmts_token_trees_to_expr_is_err() { |
438 | let rules = create_rules( | 530 | let expansion = parse_macro( |
439 | r#" | 531 | r#" |
440 | macro_rules! stmts { | 532 | macro_rules! stmts { |
441 | () => { | 533 | () => { |
@@ -446,8 +538,8 @@ mod tests { | |||
446 | } | 538 | } |
447 | } | 539 | } |
448 | "#, | 540 | "#, |
449 | ); | 541 | ) |
450 | let expansion = expand(&rules, "stmts!();"); | 542 | .expand_tt("stmts!();"); |
451 | assert!(token_tree_to_syntax_node(&expansion, FragmentKind::Expr).is_err()); | 543 | assert!(token_tree_to_syntax_node(&expansion, FragmentKind::Expr).is_err()); |
452 | } | 544 | } |
453 | 545 | ||
@@ -489,6 +581,14 @@ mod tests { | |||
489 | let token_tree = ast::TokenTree::cast(token_tree).unwrap(); | 581 | let token_tree = ast::TokenTree::cast(token_tree).unwrap(); |
490 | let tt = ast_to_token_tree(&token_tree).unwrap().0; | 582 | let tt = ast_to_token_tree(&token_tree).unwrap().0; |
491 | 583 | ||
492 | assert_eq!(tt.delimiter, tt::Delimiter::Brace); | 584 | assert_eq!(tt.delimiter_kind(), Some(tt::DelimiterKind::Brace)); |
585 | } | ||
586 | |||
587 | #[test] | ||
588 | fn test_token_tree_multi_char_punct() { | ||
589 | let source_file = ast::SourceFile::parse("struct Foo { a: x::Y }").ok().unwrap(); | ||
590 | let struct_def = source_file.syntax().descendants().find_map(ast::StructDef::cast).unwrap(); | ||
591 | let tt = ast_to_token_tree(&struct_def).unwrap().0; | ||
592 | token_tree_to_syntax_node(&tt, FragmentKind::Item).unwrap(); | ||
493 | } | 593 | } |
494 | } | 594 | } |