Skip to content

Commit 89e5eaf

Browse files
committed
Add match arm suggestions
1 parent bf0a1a6 commit 89e5eaf

17 files changed

+572
-271
lines changed

compiler/rustc_parse/messages.ftl

+6-2
Original file line numberDiff line numberDiff line change
@@ -780,10 +780,14 @@ parse_unexpected_expr_in_pat =
780780
781781
.label = arbitrary expressions are not allowed in patterns
782782
783-
parse_unexpected_expr_in_pat_const_pat_sugg = wrap the expression in a inline const (requires `{"#"}![feature(inline_const)]`)
784-
785783
parse_unexpected_expr_in_pat_const_sugg = extract the expression into a `const` and refer to it
786784
785+
parse_unexpected_expr_in_pat_create_guard_sugg = check the value in an arm guard
786+
787+
parse_unexpected_expr_in_pat_inline_const_sugg = wrap the expression in a inline const (requires `{"#"}![feature(inline_const)]`)
788+
789+
parse_unexpected_expr_in_pat_update_guard_sugg = check the value in the arm guard
790+
787791
parse_unexpected_if_with_if = unexpected `if` in the condition expression
788792
.suggestion = remove the `if`
789793

compiler/rustc_parse/src/errors.rs

+58-13
Original file line numberDiff line numberDiff line change
@@ -2442,28 +2442,73 @@ pub(crate) struct UnexpectedExpressionInPattern {
24422442
}
24432443

24442444
#[derive(Subdiagnostic)]
2445-
#[multipart_suggestion(
2446-
parse_unexpected_expr_in_pat_const_pat_sugg,
2447-
applicability = "maybe-incorrect"
2448-
)]
2449-
pub(crate) struct UnexpectedExpressionInPatternConstPatSugg {
2450-
#[suggestion_part(code = "const {{ ")]
2451-
pub start_span: Span,
2452-
#[suggestion_part(code = " }}")]
2453-
pub end_span: Span,
2445+
pub(crate) enum UnexpectedExpressionInPatternArmSugg {
2446+
#[multipart_suggestion(
2447+
parse_unexpected_expr_in_pat_create_guard_sugg,
2448+
applicability = "maybe-incorrect"
2449+
)]
2450+
CreateGuard {
2451+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2452+
#[suggestion_part(code = "{ident}")]
2453+
ident_span: Span,
2454+
/// The end of the match arm's pattern.
2455+
#[suggestion_part(code = " if {ident} == {expr}")]
2456+
pat_hi: Span,
2457+
/// The suggested identifier.
2458+
ident: String,
2459+
/// `ident_span`'s snippet.
2460+
expr: String,
2461+
},
2462+
#[multipart_suggestion(
2463+
parse_unexpected_expr_in_pat_update_guard_sugg,
2464+
applicability = "maybe-incorrect"
2465+
)]
2466+
UpdateGuard {
2467+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2468+
#[suggestion_part(code = "{ident}")]
2469+
ident_span: Span,
2470+
/// The beginning of the match arm guard's expression.
2471+
#[suggestion_part(code = "(")]
2472+
guard_lo: Span,
2473+
/// The end of the match arm guard's expression.
2474+
#[suggestion_part(code = ") && {ident} == {expr}")]
2475+
guard_hi: Span,
2476+
/// The suggested identifier.
2477+
ident: String,
2478+
/// `ident_span`'s snippet.
2479+
expr: String,
2480+
},
24542481
}
24552482

24562483
#[derive(Subdiagnostic)]
24572484
#[multipart_suggestion(parse_unexpected_expr_in_pat_const_sugg, applicability = "has-placeholders")]
24582485
pub(crate) struct UnexpectedExpressionInPatternConstSugg {
2459-
#[suggestion_part(code = "{indentation}const VAL: _ = {expr};\n")]
2460-
pub const_span: Span,
2461-
#[suggestion_part(code = "VAL")]
2462-
pub pat_span: Span,
2486+
/// The beginning of statement's line.
2487+
#[suggestion_part(code = "{indentation}const {ident}: _ = {expr};\n")]
2488+
pub stmt_lo: Span,
2489+
/// The span of the `PatKind:Err` to be transformed into a `PatKind::Ident`.
2490+
#[suggestion_part(code = "{ident}")]
2491+
pub ident_span: Span,
2492+
/// The suggested identifier.
2493+
pub ident: String,
2494+
/// `ident_span`'s snippet.
24632495
pub expr: String,
2496+
/// The statement's block's indentation.
24642497
pub indentation: String,
24652498
}
24662499

