Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax-bridge/src/lib.rs')
| -rw-r--r-- | crates/syntax-bridge/src/lib.rs | 70 |
1 files changed, 54 insertions, 16 deletions
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs index 19801c49e4..a59a3270c9 100644 --- a/crates/syntax-bridge/src/lib.rs +++ b/crates/syntax-bridge/src/lib.rs @@ -12,7 +12,7 @@ use syntax::{ SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{buffer::Cursor, token_to_literal}; +use tt::{buffer::Cursor, token_to_literal, Punct}; pub mod prettify_macro_expansion; mod to_parser_input; @@ -217,8 +217,39 @@ where tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site())); while let Some((token, abs_range)) = conv.bump() { - let delimiter = builder.expected_delimiter().map(|it| it.kind); let tt = match token.as_leaf() { + // These delimiters are not actually valid punctuation, but we produce them in syntax fixup. + // So we need to handle them specially here. + Some(&tt::Leaf::Punct(Punct { + char: char @ ('(' | ')' | '{' | '}' | '[' | ']'), + span, + spacing: _, + })) => { + let found_expected_delimiter = + builder.expected_delimiters().enumerate().find(|(_, delim)| match delim.kind { + tt::DelimiterKind::Parenthesis => char == ')', + tt::DelimiterKind::Brace => char == '}', + tt::DelimiterKind::Bracket => char == ']', + tt::DelimiterKind::Invisible => false, + }); + if let Some((idx, _)) = found_expected_delimiter { + for _ in 0..=idx { + builder.close(span); + } + continue; + } + + let delim = match char { + '(' => tt::DelimiterKind::Parenthesis, + '{' => tt::DelimiterKind::Brace, + '[' => tt::DelimiterKind::Bracket, + _ => panic!("unmatched closing delimiter from syntax fixup"), + }; + + // Start a new subtree + builder.open(delim, span); + continue; + } Some(leaf) => leaf.clone(), None => match token.kind(conv) { // Desugar doc comments into doc attributes @@ -228,17 +259,24 @@ where continue; } kind if kind.is_punct() && kind != UNDERSCORE => { - let expected = match delimiter { - Some(tt::DelimiterKind::Parenthesis) => Some(T![')']), - Some(tt::DelimiterKind::Brace) => Some(T!['}']), - Some(tt::DelimiterKind::Bracket) => Some(T![']']), - Some(tt::DelimiterKind::Invisible) | None => None, - }; + let found_expected_delimiter = + builder.expected_delimiters().enumerate().find(|(_, delim)| { + match delim.kind { + tt::DelimiterKind::Parenthesis => kind == T![')'], + tt::DelimiterKind::Brace => kind == T!['}'], + tt::DelimiterKind::Bracket => kind == T![']'], + tt::DelimiterKind::Invisible => false, + } + }); // Current token is a closing delimiter that we expect, fix up the closing span - // and end the subtree here - if matches!(expected, Some(expected) if expected == kind) { - builder.close(conv.span_for(abs_range)); + // and end the subtree here. + // We also close any open inner subtrees that might be missing their delimiter. + if let Some((idx, _)) = found_expected_delimiter { + for _ in 0..=idx { + // FIXME: record an error somewhere if we're closing more than one tree here? + builder.close(conv.span_for(abs_range)); + } continue; } @@ -262,6 +300,7 @@ where let Some(char) = token.to_char(conv) else { panic!("Token from lexer must be single char: token = {token:#?}") }; + // FIXME: this might still be an unmatched closing delimiter? Maybe we should assert here tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }) } kind => { @@ -317,11 +356,10 @@ where builder.push(tt); } - // If we get here, we've consumed all input tokens. - // We might have more than one subtree in the stack, if the delimiters are improperly balanced. - // Merge them so we're left with one. - builder.flatten_unclosed_subtrees(); - + while builder.expected_delimiters().next().is_some() { + // FIXME: record an error somewhere? + builder.close(conv.call_site()); + } builder.build_skip_top_subtree() } |