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 Jul 6, 2024
1 parent b317f9f commit 1361d1a
Show file tree
Hide file tree
Showing 21 changed files with 1,152 additions and 86 deletions.
30 changes: 30 additions & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,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 @@ -837,6 +839,23 @@ impl<'a> DiagCtxtHandle<'a> {
Some(Diag::new_diagnostic(self, diag))
}

/// Steals a previously stashed error with the given `Span` and
/// [`StashKey`] as the key, and cancels it if found.
/// Panics if the found diagnostic's level isn't `Level::Error`.
pub fn steal_err(&self, span: Span, key: StashKey, _: ErrorGuaranteed) -> bool {
let key = (span.with_parent(None), key);
// FIXME(#120456) - is `swap_remove` correct?
self.inner
.borrow_mut()
.stashed_diagnostics
.swap_remove(&key)
.inspect(|(diag, guar)| {
assert_eq!(diag.level, Error);
assert!(guar.is_some())
})
.is_some()
}

/// Steals a previously stashed error with the given `Span` and
/// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
/// no matching diagnostic is found. Panics if the found diagnostic's level
Expand Down Expand Up @@ -1281,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
14 changes: 14 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,20 @@ parse_unexpected_expr_in_pat =
.label = arbitrary expressions are not allowed in patterns
parse_unexpected_expr_in_pat_const_sugg = extract the expression into a `const` and refer to it
parse_unexpected_expr_in_pat_create_guard_sugg = check the value in an arm guard
parse_unexpected_expr_in_pat_inline_const_sugg = wrap the expression in a inline const (requires `{"#"}![feature(inline_const_pat)]`)
parse_unexpected_expr_in_pat_remove_let_sugg =
remove the `let` if you meant to {$has_initializer ->
[true] do an assignment
*[false] evaluate an expression
}
parse_unexpected_expr_in_pat_update_guard_sugg = check the value in the arm guard
parse_unexpected_if_with_if = unexpected `if` in the condition expression
.suggestion = remove the `if`
Expand Down
86 changes: 86 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2424,6 +2424,92 @@ pub(crate) struct UnexpectedExpressionInPattern {
pub is_bound: bool,
}

#[derive(Subdiagnostic)]
pub(crate) enum UnexpectedExpressionInPatternSugg {
#[multipart_suggestion(
parse_unexpected_expr_in_pat_remove_let_sugg,
applicability = "maybe-incorrect",
style = "verbose"
)]
RemoveLet {
/// The span of the `let` keyword.
#[suggestion_part(code = "")]
let_span: Span,
/// The span of the type annotation.
#[suggestion_part(code = "")]
ty_span: Option<Span>,
/// `true` iff the `let` statement has an initializer.
has_initializer: bool,
},

#[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.
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.
#[suggestion_part(code = "(")]
guard_lo: Span,
/// The end of the match arm guard's expression.
#[suggestion_part(code = ") && {ident} == {expr}")]
guard_hi: Span,
/// The suggested identifier.
ident: String,
/// `ident_span`'s snippet.
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}: _ = {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)]
#[diag(parse_unexpected_paren_in_range_pat)]
pub(crate) struct UnexpectedParenInRangePat {
Expand Down
Loading

0 comments on commit 1361d1a

Please sign in to comment.