Add ErrorGuaranteed to Recovered::Yes and use it more.

The starting point for this was identical comments on two different
fields, in `ast::VariantData::Struct` and `hir::VariantData::Struct`:
```
    // FIXME: investigate making this a `Option<ErrorGuaranteed>`
    recovered: bool
```
I tried that, and then found that I needed to add an `ErrorGuaranteed`
to `Recovered::Yes`. Then I ended up using `Recovered` instead of
`Option<ErrorGuaranteed>` for these two places and elsewhere, which
required moving `ErrorGuaranteed` from `rustc_parse` to `rustc_ast`.

This makes things more consistent, because `Recovered` is used in more
places, and there are fewer uses of `bool` and
`Option<ErrorGuaranteed>`. And safer, because it's difficult/impossible
to set `recovered` to `Recovered::Yes` without having emitted an error.
This commit is contained in:
Nicholas Nethercote 2024-05-09 18:44:40 +10:00
parent 87293c9585
commit fd91925bce
16 changed files with 91 additions and 105 deletions

View file

@ -1422,7 +1422,7 @@ pub enum ExprKind {
/// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`). /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
/// ///
/// `Span` represents the whole `let pat = expr` statement. /// `Span` represents the whole `let pat = expr` statement.
Let(P<Pat>, P<Expr>, Span, Option<ErrorGuaranteed>), Let(P<Pat>, P<Expr>, Span, Recovered),
/// An `if` block, with an optional `else` block. /// An `if` block, with an optional `else` block.
/// ///
/// `if expr { block } else { expr }` /// `if expr { block } else { expr }`
@ -2881,17 +2881,20 @@ pub struct FieldDef {
pub is_placeholder: bool, pub is_placeholder: bool,
} }
/// Was parsing recovery performed?
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum Recovered {
No,
Yes(ErrorGuaranteed),
}
/// Fields and constructor ids of enum variants and structs. /// Fields and constructor ids of enum variants and structs.
#[derive(Clone, Encodable, Decodable, Debug)] #[derive(Clone, Encodable, Decodable, Debug)]
pub enum VariantData { pub enum VariantData {
/// Struct variant. /// Struct variant.
/// ///
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`. /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
Struct { Struct { fields: ThinVec<FieldDef>, recovered: Recovered },
fields: ThinVec<FieldDef>,
// FIXME: investigate making this a `Option<ErrorGuaranteed>`
recovered: bool,
},
/// Tuple variant. /// Tuple variant.
/// ///
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.

View file

@ -158,13 +158,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ohs = self.lower_expr(ohs); let ohs = self.lower_expr(ohs);
hir::ExprKind::AddrOf(*k, *m, ohs) hir::ExprKind::AddrOf(*k, *m, ohs)
} }
ExprKind::Let(pat, scrutinee, span, is_recovered) => { ExprKind::Let(pat, scrutinee, span, recovered) => {
hir::ExprKind::Let(self.arena.alloc(hir::LetExpr { hir::ExprKind::Let(self.arena.alloc(hir::LetExpr {
span: self.lower_span(*span), span: self.lower_span(*span),
pat: self.lower_pat(pat), pat: self.lower_pat(pat),
ty: None, ty: None,
init: self.lower_expr(scrutinee), init: self.lower_expr(scrutinee),
is_recovered: *is_recovered, recovered: *recovered,
})) }))
} }
ExprKind::If(cond, then, else_opt) => { ExprKind::If(cond, then, else_opt) => {

View file

@ -1283,7 +1283,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fields.iter().enumerate().map(|f| this.lower_field_def(f)), fields.iter().enumerate().map(|f| this.lower_field_def(f)),
); );
let span = t.span; let span = t.span;
let variant_data = hir::VariantData::Struct { fields, recovered: false }; let variant_data =
hir::VariantData::Struct { fields, recovered: ast::Recovered::No };
// FIXME: capture the generics from the outer adt. // FIXME: capture the generics from the outer adt.
let generics = hir::Generics::empty(); let generics = hir::Generics::empty();
let kind = match t.kind { let kind = match t.kind {

View file

@ -7,14 +7,13 @@ use rustc_ast::{token, StmtKind};
use rustc_ast::{ use rustc_ast::{
Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs,
FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, Recovered,
}; };
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans}; use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans};
use rustc_expand::base::*; use rustc_expand::base::*;
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
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, ErrorGuaranteed, InnerSpan, Span}; use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span};
@ -112,7 +111,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a,
_ => return Err(err), _ => return Err(err),
} }
} }
Ok(Recovered::Yes) => (), Ok(Recovered::Yes(_)) => (),
Ok(Recovered::No) => unreachable!(), Ok(Recovered::No) => unreachable!(),
} }
} }

View file

@ -174,7 +174,10 @@ pub(crate) fn placeholder(
}]), }]),
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
attrs: Default::default(), attrs: Default::default(),
data: ast::VariantData::Struct { fields: Default::default(), recovered: false }, data: ast::VariantData::Struct {
fields: Default::default(),
recovered: ast::Recovered::No
},
disr_expr: None, disr_expr: None,
id, id,
ident, ident,

View file

@ -1308,9 +1308,9 @@ pub struct LetExpr<'hir> {
pub pat: &'hir Pat<'hir>, pub pat: &'hir Pat<'hir>,
pub ty: Option<&'hir Ty<'hir>>, pub ty: Option<&'hir Ty<'hir>>,
pub init: &'hir Expr<'hir>, pub init: &'hir Expr<'hir>,
/// `Some` when this let expressions is not in a syntanctically valid location. /// `Recovered::Yes` when this let expressions is not in a syntanctically valid location.
/// Used to prevent building MIR in such situations. /// Used to prevent building MIR in such situations.
pub is_recovered: Option<ErrorGuaranteed>, pub recovered: ast::Recovered,
} }
#[derive(Debug, Clone, Copy, HashStable_Generic)] #[derive(Debug, Clone, Copy, HashStable_Generic)]
@ -3030,11 +3030,7 @@ pub enum VariantData<'hir> {
/// A struct variant. /// A struct variant.
/// ///
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`. /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
Struct { Struct { fields: &'hir [FieldDef<'hir>], recovered: ast::Recovered },
fields: &'hir [FieldDef<'hir>],
// FIXME: investigate making this a `Option<ErrorGuaranteed>`
recovered: bool,
},
/// A tuple variant. /// A tuple variant.
/// ///
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.

