mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-28 10:39:45 +00:00
Fix syntax fixup producing invalid punctuation
Fixes #19206. Fixes #18244.
This commit is contained in:
parent
62dea277cc
commit
5335d8cbc5
5 changed files with 105 additions and 88 deletions
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue