Skip to content

Commit

Permalink
Add suggestions for expressions in patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
ShE3py committed Aug 6, 2024
1 parent aa09e0f commit e6cfede
Show file tree
Hide file tree
Showing 15 changed files with 1,168 additions and 86 deletions.
44 changes: 33 additions & 11 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,8 @@ pub enum StashKey {
/// Query cycle detected, stashing in favor of a better error.
Cycle,
UndeterminedMacroResolution,
/// Used by `Parser::maybe_recover_trailing_expr`
ExprInPat,
}

fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
Expand Down Expand Up @@ -873,14 +875,9 @@ impl<'a> DiagCtxtHandle<'a> {
}

/// Steals a previously stashed error with the given `Span` and
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
/// [`StashKey`] as the key, and cancels it if found.
/// Panics if the found diagnostic's level isn't `Level::Error`.
pub fn try_steal_replace_and_emit_err(
self,
span: Span,
key: StashKey,
new_err: Diag<'_>,
) -> ErrorGuaranteed {
pub fn try_steal_and_replace_err(self, span: Span, key: StashKey, _: ErrorGuaranteed) -> bool {
let key = (span.with_parent(None), key);
// FIXME(#120456) - is `swap_remove` correct?
let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
Expand All @@ -889,12 +886,26 @@ impl<'a> DiagCtxtHandle<'a> {
assert_eq!(old_err.level, Error);
assert!(guar.is_some());
// Because `old_err` has already been counted, it can only be
// safely cancelled because the `new_err` supplants it.
// safely cancelled because the passed `ErrorGuaranteed` supplants it.
Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
true
}
None => {}
};
new_err.emit()
None => false,
}
}

/// Steals a previously stashed error with the given `Span` and
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
/// Panics if the found diagnostic's level isn't `Level::Error`.
pub fn try_steal_replace_and_emit_err(
self,
span: Span,
key: StashKey,
new_err: Diag<'_>,
) -> ErrorGuaranteed {
let guar = new_err.emit();
self.try_steal_and_replace_err(span, key, guar);
guar
}

pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
Expand Down Expand Up @@ -1289,6 +1300,17 @@ impl<'a> DiagCtxtHandle<'a> {
self.create_err(err).emit()
}

/// See [`DiagCtxtHandle::stash_diagnostic`] for details.
#[track_caller]
pub fn stash_err(
&'a self,
span: Span,
key: StashKey,
err: impl Diagnostic<'a>,
) -> ErrorGuaranteed {
self.create_err(err).stash(span, key).unwrap()
}

/// Ensures that an error is printed. See `Level::DelayedBug`.
//
// No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,14 @@ parse_unexpected_expr_in_pat =
.label = arbitrary expressions are not allowed in patterns
parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const`
parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard
parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`)
parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard
parse_unexpected_if_with_if = unexpected `if` in the condition expression
.suggestion = remove the `if`
Expand Down
77 changes: 77 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore-tidy-filelength

use std::borrow::Cow;

use rustc_ast::token::Token;
Expand Down Expand Up @@ -2592,11 +2594,86 @@ pub(crate) struct ExpectedCommaAfterPatternField {
#[derive(Diagnostic)]
#[diag(parse_unexpected_expr_in_pat)]
pub(crate) struct UnexpectedExpressionInPattern {
// The unexpected expr's span.
#[primary_span]
#[label]
pub span: Span,
/// Was a `RangePatternBound` expected?
pub is_bound: bool,
/// The unexpected expr's precedence (used in match arm guard suggestions).
pub expr_precedence: i8,
}

#[derive(Subdiagnostic)]
pub(crate) enum UnexpectedExpressionInPatternSugg {
#[multipart_suggestion(
parse_unexpected_expr_in_pat_create_guard_sugg,
applicability = "maybe-incorrect"
)]
CreateGuard {
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
#[suggestion_part(code = "{ident}")]
ident_span: Span,
/// The end of the match arm's pattern.
#[suggestion_part(code = " if {ident} == {expr}")]
pat_hi: Span,
/// The suggested identifier.
ident: String,
/// `ident_span`'s snippet (with parentheses if needed).
expr: String,
},

#[multipart_suggestion(
parse_unexpected_expr_in_pat_update_guard_sugg,
applicability = "maybe-incorrect"
)]
UpdateGuard {
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
#[suggestion_part(code = "{ident}")]
ident_span: Span,
/// The beginning of the match arm guard's expression (insert a `(` if `Some`).
#[suggestion_part(code = "(")]
guard_lo: Option<Span>,
/// The end of the match arm guard's expression.
#[suggestion_part(code = "{guard_hi_paren} && {ident} == {expr}")]
guard_hi: Span,
/// Either `")"` or `""`.
guard_hi_paren: &'static str,
/// The suggested identifier.
ident: String,
/// `ident_span`'s snippet (with parentheses if needed).
expr: String,
},

#[multipart_suggestion(
parse_unexpected_expr_in_pat_const_sugg,
applicability = "has-placeholders"
)]
Const {
/// The beginning of statement's line.
#[suggestion_part(code = "{indentation}const {ident}: /* Type */ = {expr};\n")]
stmt_lo: Span,
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
#[suggestion_part(code = "{ident}")]
ident_span: Span,
/// The suggested identifier.
ident: String,
/// `ident_span`'s snippet.
expr: String,
/// The statement's block's indentation.
indentation: String,
},

#[multipart_suggestion(
parse_unexpected_expr_in_pat_inline_const_sugg,
applicability = "maybe-incorrect"
)]
InlineConst {
#[suggestion_part(code = "const {{ ")]
start_span: Span,
#[suggestion_part(code = " }}")]
end_span: Span,
},
}

#[derive(Diagnostic)]
Expand Down
Loading

0 comments on commit e6cfede

Please sign in to comment.