2500+
#[derive(Subdiagnostic)]
2501+
#[multipart_suggestion(
2502+
parse_unexpected_expr_in_pat_inline_const_sugg,
2503+
applicability = "maybe-incorrect"
2504+
)]
2505+
pub(crate) struct UnexpectedExpressionInPatternInlineConstSugg {
2506+
#[suggestion_part(code = "const {{ ")]
2507+
pub start_span: Span,
2508+
#[suggestion_part(code = " }}")]
2509+
pub end_span: Span,
2510+
}
2511+
24672512
#[derive(Diagnostic)]
24682513
#[diag(parse_unexpected_paren_in_range_pat)]
24692514
pub(crate) struct UnexpectedParenInRangePat {

compiler/rustc_parse/src/parser/item.rs

+146-29
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use super::{
44
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Recovered, Trailing,
55
TrailingToken,
66
};
7-
use crate::errors::{self, MacroExpandsToAdtField, UnexpectedExpressionInPatternConstSugg};
7+
use crate::errors::{
8+
self, MacroExpandsToAdtField, UnexpectedExpressionInPatternArmSugg,
9+
UnexpectedExpressionInPatternConstSugg, UnexpectedExpressionInPatternInlineConstSugg,
10+
};
811
use crate::fluent_generated as fluent;
912
use crate::maybe_whole;
1013
use ast::token::IdentIsRaw;
@@ -13,7 +16,7 @@ use rustc_ast::ptr::P;
1316
use rustc_ast::token::{self, Delimiter, TokenKind};
1417
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
1518
use rustc_ast::util::case::Case;
16-
use rustc_ast::visit::{walk_pat, walk_stmt, Visitor};
19+
use rustc_ast::visit::{walk_arm, walk_pat, walk_pat_field, walk_stmt, Visitor};
1720
use rustc_ast::{self as ast};
1821
use rustc_ast_pretty::pprust;
1922
use rustc_errors::{codes::*, struct_span_code_err, Applicability, PResult, StashKey};
@@ -2365,9 +2368,19 @@ impl<'a> Parser<'a> {
23652368
};
23662369

23672370
if let Some(body) = &body {
2371+
// WIP: once a fn body has been parsed, we walk through all its patterns,
2372+
// and emit now what errors `maybe_recover_trailing_expr()` stashed,
2373+
// with suggestions depending on which statement the pattern is.
2374+
23682375
struct PatVisitor<'a> {
2376+
/// `self`
23692377
parser: &'a Parser<'a>,
2378+
/// The current statement.
23702379
stmt: Option<&'a Stmt>,
2380+
/// The current match arm.
2381+
arm: Option<&'a Arm>,
2382+
/// The current struct field.
2383+
field: Option<&'a PatField>,
23712384
}
23722385

23732386
impl<'a> Visitor<'a> for PatVisitor<'a> {
@@ -2377,36 +2390,140 @@ impl<'a> Parser<'a> {
23772390
walk_stmt(self, s)
23782391
}
23792392

2380-
fn visit_pat(&mut self, pat: &'a Pat) -> Self::Result {
2381-
if matches!(pat.kind, PatKind::Err(_)) {
2382-
let sm = self.parser.psess.source_map();
2383-
2384-
self.parser.dcx().try_steal_modify_and_emit_err(
2385-
pat.span,
2386-
StashKey::ExprInPat,
2387-
|err| {
2388-
err.subdiagnostic(
2389-
&self.parser.dcx(),
2390-
UnexpectedExpressionInPatternConstSugg {
2391-
const_span: sm
2392-
.span_extend_to_line(self.stmt.unwrap().span)
2393-
.shrink_to_lo(),
2394-
pat_span: pat.span,
2395-
expr: self.parser.span_to_snippet(pat.span).unwrap(),
2396-
indentation: sm
2397-
.indentation_before(self.stmt.unwrap().span)
2398-
.unwrap_or_default(),
2399-
},
2400-
);
2401-
},
2402-
);
2403-
}
2393+
fn visit_arm(&mut self, a: &'a Arm) -> Self::Result {
2394+
self.arm = Some(a);
2395+
walk_arm(self, a);
2396+
self.arm = None;
2397+
}
24042398

2405-
walk_pat(self, pat)
2399+
fn visit_pat_field(&mut self, fp: &'a PatField) -> Self::Result {
2400+
self.field = Some(fp);
2401+
walk_pat_field(self, fp);
2402+
self.field = None;
24062403
}
2407-
}
24082404

2409-
PatVisitor { parser: self, stmt: None }.visit_block(body);
2405+
fn visit_pat(&mut self, p: &'a Pat) -> Self::Result {
2406+
// Looks for stashed `ExprInPat` errors in `stash_span`, and emit them with suggestions.
2407+
// `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
2408+
// ```txt
2409+
// &mut x.y
2410+
// -----^^^ `stash_span`
2411+
// |
2412+
// `expr_span`
2413+
// ```
2414+
let emit_now = |that: &Self,
2415+
stash_span: Span,
2416+
expr_span: Span|
2417+
-> Self::Result {
2418+
that.parser.dcx().try_steal_modify_and_emit_err(
2419+
stash_span,
2420+
StashKey::ExprInPat,
2421+
|err| {
2422+
let sm = that.parser.psess.source_map();
2423+
let stmt = that.stmt.unwrap();
2424+
let line_lo = sm.span_extend_to_line(stmt.span).shrink_to_lo();
2425+
let indentation =
2426+
sm.indentation_before(stmt.span).unwrap_or_default();
2427+
let expr = that.parser.span_to_snippet(expr_span).unwrap();
2428+
2429+
err.span.replace(stash_span, expr_span);
2430+
2431+
if let StmtKind::Let(local) = &stmt.kind {
2432+
// If we have an `ExprInPat`, the user tried to assign a value to another value,
2433+
// which doesn't makes much sense.
2434+
match &local.kind {
2435+
LocalKind::Decl => {}
2436+
LocalKind::Init(_) => {}
2437+
LocalKind::InitElse(_, _) => {}
2438+
}
2439+
}
2440+
else {
2441+
// help: use an arm guard `if val == expr`
2442+
if let Some(arm) = &self.arm {
2443+
let (ident, ident_span) = match self.field {
2444+
Some(field) => (field.ident.to_string(), field.ident.span.to(expr_span)),
2445+
None => ("val".to_owned(), expr_span),
2446+
};
2447+
2448+
match &arm.guard {
2449+
None => {
2450+
err.subdiagnostic(&that.parser.dcx(), UnexpectedExpressionInPatternArmSugg::CreateGuard {
2451+
ident_span,
2452+
pat_hi: arm.pat.span.shrink_to_hi(),
2453+
ident,
2454+
expr: expr.clone(),
2455+
});
2456+
}
2457+
Some(guard) => {
2458+
err.subdiagnostic(&that.parser.dcx(), UnexpectedExpressionInPatternArmSugg::UpdateGuard {
2459+
ident_span,
2460+
guard_lo: guard.span.shrink_to_lo(),
2461+
guard_hi: guard.span.shrink_to_hi(),
2462+
ident,
2463+
expr: expr.clone(),
2464+
});
2465+
}
2466+
}
2467+
}
2468+
2469+
// help: extract the expr into a `const VAL: _ = expr`
2470+
let ident = match self.field {
2471+
Some(field) => field.ident.as_str().to_uppercase(),
2472+
None => "VAL".to_owned(),
2473+
};
2474+
err.subdiagnostic(
2475+
&that.parser.dcx(),
2476+
UnexpectedExpressionInPatternConstSugg {
2477+
stmt_lo: line_lo,
2478+
ident_span: expr_span,
2479+
expr,
2480+
ident,
2481+
indentation,
2482+
},
2483+
);
2484+
2485+
// help: wrap the expr in a `const { expr }`
2486+
// FIXME(inline_const): once stabilized, remove this check and remove the `(requires #[feature(inline_const])` note from the message
2487+
if that.parser.psess.unstable_features.is_nightly_build() {
2488+
err.subdiagnostic(
2489+
&that.parser.dcx(),
2490+
UnexpectedExpressionInPatternInlineConstSugg {
2491+
start_span: expr_span.shrink_to_lo(),
2492+
end_span: expr_span.shrink_to_hi(),
2493+
},
2494+
);
2495+
}
2496+
}
2497+
},
2498+
);
2499+
}; // end of `emit_now` closure, we're back in `visit_pat`
2500+
2501+
match &p.kind {
2502+
// Base expression
2503+
PatKind::Err(_) => emit_now(self, p.span, p.span),
2504+
// Sub-patterns
2505+
PatKind::Box(subpat) | PatKind::Ref(subpat, _)
2506+
if matches!(subpat.kind, PatKind::Err(_)) =>
2507+
{
2508+
emit_now(self, subpat.span, p.span)
2509+
}
2510+
// Sub-expressions
2511+
PatKind::Range(start, end, _) => {
2512+
if let Some(start) = start {
2513+
emit_now(self, start.span, start.span);
2514+
}
2515+
2516+
if let Some(end) = end {
2517+
emit_now(self, end.span, end.span);
2518+
}
2519+
}
2520+
// Walk continuation
2521+
_ => walk_pat(self, p),
2522+
}
2523+
}
2524+
} // end of `PatVisitor` impl, we're back in `parse_fn_body`
2525+
2526+
PatVisitor { parser: self, stmt: None, arm: None, field: None }.visit_block(body);
24102527
}
24112528

