Improve ternary operator recovery

This commit is contained in:
Jamie
2025-05-14 13:32:41 +01:00
committed by clubby789
parent bc7512ee63
commit 1267333ef1
6 changed files with 75 additions and 17 deletions

View File

@@ -41,8 +41,9 @@ use crate::errors::{
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam,
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
UseEqInstead, WrapType,
};
use crate::parser::attr::InnerAttrPolicy;
use crate::{exp, fluent_generated as fluent};
@@ -497,7 +498,7 @@ impl<'a> Parser<'a> {
// If the user is trying to write a ternary expression, recover it and
// return an Err to prevent a cascade of irrelevant diagnostics.
if self.prev_token == token::Question
&& let Err(e) = self.maybe_recover_from_ternary_operator()
&& let Err(e) = self.maybe_recover_from_ternary_operator(None)
{
return Err(e);
}
@@ -1602,12 +1603,18 @@ impl<'a> Parser<'a> {
/// Rust has no ternary operator (`cond ? then : else`). Parse it and try
/// to recover from it if `then` and `else` are valid expressions. Returns
/// an err if this appears to be a ternary expression.
pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> {
/// If we have the span of the condition, we can provide a better error span
/// and code suggestion.
pub(super) fn maybe_recover_from_ternary_operator(
&mut self,
cond: Option<Span>,
) -> PResult<'a, ()> {
if self.prev_token != token::Question {
return PResult::Ok(());
}
let lo = self.prev_token.span.lo();
let question = self.prev_token.span;
let lo = cond.unwrap_or(question).lo();
let snapshot = self.create_snapshot_for_diagnostic();
if match self.parse_expr() {
@@ -1620,11 +1627,20 @@ impl<'a> Parser<'a> {
}
} {
if self.eat_noexpect(&token::Colon) {
let colon = self.prev_token.span;
match self.parse_expr() {
Ok(_) => {
return Err(self
.dcx()
.create_err(TernaryOperator { span: self.token.span.with_lo(lo) }));
Ok(expr) => {
let sugg = cond.map(|cond| TernaryOperatorSuggestion {
before_cond: cond.shrink_to_lo(),
question,
colon,
end: expr.span.shrink_to_hi(),
});
return Err(self.dcx().create_err(TernaryOperator {
span: self.prev_token.span.with_lo(lo),
sugg,
no_sugg: sugg.is_none(),
}));
}
Err(err) => {
err.cancel();

View File

@@ -879,7 +879,12 @@ impl<'a> Parser<'a> {
{
// Just check for errors and recover; do not eat semicolon yet.
let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
let expect_result =
if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) {
Err(e)
} else {
self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)])
};
// Try to both emit a better diagnostic, and avoid further errors by replacing
// the `expr` with `ExprKind::Err`.