View file

@ -768,7 +768,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::DropTemps(ref subexpression) => { ExprKind::DropTemps(ref subexpression) => {
try_visit!(visitor.visit_expr(subexpression)); try_visit!(visitor.visit_expr(subexpression));
} }
ExprKind::Let(LetExpr { span: _, pat, ty, init, is_recovered: _ }) => { ExprKind::Let(LetExpr { span: _, pat, ty, init, recovered: _ }) => {
// match the visit order in walk_local // match the visit order in walk_local
try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_expr(init));
try_visit!(visitor.visit_pat(pat)); try_visit!(visitor.visit_pat(pat));

View file

@ -14,6 +14,7 @@
//! At present, however, we do run collection across all items in the //! At present, however, we do run collection across all items in the
//! crate as a kind of pass. This should eventually be factored away. //! crate as a kind of pass. This should eventually be factored away.
use rustc_ast::Recovered;
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::UnordMap;
@ -1005,10 +1006,7 @@ fn lower_variant(
vis: tcx.visibility(f.def_id), vis: tcx.visibility(f.def_id),
}) })
.collect(); .collect();
let recovered = match def { let recovered = matches!(def, hir::VariantData::Struct { recovered: Recovered::Yes(_), .. });
hir::VariantData::Struct { recovered, .. } => *recovered,
_ => false,
};
ty::VariantDef::new( ty::VariantDef::new(
ident.name, ident.name,
variant_did.map(LocalDefId::to_def_id), variant_did.map(LocalDefId::to_def_id),

View file

@ -1271,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// otherwise check exactly as a let statement // otherwise check exactly as a let statement
self.check_decl((let_expr, hir_id).into()); self.check_decl((let_expr, hir_id).into());
// but return a bool, for this is a boolean expression // but return a bool, for this is a boolean expression
if let Some(error_guaranteed) = let_expr.is_recovered { if let ast::Recovered::Yes(error_guaranteed) = let_expr.recovered {
self.set_tainted_by_errors(error_guaranteed); self.set_tainted_by_errors(error_guaranteed);
Ty::new_error(self.tcx, error_guaranteed) Ty::new_error(self.tcx, error_guaranteed)
} else { } else {

View file

@ -50,7 +50,7 @@ impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> {
impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> { impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> {
fn from((let_expr, hir_id): (&'a hir::LetExpr<'a>, HirId)) -> Self { fn from((let_expr, hir_id): (&'a hir::LetExpr<'a>, HirId)) -> Self {
let hir::LetExpr { pat, ty, span, init, is_recovered: _ } = *let_expr; let hir::LetExpr { pat, ty, span, init, recovered: _ } = *let_expr;
Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr } Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr }
} }
} }

View file

@ -22,7 +22,6 @@ 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};
@ -31,7 +30,7 @@ use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ use rustc_ast::{
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat,
PatKind, Path, PathSegment, QSelf, Ty, TyKind, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,
}; };
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
@ -527,14 +526,14 @@ impl<'a> Parser<'a> {
// //
// let x = 32: // let x = 32:
// let y = 42; // let y = 42;
self.dcx().emit_err(ExpectedSemi { let guar = self.dcx().emit_err(ExpectedSemi {
span: self.token.span, span: self.token.span,
token: self.token.clone(), token: self.token.clone(),
unexpected_token_label: None, unexpected_token_label: None,
sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span), sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
}); });
self.bump(); self.bump();
return Ok(Recovered::Yes); return Ok(Recovered::Yes(guar));
} 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())
@ -552,13 +551,13 @@ impl<'a> Parser<'a> {
// let x = 32 // let x = 32
// let y = 42; // let y = 42;
let span = self.prev_token.span.shrink_to_hi(); let span = self.prev_token.span.shrink_to_hi();
self.dcx().emit_err(ExpectedSemi { let guar = self.dcx().emit_err(ExpectedSemi {
span, span,
token: self.token.clone(), token: self.token.clone(),
unexpected_token_label: Some(self.token.span), unexpected_token_label: Some(self.token.span),
sugg: ExpectedSemiSugg::AddSemi(span), sugg: ExpectedSemiSugg::AddSemi(span),
}); });
return Ok(Recovered::Yes); return Ok(Recovered::Yes(guar));
} }
} }
@ -712,8 +711,8 @@ 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(); let guar = err.emit();
return Ok(Recovered::Yes); return Ok(Recovered::Yes(guar));
} else { } else {
return Err(err); return Err(err);
} }
@ -1251,7 +1250,7 @@ impl<'a> Parser<'a> {
} }
} }
} }
Ok((_, _, Recovered::Yes)) => {} Ok((_, _, Recovered::Yes(_))) => {}
Err(err) => { Err(err) => {
err.cancel(); err.cancel();
} }
@ -1284,13 +1283,13 @@ impl<'a> Parser<'a> {
/// Check to see if a pair of chained operators looks like an attempt at chained comparison, /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
/// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
/// parenthesising the leftmost comparison. /// parenthesising the leftmost comparison. The return value indicates if recovery happened.
fn attempt_chained_comparison_suggestion( fn attempt_chained_comparison_suggestion(
&mut self, &mut self,
err: &mut ComparisonOperatorsCannotBeChained, err: &mut ComparisonOperatorsCannotBeChained,
inner_op: &Expr, inner_op: &Expr,
outer_op: &Spanned<AssocOp>, outer_op: &Spanned<AssocOp>,
) -> Recovered { ) -> bool {
if let ExprKind::Binary(op, l1, r1) = &inner_op.kind { if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
if let ExprKind::Field(_, ident) = l1.kind if let ExprKind::Field(_, ident) = l1.kind
&& ident.as_str().parse::<i32>().is_err() && ident.as_str().parse::<i32>().is_err()
@ -1298,7 +1297,7 @@ impl<'a> Parser<'a> {
{ {
// The parser has encountered `foo.bar<baz`, the likelihood of the turbofish // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
// suggestion being the only one to apply is high. // suggestion being the only one to apply is high.
return Recovered::No; return false;
} }
return match (op.node, &outer_op.node) { return match (op.node, &outer_op.node) {
// `x == y == z` // `x == y == z`
@ -1317,7 +1316,7 @@ impl<'a> Parser<'a> {
span: inner_op.span.shrink_to_hi(), span: inner_op.span.shrink_to_hi(),
middle_term: expr_to_str(r1), middle_term: expr_to_str(r1),
}); });
Recovered::No // Keep the current parse behavior, where the AST is `(x < y) < z`. false // Keep the current parse behavior, where the AST is `(x < y) < z`.
} }
// `x == y < z` // `x == y < z`
(BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => {
@ -1331,12 +1330,12 @@ impl<'a> Parser<'a> {
left: r1.span.shrink_to_lo(), left: r1.span.shrink_to_lo(),
right: r2.span.shrink_to_hi(), right: r2.span.shrink_to_hi(),
}); });
Recovered::Yes true
} }
Err(expr_err) => { Err(expr_err) => {
expr_err.cancel(); expr_err.cancel();
self.restore_snapshot(snapshot); self.restore_snapshot(snapshot);
Recovered::Yes true
} }
} }
} }
@ -1351,19 +1350,19 @@ impl<'a> Parser<'a> {
left: l1.span.shrink_to_lo(), left: l1.span.shrink_to_lo(),
right: r1.span.shrink_to_hi(), right: r1.span.shrink_to_hi(),
}); });
Recovered::Yes true
} }
Err(expr_err) => { Err(expr_err) => {
expr_err.cancel(); expr_err.cancel();
self.restore_snapshot(snapshot); self.restore_snapshot(snapshot);
Recovered::No false
} }
} }
} }
_ => Recovered::No, _ => false
}; };
} }
Recovered::No false
} }
/// Produces an error if comparison operators are chained (RFC #558). /// Produces an error if comparison operators are chained (RFC #558).
@ -1494,7 +1493,7 @@ impl<'a> Parser<'a> {
// misformatted turbofish, for instance), suggest a correct form. // misformatted turbofish, for instance), suggest a correct form.
let recovered = self let recovered = self
.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); .attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
if matches!(recovered, Recovered::Yes) { if recovered {
let guar = self.dcx().emit_err(err); let guar = self.dcx().emit_err(err);
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar) mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
} else { } else {
@ -1503,10 +1502,10 @@ impl<'a> Parser<'a> {
} }
}; };
} }
let recover = let recovered =
self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
let guar = self.dcx().emit_err(err); let guar = self.dcx().emit_err(err);
if matches!(recover, Recovered::Yes) { if recovered {
return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar); return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar);
} }
} }

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, Recovered, Restrictions, AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken, SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken,
}; };
@ -11,7 +11,7 @@ use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath; use crate::maybe_recover_from_interpolated_ty_qpath;
use ast::mut_visit::{noop_visit_expr, MutVisitor}; use ast::mut_visit::{noop_visit_expr, MutVisitor};
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment}; use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered};
use core::mem; use core::mem;
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
@ -2629,7 +2629,7 @@ impl<'a> Parser<'a> {
CondChecker::new(self).visit_expr(&mut cond); CondChecker::new(self).visit_expr(&mut cond);
if let ExprKind::Let(_, _, _, None) = cond.kind { if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind {
// Remove the last feature gating of a `let` expression since it's stable. // Remove the last feature gating of a `let` expression since it's stable.
self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); self.psess.gated_spans.ungate_last(sym::let_chains, cond.span);
} }
@ -2639,7 +2639,7 @@ impl<'a> Parser<'a> {
/// Parses a `let $pat = $expr` pseudo-expression. /// Parses a `let $pat = $expr` pseudo-expression.
fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> { fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
let err = errors::ExpectedExpressionFoundLet { let err = errors::ExpectedExpressionFoundLet {
span: self.token.span, span: self.token.span,
reason: ForbiddenLetReason::OtherForbidden, reason: ForbiddenLetReason::OtherForbidden,
@ -2650,10 +2650,10 @@ impl<'a> Parser<'a> {
// This was part of a closure, the that part of the parser recover. // This was part of a closure, the that part of the parser recover.
return Err(self.dcx().create_err(err)); return Err(self.dcx().create_err(err));
} else { } else {
Some(self.dcx().emit_err(err)) Recovered::Yes(self.dcx().emit_err(err))
} }
} else { } else {
None Recovered::No
}; };
self.bump(); // Eat `let` token self.bump(); // Eat `let` token
let lo = self.prev_token.span; let lo = self.prev_token.span;
@ -2674,7 +2674,7 @@ impl<'a> Parser<'a> {
} }
let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?;
let span = lo.to(expr.span); let span = lo.to(expr.span);
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, is_recovered))) Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
} }
/// Parses an `else { ... }` expression (`else` token already eaten). /// Parses an `else { ... }` expression (`else` token already eaten).
@ -2998,7 +2998,7 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
first_expr: &P<Expr>, first_expr: &P<Expr>,
arrow_span: Span, arrow_span: Span,
) -> Option<P<Expr>> { ) -> Option<(Span, ErrorGuaranteed)> {
if self.token.kind != token::Semi { if self.token.kind != token::Semi {
return None; return None;
} }
@ -3023,7 +3023,7 @@ impl<'a> Parser<'a> {
errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
}, },
}); });
this.mk_expr_err(span, guar) (span, guar)
}; };
// We might have either a `,` -> `;` typo, or a block without braces. We need // We might have either a `,` -> `;` typo, or a block without braces. We need
// a more subtle parsing strategy. // a more subtle parsing strategy.
@ -3143,9 +3143,12 @@ impl<'a> Parser<'a> {
arm_body = Some(expr); arm_body = Some(expr);
this.eat(&token::Comma); this.eat(&token::Comma);
Ok(Recovered::No) Ok(Recovered::No)
} else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) { } else if let Some((span, guar)) =
this.parse_arm_body_missing_braces(&expr, arrow_span)
{
let body = this.mk_expr_err(span, guar);
arm_body = Some(body); arm_body = Some(body);
Ok(Recovered::Yes) Ok(Recovered::Yes(guar))
} else { } else {
let expr_span = expr.span; let expr_span = expr.span;
arm_body = Some(expr); arm_body = Some(expr);
@ -3223,10 +3226,10 @@ impl<'a> Parser<'a> {
.is_ok(); .is_ok();
if pattern_follows && snapshot.check(&TokenKind::FatArrow) { if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
err.cancel(); err.cancel();
this.dcx().emit_err(errors::MissingCommaAfterMatchArm { let guar = this.dcx().emit_err(errors::MissingCommaAfterMatchArm {
span: arm_span.shrink_to_hi(), span: arm_span.shrink_to_hi(),
}); });
return Ok(Recovered::Yes); return Ok(Recovered::Yes(guar));
} }
Err(err) Err(err)
}); });
@ -3904,15 +3907,16 @@ impl MutVisitor for CondChecker<'_> {
let span = e.span; let span = e.span;
match e.kind { match e.kind {
ExprKind::Let(_, _, _, ref mut is_recovered @ None) => { ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => {
if let Some(reason) = self.forbid_let_reason { if let Some(reason) = self.forbid_let_reason {
*is_recovered = *recovered = Recovered::Yes(self.parser.dcx().emit_err(
Some(self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { errors::ExpectedExpressionFoundLet {
span, span,
reason, reason,
missing_let: self.missing_let, missing_let: self.missing_let,
comparison: self.comparison, comparison: self.comparison,
})); },
));
} else { } else {
self.parser.psess.gated_spans.gate(sym::let_chains, span); self.parser.psess.gated_spans.gate(sym::let_chains, span);
} }
@ -3980,7 +3984,7 @@ impl MutVisitor for CondChecker<'_> {
self.visit_expr(op); self.visit_expr(op);
self.forbid_let_reason = forbid_let_reason; self.forbid_let_reason = forbid_let_reason;
} }
ExprKind::Let(_, _, _, Some(_)) ExprKind::Let(_, _, _, Recovered::Yes(_))
| ExprKind::Array(_) | ExprKind::Array(_)
| ExprKind::ConstBlock(_) | ExprKind::ConstBlock(_)
| ExprKind::Lit(_) | ExprKind::Lit(_)

View file

@ -1,8 +1,7 @@
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::{ use super::{
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Recovered, Trailing, AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, TrailingToken,
TrailingToken,
}; };
use crate::errors::{self, MacroExpandsToAdtField}; use crate::errors::{self, MacroExpandsToAdtField};
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
@ -1540,8 +1539,8 @@ impl<'a> Parser<'a> {
this.bump(); // } this.bump(); // }
err.span_label(span, "while parsing this enum"); err.span_label(span, "while parsing this enum");
err.help(help); err.help(help);
err.emit(); let guar = err.emit();
(thin_vec![], Recovered::Yes) (thin_vec![], Recovered::Yes(guar))
} }
}; };
VariantData::Struct { fields, recovered: recovered.into() } VariantData::Struct { fields, recovered: recovered.into() }
@ -1699,16 +1698,15 @@ impl<'a> Parser<'a> {
let mut recovered = Recovered::No; 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| { match self.parse_field_def(adt_ty) {
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No); Ok(field) => {
recovered = Recovered::Yes; fields.push(field);
e }
});
match field {
Ok(field) => fields.push(field),
Err(mut err) => { Err(mut err) => {
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No);
err.span_label(ident_span, format!("while parsing this {adt_ty}")); err.span_label(ident_span, format!("while parsing this {adt_ty}"));
err.emit(); let guar = err.emit();
recovered = Recovered::Yes(guar);
break; break;
} }
} }
@ -2469,7 +2467,7 @@ 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(Recovered::Yes) => {} Ok(Recovered::Yes(_)) => {}
Ok(Recovered::No) => unreachable!(), Ok(Recovered::No) => unreachable!(),
Err(mut err) => { Err(mut err) => {
// Qualifier keywords ordering check // Qualifier keywords ordering check

View file

@ -27,13 +27,12 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case; use rustc_ast::util::case::Case;
use rustc_ast::{ use rustc_ast::{
self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs,
Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, StrLit, Unsafe, Visibility, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, StrLit, Unsafe, Visibility,
VisibilityKind, DUMMY_NODE_ID, VisibilityKind, DUMMY_NODE_ID,
}; };
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult; use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult};
use rustc_errors::{Applicability, Diag, FatalError, MultiSpan};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -374,19 +373,6 @@ 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(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Trailing { pub enum Trailing {
No, No,
@ -856,9 +842,9 @@ impl<'a> Parser<'a> {
Ok(Recovered::No) => { Ok(Recovered::No) => {
self.current_closure.take(); self.current_closure.take();
} }
Ok(Recovered::Yes) => { Ok(Recovered::Yes(guar)) => {
self.current_closure.take(); self.current_closure.take();
recovered = Recovered::Yes; recovered = Recovered::Yes(guar);
break; break;
} }
Err(mut expect_err) => { Err(mut expect_err) => {

View file

@ -11,14 +11,13 @@ 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;
use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_ast::{StmtKind, DUMMY_NODE_ID};
use rustc_errors::{Applicability, Diag, PResult}; use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
@ -675,11 +674,8 @@ impl<'a> Parser<'a> {
let replace_with_err = 'break_recover: { let replace_with_err = 'break_recover: {
match expect_result { match expect_result {
Ok(Recovered::No) => None, Ok(Recovered::No) => None,
Ok(Recovered::Yes) => { Ok(Recovered::Yes(guar)) => {
// Skip type error to avoid extra errors. // Skip type error to avoid extra errors.
let guar = self
.dcx()
.span_delayed_bug(self.prev_token.span, "expected `;` or `}`");
Some(guar) Some(guar)
} }
Err(e) => { Err(e) => {

View file

@ -180,7 +180,10 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
18 => { 18 => {
let pat = let pat =
P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None }); P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None });
iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e, DUMMY_SP, None))) iter_exprs(
depth - 1,
&mut |e| g(ExprKind::Let(pat.clone(), e, DUMMY_SP, Recovered::No))
)
} }
_ => panic!("bad counter value in iter_exprs"), _ => panic!("bad counter value in iter_exprs"),
} }