24122529
attrs.extend(inner_attrs);

compiler/rustc_parse/src/parser/pat.rs

+17-28
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use crate::errors::{
66
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
77
PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder,
88
TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
9-
UnexpectedExpressionInPattern, UnexpectedExpressionInPatternConstPatSugg,
10-
UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
11-
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern,
9+
UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
10+
UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
11+
UnexpectedVertVertInPattern,
1212
};
1313
use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat};
1414
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@@ -424,19 +424,7 @@ impl<'a> Parser<'a> {
424424

425425
let span = expr.span;
426426

427-
let mut err =
428-
self.dcx().create_err(UnexpectedExpressionInPattern { span, is_bound });
429-
430-
// FIXME(inline_const): once stabilized, remove this check and remove the `(requires #[feature(inline_const])` note from the message
431-
if self.psess.unstable_features.is_nightly_build() {
432-
err.subdiagnostic(
433-
&self.dcx(),
434-
UnexpectedExpressionInPatternConstPatSugg {
435-
start_span: span.shrink_to_lo(),
436-
end_span: span.shrink_to_hi(),
437-
},
438-
);
439-
}
427+
let err = self.dcx().create_err(UnexpectedExpressionInPattern { span, is_bound });
440428

441429
return Some((err.stash(span, StashKey::ExprInPat).unwrap(), span));
442430
}
@@ -587,19 +575,20 @@ impl<'a> Parser<'a> {
587575
} else {
588576
// Try to parse everything else as literal with optional minus
589577
match self.parse_literal_maybe_minus() {
590-
Ok(mut begin) => {
591-
let guar =
592-
self.maybe_recover_trailing_expr(begin.span, false).map(|(guar, sp)| {
593-
begin = self.mk_expr_err(sp, guar);
594-
guar
595-
});
596-
597-
let pat = match self.parse_range_end() {
598-
Some(form) => self.parse_pat_range_begin_with(begin, form)?,
599-
None => PatKind::Lit(begin),
600-
};
578+
Ok(begin) => {
579+
let begin = self
580+
.maybe_recover_trailing_expr(begin.span, false)
581+
.map(|(guar, sp)| self.mk_expr_err(sp, guar))
582+
.unwrap_or(begin);
601583

602-
if let Some(guar) = guar { PatKind::Err(guar) } else { pat }
584+
match self.parse_range_end() {
585+
Some(form) => self.parse_pat_range_begin_with(begin, form)?,
586+
None => match &begin.kind {
587+
// Avoid `PatKind::Lit(ExprKind::Err)`
588+
ExprKind::Err(guar) => PatKind::Err(*guar),
589+
_ => PatKind::Lit(begin),
590+
},
591+
}
603592
}
604593
Err(err) => return self.fatal_unexpected_non_pat(err, expected),
605594
}

0 commit comments

Comments
 (0)