Skip to content

Commit e6cfede

Browse files
committed
Add suggestions for expressions in patterns
1 parent aa09e0f commit e6cfede

15 files changed

+1168
-86
lines changed

compiler/rustc_errors/src/lib.rs

+33-11
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@ pub enum StashKey {
543543
/// Query cycle detected, stashing in favor of a better error.
544544
Cycle,
545545
UndeterminedMacroResolution,
546+
/// Used by `Parser::maybe_recover_trailing_expr`
547+
ExprInPat,
546548
}
547549

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

875877
/// Steals a previously stashed error with the given `Span` and
876-
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
878+
/// [`StashKey`] as the key, and cancels it if found.
877879
/// Panics if the found diagnostic's level isn't `Level::Error`.
878-
pub fn try_steal_replace_and_emit_err(
879-
self,
880-
span: Span,
881-
key: StashKey,
882-
new_err: Diag<'_>,
883-
) -> ErrorGuaranteed {
880+
pub fn try_steal_and_replace_err(self, span: Span, key: StashKey, _: ErrorGuaranteed) -> bool {
884881
let key = (span.with_parent(None), key);
885882
// FIXME(#120456) - is `swap_remove` correct?
886883
let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
@@ -889,12 +886,26 @@ impl<'a> DiagCtxtHandle<'a> {
889886
assert_eq!(old_err.level, Error);
890887
assert!(guar.is_some());
891888
// Because `old_err` has already been counted, it can only be
892-
// safely cancelled because the `new_err` supplants it.
889+
// safely cancelled because the passed `ErrorGuaranteed` supplants it.
893890
Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
891+
true
894892
}
895-
None => {}
896-
};
897-
new_err.emit()
893+
None => false,
894+
}
895+
}
896+
897+
/// Steals a previously stashed error with the given `Span` and
898+
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
899+
/// Panics if the found diagnostic's level isn't `Level::Error`.
900+
pub fn try_steal_replace_and_emit_err(
901+
self,
902+
span: Span,
903+
key: StashKey,
904+
new_err: Diag<'_>,
905+
) -> ErrorGuaranteed {
906+
let guar = new_err.emit();
907+
self.try_steal_and_replace_err(span, key, guar);
908+
guar
898909
}
899910

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

1303+
/// See [`DiagCtxtHandle::stash_diagnostic`] for details.
1304+
#[track_caller]
1305+
pub fn stash_err(
1306+
&'a self,
1307+
span: Span,
1308+
key: StashKey,
1309+
err: impl Diagnostic<'a>,
1310+
) -> ErrorGuaranteed {
1311+
self.create_err(err).stash(span, key).unwrap()
1312+
}
1313+
12921314
/// Ensures that an error is printed. See `Level::DelayedBug`.
12931315
//
12941316
// No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't

compiler/rustc_parse/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,14 @@ parse_unexpected_expr_in_pat =
801801
802802
.label = arbitrary expressions are not allowed in patterns
803803
804+
parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const`
805+
806+
parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard
807+
808+
parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`)
809+
810+
parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard
811+
804812
parse_unexpected_if_with_if = unexpected `if` in the condition expression
805813
.suggestion = remove the `if`
806814

compiler/rustc_parse/src/errors.rs

+77
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore-tidy-filelength
2+
13
use std::borrow::Cow;
24

35
use rustc_ast::token::Token;
@@ -2592,11 +2594,86 @@ pub(crate) struct ExpectedCommaAfterPatternField {
25922594
#[derive(Diagnostic)]
25932595
#[diag(parse_unexpected_expr_in_pat)]
25942596
pub(crate) struct UnexpectedExpressionInPattern {
2597+
// The unexpected expr's span.
25952598
#[primary_span]
25962599
#[label]
25972600
pub span: Span,
25982601
/// Was a `RangePatternBound` expected?
25992602
pub is_bound: bool,
2603+
/// The unexpected expr's precedence (used in match arm guard suggestions).
2604+
pub expr_precedence: i8,
2605+
}
2606+
2607+
#[derive(Subdiagnostic)]
2608+
pub(crate) enum UnexpectedExpressionInPatternSugg {
2609+
#[multipart_suggestion(
2610+
parse_unexpected_expr_in_pat_create_guard_sugg,
2611+
applicability = "maybe-incorrect"
2612+
)]
2613+
CreateGuard {
2614+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2615+
#[suggestion_part(code = "{ident}")]
2616+
ident_span: Span,
2617+
/// The end of the match arm's pattern.
2618+
#[suggestion_part(code = " if {ident} == {expr}")]
2619+
pat_hi: Span,
2620+
/// The suggested identifier.
2621+
ident: String,
2622+
/// `ident_span`'s snippet (with parentheses if needed).
2623+
expr: String,
2624+
},
2625+
2626+
#[multipart_suggestion(
2627+
parse_unexpected_expr_in_pat_update_guard_sugg,
2628+
applicability = "maybe-incorrect"
2629+
)]
2630+
UpdateGuard {
2631+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2632+
#[suggestion_part(code = "{ident}")]
2633+
ident_span: Span,
2634+
/// The beginning of the match arm guard's expression (insert a `(` if `Some`).
2635+
#[suggestion_part(code = "(")]
2636+
guard_lo: Option<Span>,
2637+
/// The end of the match arm guard's expression.
2638+
#[suggestion_part(code = "{guard_hi_paren} && {ident} == {expr}")]
2639+
guard_hi: Span,
2640+
/// Either `")"` or `""`.
2641+
guard_hi_paren: &'static str,
2642+
/// The suggested identifier.
2643+
ident: String,
2644+
/// `ident_span`'s snippet (with parentheses if needed).
2645+
expr: String,
2646+
},
2647+
2648+
#[multipart_suggestion(
2649+
parse_unexpected_expr_in_pat_const_sugg,
2650+
applicability = "has-placeholders"
2651+
)]
2652+
Const {
2653+
/// The beginning of statement's line.
2654+
#[suggestion_part(code = "{indentation}const {ident}: /* Type */ = {expr};\n")]
2655+
stmt_lo: Span,
2656+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2657+
#[suggestion_part(code = "{ident}")]
2658+
ident_span: Span,
2659+
/// The suggested identifier.
2660+
ident: String,
2661+
/// `ident_span`'s snippet.
2662+
expr: String,
2663+
/// The statement's block's indentation.
2664+
indentation: String,
2665+
},
2666+
2667+
#[multipart_suggestion(
2668+
parse_unexpected_expr_in_pat_inline_const_sugg,
2669+
applicability = "maybe-incorrect"
2670+
)]
2671+
InlineConst {
2672+
#[suggestion_part(code = "const {{ ")]
2673+
start_span: Span,
2674+
#[suggestion_part(code = " }}")]
2675+
end_span: Span,
2676+
},
26002677
}
26012678

26022679
#[derive(Diagnostic)]

0 commit comments

Comments
 (0)