@@ -6,21 +6,22 @@ use crate::errors::{
6
6
InclusiveRangeExtraEquals , InclusiveRangeMatchArrow , InclusiveRangeNoEnd , InvalidMutInPattern ,
7
7
PatternOnWrongSideOfAt , RemoveLet , RepeatedMutInPattern , SwitchRefBoxOrder ,
8
8
TopLevelOrPatternNotAllowed , TopLevelOrPatternNotAllowedSugg , TrailingVertNotAllowed ,
9
- UnexpectedExpressionInPattern , UnexpectedLifetimeInPattern , UnexpectedParenInRangePat ,
10
- UnexpectedParenInRangePatSugg , UnexpectedVertVertBeforeFunctionParam ,
11
- UnexpectedVertVertInPattern ,
9
+ UnexpectedExpressionInPattern , UnexpectedExpressionInPatternSugg , UnexpectedLifetimeInPattern ,
10
+ UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
11
+ UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern ,
12
12
} ;
13
13
use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat , LhsExpr } ;
14
14
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
15
15
use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
16
16
use rustc_ast:: ptr:: P ;
17
17
use rustc_ast:: token:: { self , BinOpToken , Delimiter , Token } ;
18
+ use rustc_ast:: visit:: { walk_arm, walk_pat, walk_pat_field, Visitor } ;
18
19
use rustc_ast:: {
19
- self as ast, AttrVec , BindingMode , ByRef , Expr , ExprKind , MacCall , Mutability , Pat , PatField ,
20
- PatFieldsRest , PatKind , Path , QSelf , RangeEnd , RangeSyntax ,
20
+ self as ast, Arm , AttrVec , BindingMode , ByRef , Expr , ExprKind , LocalKind , MacCall , Mutability ,
21
+ Pat , PatField , PatFieldsRest , PatKind , Path , QSelf , RangeEnd , RangeSyntax , Stmt , StmtKind ,
21
22
} ;
22
23
use rustc_ast_pretty:: pprust;
23
- use rustc_errors:: { Applicability , Diag , PResult } ;
24
+ use rustc_errors:: { Applicability , Diag , PResult , StashKey } ;
24
25
use rustc_session:: errors:: ExprParenthesesNeeded ;
25
26
use rustc_span:: source_map:: { respan, Spanned } ;
26
27
use rustc_span:: symbol:: { kw, sym, Ident } ;
@@ -431,12 +432,194 @@ impl<'a> Parser<'a> {
431
432
|| self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
432
433
&& self . look_ahead ( 1 , Token :: is_range_separator) ;
433
434
435
+ let span = expr. span ;
436
+
434
437
Some ( (
435
- self . dcx ( ) . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
436
- expr. span ,
438
+ self . dcx ( ) . stash_err (
439
+ span,
440
+ StashKey :: ExprInPat ,
441
+ UnexpectedExpressionInPattern { span, is_bound } ,
442
+ ) ,
443
+ span,
437
444
) )
438
445
}
439
446
447
+ /// Called by [`Parser::parse_stmt_without_recovery`], used to add statement-aware subdiagnostics to the errors stashed
448
+ /// by [`Parser::maybe_recover_trailing_expr`].
449
+ pub ( super ) fn maybe_emit_stashed_expr_in_pats ( & mut self , stmt : & Stmt ) {
450
+ if self . dcx ( ) . has_errors ( ) . is_none ( ) {
451
+ // No need to walk the statement if there's no stashed errors.
452
+ return ;
453
+ }
454
+
455
+ struct PatVisitor < ' a > {
456
+ /// `self`
457
+ parser : & ' a Parser < ' a > ,
458
+ /// The freshly-parsed statement.
459
+ stmt : & ' a Stmt ,
460
+ /// The current match arm (for arm guard suggestions).
461
+ arm : Option < & ' a Arm > ,
462
+ /// The current struct field (for variable name suggestions).
463
+ field : Option < & ' a PatField > ,
464
+ }
465
+
466
+ impl < ' a > PatVisitor < ' a > {
467
+ /// Looks for stashed [`StashKey::ExprInPat`] errors in `stash_span`, and emit them with suggestions.
468
+ /// `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
469
+ /// ```txt
470
+ /// &mut x.y
471
+ /// -----^^^ `stash_span`
472
+ /// |
473
+ /// `expr_span`
474
+ /// ```
475
+ fn emit_now ( & self , stash_span : Span , expr_span : Span ) {
476
+ self . parser . dcx ( ) . try_steal_modify_and_emit_err (
477
+ stash_span,
478
+ StashKey :: ExprInPat ,
479
+ |err| {
480
+ let sm = self . parser . psess . source_map ( ) ;
481
+ let stmt = self . stmt ;
482
+ let line_lo = sm. span_extend_to_line ( stmt. span ) . shrink_to_lo ( ) ;
483
+ let indentation = sm. indentation_before ( stmt. span ) . unwrap_or_default ( ) ;
484
+ let expr = self . parser . span_to_snippet ( expr_span) . unwrap ( ) ;
485
+
486
+ // Includes pre-pats (e.g. `&mut <err>`) in the diagnostic.
487
+ err. span . replace ( stash_span, expr_span) ;
488
+
489
+ if let StmtKind :: Let ( local) = & stmt. kind {
490
+ // help: remove the let
491
+
492
+ match & local. kind {
493
+ // help: remove the `let`
494
+ LocalKind :: Decl | LocalKind :: Init ( _) => {
495
+ err. subdiagnostic (
496
+ UnexpectedExpressionInPatternSugg :: RemoveLet {
497
+ let_span : local
498
+ . span
499
+ . shrink_to_lo ( )
500
+ . until ( local. pat . span ) ,
501
+ ty_span : local
502
+ . colon_sp
503
+ . map ( |sp| sp. to ( local. ty . as_ref ( ) . unwrap ( ) . span ) ) ,
504
+ has_initializer : !matches ! ( local. kind, LocalKind :: Decl ) ,
505
+ } ,
506
+ ) ;
507
+
508
+ // don't suggest `let const { pat } = expr;`
509
+ return ;
510
+ }
511
+
512
+ LocalKind :: InitElse ( _, _) => { }
513
+ }
514
+ }
515
+
516
+ // help: use an arm guard `if val == expr`
517
+ if let Some ( arm) = & self . arm {
518
+ let ( ident, ident_span) = match self . field {
519
+ Some ( field) => {
520
+ ( field. ident . to_string ( ) , field. ident . span . to ( expr_span) )
521
+ }
522
+ None => ( "val" . to_owned ( ) , expr_span) ,
523
+ } ;
524
+
525
+ match & arm. guard {
526
+ None => {
527
+ err. subdiagnostic (
528
+ UnexpectedExpressionInPatternSugg :: CreateGuard {
529
+ ident_span,
530
+ pat_hi : arm. pat . span . shrink_to_hi ( ) ,
531
+ ident,
532
+ expr : expr. clone ( ) ,
533
+ } ,
534
+ ) ;
535
+ }
536
+ Some ( guard) => {
537
+ err. subdiagnostic (
538
+ UnexpectedExpressionInPatternSugg :: UpdateGuard {
539
+ ident_span,
540
+ guard_lo : guard. span . shrink_to_lo ( ) ,
541
+ guard_hi : guard. span . shrink_to_hi ( ) ,
542
+ ident,
543
+ expr : expr. clone ( ) ,
544
+ } ,
545
+ ) ;
546
+ }
547
+ }
548
+ }
549
+
550
+ // help: extract the expr into a `const VAL: _ = expr`
551
+ let ident = match self . field {
552
+ Some ( field) => field. ident . as_str ( ) . to_uppercase ( ) ,
553
+ None => "VAL" . to_owned ( ) ,
554
+ } ;
555
+ err. subdiagnostic ( UnexpectedExpressionInPatternSugg :: Const {
556
+ stmt_lo : line_lo,
557
+ ident_span : expr_span,
558
+ expr,
559
+ ident,
560
+ indentation,
561
+ } ) ;
562
+
563
+ // help: wrap the expr in a `const { expr }`
564
+ // FIXME(inline_const_pat): once stabilized, remove this check and remove the `(requires #[feature(inline_const_pat)]` note from the message
565
+ if self . parser . psess . unstable_features . is_nightly_build ( ) {
566
+ err. subdiagnostic ( UnexpectedExpressionInPatternSugg :: InlineConst {
567
+ start_span : expr_span. shrink_to_lo ( ) ,
568
+ end_span : expr_span. shrink_to_hi ( ) ,
569
+ } ) ;
570
+ }
571
+ } ,
572
+ ) ;
573
+ }
574
+ }
575
+
576
+ impl < ' a > Visitor < ' a > for PatVisitor < ' a > {
577
+ fn visit_arm ( & mut self , a : & ' a Arm ) -> Self :: Result {
578
+ self . arm = Some ( a) ;
579
+ walk_arm ( self , a) ;
580
+ self . arm = None ;
581
+ }
582
+
583
+ fn visit_pat_field ( & mut self , fp : & ' a PatField ) -> Self :: Result {
584
+ self . field = Some ( fp) ;
585
+ walk_pat_field ( self , fp) ;
586
+ self . field = None ;
587
+ }
588
+
589
+ fn visit_pat ( & mut self , p : & ' a Pat ) -> Self :: Result {
590
+ match & p. kind {
591
+ // Base expression
592
+ PatKind :: Err ( _) => self . emit_now ( p. span , p. span ) ,
593
+
594
+ // Sub-patterns
595
+ // FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`)
596
+ PatKind :: Box ( subpat) | PatKind :: Ref ( subpat, _)
597
+ if matches ! ( subpat. kind, PatKind :: Err ( _) ) =>
598
+ {
599
+ self . emit_now ( subpat. span , p. span )
600
+ }
601
+
602
+ // Sub-expressions
603
+ PatKind :: Range ( start, end, _) => {
604
+ if let Some ( start) = start {
605
+ self . emit_now ( start. span , start. span ) ;
606
+ }
607
+
608
+ if let Some ( end) = end {
609
+ self . emit_now ( end. span , end. span ) ;
610
+ }
611
+ }
612
+
613
+ // Walk continuation
614
+ _ => walk_pat ( self , p) ,
615
+ }
616
+ }
617
+ }
618
+
619
+ // Starts the visit.
620
+ PatVisitor { parser : self , stmt, arm : None , field : None } . visit_stmt ( stmt) ;
621
+ }
622
+
440
623
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
441
624
/// allowed).
442
625
fn parse_pat_with_range_pat (
@@ -586,7 +769,11 @@ impl<'a> Parser<'a> {
586
769
587
770
match self . parse_range_end ( ) {
588
771
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
589
- None => PatKind :: Lit ( begin) ,
772
+ None => match & begin. kind {
773
+ // Avoid `PatKind::Lit(ExprKind::Err)`
774
+ ExprKind :: Err ( guar) => PatKind :: Err ( * guar) ,
775
+ _ => PatKind :: Lit ( begin) ,
776
+ } ,
590
777
}
591
778
}
592
779
Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
@@ -758,7 +945,25 @@ impl<'a> Parser<'a> {
758
945
759
946
Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
760
947
None => pat,
761
- Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
948
+ Some ( ( guar, _) ) => {
949
+ // We just recovered a bigger expression, so cancel its children
950
+ // (e.g. `(1 + 2) * 3`, cancel “`1 + 2` is not a pattern”).
951
+ match pat {
952
+ PatKind :: Paren ( pat) => {
953
+ self . dcx ( ) . steal_err ( pat. span , StashKey :: ExprInPat , guar) ;
954
+ }
955
+
956
+ PatKind :: Tuple ( fields) => {
957
+ for pat in fields {
958
+ self . dcx ( ) . steal_err ( pat. span , StashKey :: ExprInPat , guar) ;
959
+ }
960
+ }
961
+
962
+ _ => unreachable ! ( "{pat:?}" ) ,
963
+ }
964
+
965
+ PatKind :: Err ( guar)
966
+ }
762
967
} )
763
968
}
764
969
0 commit comments