Rollup merge of #108715 - chenyukang:yukang/cleanup-parser-delims, r=compiler-errors
Remove unclosed_delims from parser After landing https://github.com/rust-lang/rust/pull/108297 we could remove `unclosed_delims` from the parser now.
This commit is contained in:
commit
dd6f03de9a
@ -19,7 +19,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
use crate::lexer::UnmatchedDelim;
|
|
||||||
use crate::parser;
|
use crate::parser;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
@ -220,7 +219,6 @@ fn emit_verbose(self, err: &mut Diagnostic) {
|
|||||||
/// is dropped.
|
/// is dropped.
|
||||||
pub struct SnapshotParser<'a> {
|
pub struct SnapshotParser<'a> {
|
||||||
parser: Parser<'a>,
|
parser: Parser<'a>,
|
||||||
unclosed_delims: Vec<UnmatchedDelim>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deref for SnapshotParser<'a> {
|
impl<'a> Deref for SnapshotParser<'a> {
|
||||||
@ -255,27 +253,15 @@ pub(super) fn diagnostic(&self) -> &'a Handler {
|
|||||||
&self.sess.span_diagnostic
|
&self.sess.span_diagnostic
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace `self` with `snapshot.parser` and extend `unclosed_delims` with `snapshot.unclosed_delims`.
|
/// Replace `self` with `snapshot.parser`.
|
||||||
/// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears.
|
|
||||||
pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
|
pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
|
||||||
*self = snapshot.parser;
|
*self = snapshot.parser;
|
||||||
self.unclosed_delims.extend(snapshot.unclosed_delims);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unclosed_delims(&self) -> &[UnmatchedDelim] {
|
|
||||||
&self.unclosed_delims
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a snapshot of the `Parser`.
|
/// Create a snapshot of the `Parser`.
|
||||||
pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
|
pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
|
||||||
let mut snapshot = self.clone();
|
let snapshot = self.clone();
|
||||||
let unclosed_delims = self.unclosed_delims.clone();
|
SnapshotParser { parser: snapshot }
|
||||||
// Clear `unclosed_delims` in snapshot to avoid
|
|
||||||
// duplicate errors being emitted when the `Parser`
|
|
||||||
// is dropped (which may or may not happen, depending
|
|
||||||
// if the parsing the snapshot is created for is successful)
|
|
||||||
snapshot.unclosed_delims.clear();
|
|
||||||
SnapshotParser { parser: snapshot, unclosed_delims }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
|
pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
|
||||||
@ -579,21 +565,6 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
label_sp
|
label_sp
|
||||||
};
|
};
|
||||||
match self.recover_closing_delimiter(
|
|
||||||
&expected
|
|
||||||
.iter()
|
|
||||||
.filter_map(|tt| match tt {
|
|
||||||
TokenType::Token(t) => Some(t.clone()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
err,
|
|
||||||
) {
|
|
||||||
Err(e) => err = e,
|
|
||||||
Ok(recovered) => {
|
|
||||||
return Ok(recovered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.check_too_many_raw_str_terminators(&mut err) {
|
if self.check_too_many_raw_str_terminators(&mut err) {
|
||||||
if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
|
if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
|
||||||
@ -1573,12 +1544,6 @@ pub(super) fn unexpected_try_recover(
|
|||||||
);
|
);
|
||||||
let mut err = self.struct_span_err(sp, &msg);
|
let mut err = self.struct_span_err(sp, &msg);
|
||||||
let label_exp = format!("expected `{token_str}`");
|
let label_exp = format!("expected `{token_str}`");
|
||||||
match self.recover_closing_delimiter(&[t.clone()], err) {
|
|
||||||
Err(e) => err = e,
|
|
||||||
Ok(recovered) => {
|
|
||||||
return Ok(recovered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let sm = self.sess.source_map();
|
let sm = self.sess.source_map();
|
||||||
if !sm.is_multiline(prev_sp.until(sp)) {
|
if !sm.is_multiline(prev_sp.until(sp)) {
|
||||||
// When the spans are in the same line, it means that the only content
|
// When the spans are in the same line, it means that the only content
|
||||||
@ -1795,81 +1760,6 @@ pub(super) fn recover_seq_parse_error(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn recover_closing_delimiter(
|
|
||||||
&mut self,
|
|
||||||
tokens: &[TokenKind],
|
|
||||||
mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
|
|
||||||
) -> PResult<'a, bool> {
|
|
||||||
let mut pos = None;
|
|
||||||
// We want to use the last closing delim that would apply.
|
|
||||||
for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
|
|
||||||
if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
|
|
||||||
&& Some(self.token.span) > unmatched.unclosed_span
|
|
||||||
{
|
|
||||||
pos = Some(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match pos {
|
|
||||||
Some(pos) => {
|
|
||||||
// Recover and assume that the detected unclosed delimiter was meant for
|
|
||||||
// this location. Emit the diagnostic and act as if the delimiter was
|
|
||||||
// present for the parser's sake.
|
|
||||||
|
|
||||||
// Don't attempt to recover from this unclosed delimiter more than once.
|
|
||||||
let unmatched = self.unclosed_delims.remove(pos);
|
|
||||||
let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
|
|
||||||
if unmatched.found_delim.is_none() {
|
|
||||||
// We encountered `Eof`, set this fact here to avoid complaining about missing
|
|
||||||
// `fn main()` when we found place to suggest the closing brace.
|
|
||||||
*self.sess.reached_eof.borrow_mut() = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to suggest the inclusion of the closing delimiter where it makes
|
|
||||||
// the most sense, which is immediately after the last token:
|
|
||||||
//
|
|
||||||
// {foo(bar {}}
|
|
||||||
// ^ ^
|
|
||||||
// | |
|
|
||||||
// | help: `)` may belong here
|
|
||||||
// |
|
|
||||||
// unclosed delimiter
|
|
||||||
if let Some(sp) = unmatched.unclosed_span {
|
|
||||||
let mut primary_span: Vec<Span> =
|
|
||||||
err.span.primary_spans().iter().cloned().collect();
|
|
||||||
primary_span.push(sp);
|
|
||||||
let mut primary_span: MultiSpan = primary_span.into();
|
|
||||||
for span_label in err.span.span_labels() {
|
|
||||||
if let Some(label) = span_label.label {
|
|
||||||
primary_span.push_span_label(span_label.span, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err.set_span(primary_span);
|
|
||||||
err.span_label(sp, "unclosed delimiter");
|
|
||||||
}
|
|
||||||
// Backticks should be removed to apply suggestions.
|
|
||||||
let mut delim = delim.to_string();
|
|
||||||
delim.retain(|c| c != '`');
|
|
||||||
err.span_suggestion_short(
|
|
||||||
self.prev_token.span.shrink_to_hi(),
|
|
||||||
&format!("`{delim}` may belong here"),
|
|
||||||
delim,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
if unmatched.found_delim.is_none() {
|
|
||||||
// Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown
|
|
||||||
// errors which would be emitted elsewhere in the parser and let other error
|
|
||||||
// recovery consume the rest of the file.
|
|
||||||
Err(err)
|
|
||||||
} else {
|
|
||||||
err.emit();
|
|
||||||
self.expected_tokens.clear(); // Reduce the number of errors.
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Eats tokens until we can be relatively sure we reached the end of the
|
/// Eats tokens until we can be relatively sure we reached the end of the
|
||||||
/// statement. This is something of a best-effort heuristic.
|
/// statement. This is something of a best-effort heuristic.
|
||||||
///
|
///
|
||||||
|
@ -1394,19 +1394,6 @@ fn parse_expr_bottom(&mut self) -> PResult<'a, P<Expr>> {
|
|||||||
self.parse_expr_let()
|
self.parse_expr_let()
|
||||||
} else if self.eat_keyword(kw::Underscore) {
|
} else if self.eat_keyword(kw::Underscore) {
|
||||||
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
|
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
|
||||||
} else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
|
|
||||||
// Don't complain about bare semicolons after unclosed braces
|
|
||||||
// recovery in order to keep the error count down. Fixing the
|
|
||||||
// delimiters will possibly also fix the bare semicolon found in
|
|
||||||
// expression context. For example, silence the following error:
|
|
||||||
//
|
|
||||||
// error: expected expression, found `;`
|
|
||||||
// --> file.rs:2:13
|
|
||||||
// |
|
|
||||||
// 2 | foo(bar(;
|
|
||||||
// | ^ expected expression
|
|
||||||
self.bump();
|
|
||||||
Ok(self.mk_expr_err(self.token.span))
|
|
||||||
} else if self.token.uninterpolated_span().rust_2018() {
|
} else if self.token.uninterpolated_span().rust_2018() {
|
||||||
// `Span::rust_2018()` is somewhat expensive; don't get it repeatedly.
|
// `Span::rust_2018()` is somewhat expensive; don't get it repeatedly.
|
||||||
if self.check_keyword(kw::Async) {
|
if self.check_keyword(kw::Async) {
|
||||||
|
@ -125,16 +125,13 @@ pub(super) fn parse_item_common(
|
|||||||
return Ok(Some(item.into_inner()));
|
return Ok(Some(item.into_inner()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut unclosed_delims = vec![];
|
|
||||||
let item =
|
let item =
|
||||||
self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| {
|
self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| {
|
||||||
let item =
|
let item =
|
||||||
this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode);
|
this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode);
|
||||||
unclosed_delims.append(&mut this.unclosed_delims);
|
|
||||||
Ok((item?, TrailingToken::None))
|
Ok((item?, TrailingToken::None))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.unclosed_delims.append(&mut unclosed_delims);
|
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1960,21 +1957,12 @@ fn report_invalid_macro_expansion_item(&self, args: &DelimArgs) {
|
|||||||
// FIXME: This will make us not emit the help even for declarative
|
// FIXME: This will make us not emit the help even for declarative
|
||||||
// macros within the same crate (that we can fix), which is sad.
|
// macros within the same crate (that we can fix), which is sad.
|
||||||
if !span.from_expansion() {
|
if !span.from_expansion() {
|
||||||
if self.unclosed_delims.is_empty() {
|
let DelimSpan { open, close } = args.dspan;
|
||||||
let DelimSpan { open, close } = args.dspan;
|
err.multipart_suggestion(
|
||||||
err.multipart_suggestion(
|
"change the delimiters to curly braces",
|
||||||
"change the delimiters to curly braces",
|
vec![(open, "{".to_string()), (close, '}'.to_string())],
|
||||||
vec![(open, "{".to_string()), (close, '}'.to_string())],
|
Applicability::MaybeIncorrect,
|
||||||
Applicability::MaybeIncorrect,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
err.span_suggestion(
|
|
||||||
span,
|
|
||||||
"change the delimiters to curly braces",
|
|
||||||
" { /* items */ }",
|
|
||||||
Applicability::HasPlaceholders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
span.shrink_to_hi(),
|
span.shrink_to_hi(),
|
||||||
"add a semicolon",
|
"add a semicolon",
|
||||||
|
@ -146,10 +146,7 @@ pub struct Parser<'a> {
|
|||||||
/// See the comments in the `parse_path_segment` function for more details.
|
/// See the comments in the `parse_path_segment` function for more details.
|
||||||
unmatched_angle_bracket_count: u32,
|
unmatched_angle_bracket_count: u32,
|
||||||
max_angle_bracket_count: u32,
|
max_angle_bracket_count: u32,
|
||||||
/// A list of all unclosed delimiters found by the lexer. If an entry is used for error recovery
|
|
||||||
/// it gets removed from here. Every entry left at the end gets emitted as an independent
|
|
||||||
/// error.
|
|
||||||
pub(super) unclosed_delims: Vec<UnmatchedDelim>,
|
|
||||||
last_unexpected_token_span: Option<Span>,
|
last_unexpected_token_span: Option<Span>,
|
||||||
/// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
|
/// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
|
||||||
/// looked like it could have been a mistyped path or literal `Option:Some(42)`).
|
/// looked like it could have been a mistyped path or literal `Option:Some(42)`).
|
||||||
@ -168,7 +165,7 @@ pub struct Parser<'a> {
|
|||||||
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
|
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
|
||||||
// it doesn't unintentionally get bigger.
|
// it doesn't unintentionally get bigger.
|
||||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||||
rustc_data_structures::static_assert_size!(Parser<'_>, 312);
|
rustc_data_structures::static_assert_size!(Parser<'_>, 288);
|
||||||
|
|
||||||
/// Stores span information about a closure.
|
/// Stores span information about a closure.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -215,12 +212,6 @@ struct CaptureState {
|
|||||||
inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>,
|
inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for Parser<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
emit_unclosed_delims(&mut self.unclosed_delims, &self.sess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
|
/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
|
||||||
/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
|
/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
|
||||||
/// use this type to emit them as a linear sequence. But a linear sequence is
|
/// use this type to emit them as a linear sequence. But a linear sequence is
|
||||||
@ -478,7 +469,6 @@ pub fn new(
|
|||||||
desugar_doc_comments,
|
desugar_doc_comments,
|
||||||
unmatched_angle_bracket_count: 0,
|
unmatched_angle_bracket_count: 0,
|
||||||
max_angle_bracket_count: 0,
|
max_angle_bracket_count: 0,
|
||||||
unclosed_delims: Vec::new(),
|
|
||||||
last_unexpected_token_span: None,
|
last_unexpected_token_span: None,
|
||||||
last_type_ascription: None,
|
last_type_ascription: None,
|
||||||
subparser_name,
|
subparser_name,
|
||||||
@ -859,7 +849,6 @@ fn parse_seq_to_before_tokens<T>(
|
|||||||
let mut recovered = false;
|
let mut recovered = false;
|
||||||
let mut trailing = false;
|
let mut trailing = false;
|
||||||
let mut v = ThinVec::new();
|
let mut v = ThinVec::new();
|
||||||
let unclosed_delims = !self.unclosed_delims.is_empty();
|
|
||||||
|
|
||||||
while !self.expect_any_with_type(kets, expect) {
|
while !self.expect_any_with_type(kets, expect) {
|
||||||
if let token::CloseDelim(..) | token::Eof = self.token.kind {
|
if let token::CloseDelim(..) | token::Eof = self.token.kind {
|
||||||
@ -901,7 +890,7 @@ fn parse_seq_to_before_tokens<T>(
|
|||||||
_ => {
|
_ => {
|
||||||
// Attempt to keep parsing if it was a similar separator.
|
// Attempt to keep parsing if it was a similar separator.
|
||||||
if let Some(tokens) = t.similar_tokens() {
|
if let Some(tokens) = t.similar_tokens() {
|
||||||
if tokens.contains(&self.token.kind) && !unclosed_delims {
|
if tokens.contains(&self.token.kind) {
|
||||||
self.bump();
|
self.bump();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user