1
Fork 0

Add newtype for parser recovery

This commit is contained in:
clubby789 2024-02-13 23:44:33 +00:00
parent f5d0d087ad
commit 4850ae8442
6 changed files with 57 additions and 44 deletions

View file

@ -10,6 +10,7 @@ use rustc_ast::{
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult, SingleLabelManySpans}; use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult, SingleLabelManySpans};
use rustc_expand::base::{self, *}; use rustc_expand::base::{self, *};
use rustc_parse::parser::Recovered;
use rustc_parse_format as parse; use rustc_parse_format as parse;
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span}; use rustc_span::{BytePos, InnerSpan, Span};
@ -111,9 +112,8 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
_ => return Err(err), _ => return Err(err),
} }
} }
Ok(recovered) => { Ok(Recovered::Yes) => (),
assert!(recovered); Ok(Recovered::No) => unreachable!(),
}
} }
} }
first = false; first = false;

View file

@ -22,6 +22,7 @@ use crate::fluent_generated as fluent;
use crate::parser; use crate::parser;
use crate::parser::attr::InnerAttrPolicy; use crate::parser::attr::InnerAttrPolicy;
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
use parser::Recovered;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind};
@ -430,7 +431,7 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
edible: &[TokenKind], edible: &[TokenKind],
inedible: &[TokenKind], inedible: &[TokenKind],
) -> PResult<'a, bool /* recovered */> { ) -> PResult<'a, Recovered> {
debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
fn tokens_to_string(tokens: &[TokenType]) -> String { fn tokens_to_string(tokens: &[TokenType]) -> String {
let mut i = tokens.iter(); let mut i = tokens.iter();
@ -533,7 +534,7 @@ impl<'a> Parser<'a> {
sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span), sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
}); });
self.bump(); self.bump();
return Ok(true); return Ok(Recovered::Yes);
} else if self.look_ahead(0, |t| { } else if self.look_ahead(0, |t| {
t == &token::CloseDelim(Delimiter::Brace) t == &token::CloseDelim(Delimiter::Brace)
|| ((t.can_begin_expr() || t.can_begin_item()) || ((t.can_begin_expr() || t.can_begin_item())
@ -557,7 +558,7 @@ impl<'a> Parser<'a> {
unexpected_token_label: Some(self.token.span), unexpected_token_label: Some(self.token.span),
sugg: ExpectedSemiSugg::AddSemi(span), sugg: ExpectedSemiSugg::AddSemi(span),
}); });
return Ok(true); return Ok(Recovered::Yes);
} }
} }
@ -712,7 +713,7 @@ impl<'a> Parser<'a> {
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) {
err.emit(); err.emit();
return Ok(true); return Ok(Recovered::Yes);
} else { } else {
return Err(err); return Err(err);
} }
@ -1224,7 +1225,7 @@ impl<'a> Parser<'a> {
|p| p.parse_generic_arg(None), |p| p.parse_generic_arg(None),
); );
match x { match x {
Ok((_, _, false)) => { Ok((_, _, Recovered::No)) => {
if self.eat(&token::Gt) { if self.eat(&token::Gt) {
// We made sense of it. Improve the error message. // We made sense of it. Improve the error message.
e.span_suggestion_verbose( e.span_suggestion_verbose(
@ -1248,7 +1249,7 @@ impl<'a> Parser<'a> {
} }
} }
} }
Ok((_, _, true)) => {} Ok((_, _, Recovered::Yes)) => {}
Err(err) => { Err(err) => {
err.cancel(); err.cancel();
} }
@ -1841,10 +1842,7 @@ impl<'a> Parser<'a> {
/// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a /// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a
/// closing delimiter. /// closing delimiter.
pub(super) fn unexpected_try_recover( pub(super) fn unexpected_try_recover(&mut self, t: &TokenKind) -> PResult<'a, Recovered> {
&mut self,
t: &TokenKind,
) -> PResult<'a, bool /* recovered */> {
let token_str = pprust::token_kind_to_string(t); let token_str = pprust::token_kind_to_string(t);
let this_token_str = super::token_descr(&self.token); let this_token_str = super::token_descr(&self.token);
let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {

View file

@ -3,7 +3,7 @@ use super::diagnostics::SnapshotParser;
use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{ use super::{
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Recovered, Restrictions,
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
}; };
@ -3093,10 +3093,10 @@ impl<'a> Parser<'a> {
if !require_comma { if !require_comma {
arm_body = Some(expr); arm_body = Some(expr);
this.eat(&token::Comma); this.eat(&token::Comma);
Ok(false) Ok(Recovered::No)
} else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) { } else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) {
arm_body = Some(body); arm_body = Some(body);
Ok(true) Ok(Recovered::Yes)
} else { } else {
let expr_span = expr.span; let expr_span = expr.span;
arm_body = Some(expr); arm_body = Some(expr);
@ -3177,7 +3177,7 @@ impl<'a> Parser<'a> {
this.dcx().emit_err(errors::MissingCommaAfterMatchArm { this.dcx().emit_err(errors::MissingCommaAfterMatchArm {
span: arm_span.shrink_to_hi(), span: arm_span.shrink_to_hi(),
}); });
return Ok(true); return Ok(Recovered::Yes);
} }
Err(err) Err(err)
}); });

View file

@ -1,6 +1,8 @@
use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use super::{
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Recovered, TrailingToken,
};
use crate::errors::{self, MacroExpandsToAdtField}; use crate::errors::{self, MacroExpandsToAdtField};
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
@ -1534,10 +1536,10 @@ impl<'a> Parser<'a> {
err.span_label(span, "while parsing this enum"); err.span_label(span, "while parsing this enum");
err.help(help); err.help(help);
err.emit(); err.emit();
(thin_vec![], true) (thin_vec![], Recovered::Yes)
} }
}; };
VariantData::Struct { fields, recovered } VariantData::Struct { fields, recovered: recovered.into() }
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) { } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
let body = match this.parse_tuple_struct_body() { let body = match this.parse_tuple_struct_body() {
Ok(body) => body, Ok(body) => body,
@ -1622,7 +1624,7 @@ impl<'a> Parser<'a> {
class_name.span, class_name.span,
generics.where_clause.has_where_token, generics.where_clause.has_where_token,
)?; )?;
VariantData::Struct { fields, recovered } VariantData::Struct { fields, recovered: recovered.into() }
} }
// No `where` so: `struct Foo<T>;` // No `where` so: `struct Foo<T>;`
} else if self.eat(&token::Semi) { } else if self.eat(&token::Semi) {
@ -1634,7 +1636,7 @@ impl<'a> Parser<'a> {
class_name.span, class_name.span,
generics.where_clause.has_where_token, generics.where_clause.has_where_token,
)?; )?;
VariantData::Struct { fields, recovered } VariantData::Struct { fields, recovered: recovered.into() }
// Tuple-style struct definition with optional where-clause. // Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) { } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID); let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
@ -1663,14 +1665,14 @@ impl<'a> Parser<'a> {
class_name.span, class_name.span,
generics.where_clause.has_where_token, generics.where_clause.has_where_token,
)?; )?;
VariantData::Struct { fields, recovered } VariantData::Struct { fields, recovered: recovered.into() }
} else if self.token == token::OpenDelim(Delimiter::Brace) { } else if self.token == token::OpenDelim(Delimiter::Brace) {
let (fields, recovered) = self.parse_record_struct_body( let (fields, recovered) = self.parse_record_struct_body(
"union", "union",
class_name.span, class_name.span,
generics.where_clause.has_where_token, generics.where_clause.has_where_token,
)?; )?;
VariantData::Struct { fields, recovered } VariantData::Struct { fields, recovered: recovered.into() }
} else { } else {
let token_str = super::token_descr(&self.token); let token_str = super::token_descr(&self.token);
let msg = format!("expected `where` or `{{` after union name, found {token_str}"); let msg = format!("expected `where` or `{{` after union name, found {token_str}");
@ -1687,14 +1689,14 @@ impl<'a> Parser<'a> {
adt_ty: &str, adt_ty: &str,
ident_span: Span, ident_span: Span,
parsed_where: bool, parsed_where: bool,
) -> PResult<'a, (ThinVec<FieldDef>, /* recovered */ bool)> { ) -> PResult<'a, (ThinVec<FieldDef>, Recovered)> {
let mut fields = ThinVec::new(); let mut fields = ThinVec::new();
let mut recovered = false; let mut recovered = Recovered::No;
if self.eat(&token::OpenDelim(Delimiter::Brace)) { if self.eat(&token::OpenDelim(Delimiter::Brace)) {
while self.token != token::CloseDelim(Delimiter::Brace) { while self.token != token::CloseDelim(Delimiter::Brace) {
let field = self.parse_field_def(adt_ty).map_err(|e| { let field = self.parse_field_def(adt_ty).map_err(|e| {
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No); self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No);
recovered = true; recovered = Recovered::Yes;
e e
}); });
match field { match field {
@ -2465,8 +2467,8 @@ impl<'a> Parser<'a> {
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
// account for this. // account for this.
match self.expect_one_of(&[], &[]) { match self.expect_one_of(&[], &[]) {
Ok(true) => {} Ok(Recovered::Yes) => {}
Ok(false) => unreachable!(), Ok(Recovered::No) => unreachable!(),
Err(mut err) => { Err(mut err) => {
// Qualifier keywords ordering check // Qualifier keywords ordering check
enum WrongKw { enum WrongKw {

View file

@ -358,6 +358,19 @@ pub enum FollowedByType {
No, No,
} }
/// Whether a function performed recovery
#[derive(Copy, Clone, Debug)]
pub enum Recovered {
No,
Yes,
}
impl From<Recovered> for bool {
fn from(r: Recovered) -> bool {
matches!(r, Recovered::Yes)
}
}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum TokenDescription { pub enum TokenDescription {
ReservedIdentifier, ReservedIdentifier,
@ -456,11 +469,11 @@ impl<'a> Parser<'a> {
} }
/// Expects and consumes the token `t`. Signals an error if the next token is not `t`. /// Expects and consumes the token `t`. Signals an error if the next token is not `t`.
pub fn expect(&mut self, t: &TokenKind) -> PResult<'a, bool /* recovered */> { pub fn expect(&mut self, t: &TokenKind) -> PResult<'a, Recovered> {
if self.expected_tokens.is_empty() { if self.expected_tokens.is_empty() {
if self.token == *t { if self.token == *t {
self.bump(); self.bump();
Ok(false) Ok(Recovered::No)
} else { } else {
self.unexpected_try_recover(t) self.unexpected_try_recover(t)
} }
@ -476,13 +489,13 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
edible: &[TokenKind], edible: &[TokenKind],
inedible: &[TokenKind], inedible: &[TokenKind],
) -> PResult<'a, bool /* recovered */> { ) -> PResult<'a, Recovered> {
if edible.contains(&self.token.kind) { if edible.contains(&self.token.kind) {
self.bump(); self.bump();
Ok(false) Ok(Recovered::No)
} else if inedible.contains(&self.token.kind) { } else if inedible.contains(&self.token.kind) {
// leave it in the input // leave it in the input
Ok(false) Ok(Recovered::No)
} else if self.token.kind != token::Eof } else if self.token.kind != token::Eof
&& self.last_unexpected_token_span == Some(self.token.span) && self.last_unexpected_token_span == Some(self.token.span)
{ {
@ -784,9 +797,9 @@ impl<'a> Parser<'a> {
sep: SeqSep, sep: SeqSep,
expect: TokenExpectType, expect: TokenExpectType,
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (ThinVec<T>, bool /* trailing */, bool /* recovered */)> { ) -> PResult<'a, (ThinVec<T>, bool /* trailing */, Recovered)> {
let mut first = true; let mut first = true;
let mut recovered = false; let mut recovered = Recovered::No;
let mut trailing = false; let mut trailing = false;
let mut v = ThinVec::new(); let mut v = ThinVec::new();
@ -801,12 +814,12 @@ impl<'a> Parser<'a> {
} else { } else {
// check for separator // check for separator
match self.expect(t) { match self.expect(t) {
Ok(false) /* not recovered */ => { Ok(Recovered::No) => {
self.current_closure.take(); self.current_closure.take();
} }
Ok(true) /* recovered */ => { Ok(Recovered::Yes) => {
self.current_closure.take(); self.current_closure.take();
recovered = true; recovered = Recovered::Yes;
break; break;
} }
Err(mut expect_err) => { Err(mut expect_err) => {
@ -979,7 +992,7 @@ impl<'a> Parser<'a> {
ket: &TokenKind, ket: &TokenKind,
sep: SeqSep, sep: SeqSep,
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (ThinVec<T>, bool /* trailing */, bool /* recovered */)> { ) -> PResult<'a, (ThinVec<T>, bool /* trailing */, Recovered)> {
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
} }
@ -993,7 +1006,7 @@ impl<'a> Parser<'a> {
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (ThinVec<T>, bool /* trailing */)> { ) -> PResult<'a, (ThinVec<T>, bool /* trailing */)> {
let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
if !recovered { if matches!(recovered, Recovered::No) {
self.eat(ket); self.eat(ket);
} }
Ok((val, trailing)) Ok((val, trailing))

View file

@ -11,6 +11,7 @@ use crate::errors;
use crate::maybe_whole; use crate::maybe_whole;
use crate::errors::MalformedLoopLabel; use crate::errors::MalformedLoopLabel;
use crate::parser::Recovered;
use ast::Label; use ast::Label;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
@ -661,7 +662,6 @@ impl<'a> Parser<'a> {
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
{ {
// Just check for errors and recover; do not eat semicolon yet. // Just check for errors and recover; do not eat semicolon yet.
// `expect_one_of` returns PResult<'a, bool /* recovered */>
let expect_result = let expect_result =
self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]); self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]);
@ -669,7 +669,7 @@ impl<'a> Parser<'a> {
let replace_with_err = 'break_recover: { let replace_with_err = 'break_recover: {
match expect_result { match expect_result {
// Recover from parser, skip type error to avoid extra errors. // Recover from parser, skip type error to avoid extra errors.
Ok(true) => true, Ok(Recovered::Yes) => true,
Err(e) => { Err(e) => {
if self.recover_colon_as_semi() { if self.recover_colon_as_semi() {
// recover_colon_as_semi has already emitted a nicer error. // recover_colon_as_semi has already emitted a nicer error.
@ -735,7 +735,7 @@ impl<'a> Parser<'a> {
true true
} }
Ok(false) => false, Ok(Recovered::No) => false,
} }
}; };