@@ -6,21 +6,23 @@ 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 , UnexpectedExpressionInPatternArmSugg ,
10
+ UnexpectedExpressionInPatternConstSugg , UnexpectedExpressionInPatternInlineConstSugg ,
11
+ UnexpectedLifetimeInPattern , UnexpectedParenInRangePat , UnexpectedParenInRangePatSugg ,
12
+ UnexpectedVertVertBeforeFunctionParam , UnexpectedVertVertInPattern ,
12
13
} ;
13
14
use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat , LhsExpr } ;
14
15
use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
15
16
use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
16
17
use rustc_ast:: ptr:: P ;
17
18
use rustc_ast:: token:: { self , BinOpToken , Delimiter , Token } ;
19
+ use rustc_ast:: visit:: { walk_arm, walk_pat, walk_pat_field, Visitor } ;
18
20
use rustc_ast:: {
19
- self as ast, AttrVec , BindingMode , ByRef , Expr , ExprKind , MacCall , Mutability , Pat , PatField ,
20
- PatFieldsRest , PatKind , Path , QSelf , RangeEnd , RangeSyntax ,
21
+ self as ast, Arm , AttrVec , BindingMode , ByRef , Expr , ExprKind , LocalKind , MacCall , Mutability ,
22
+ Pat , PatField , PatFieldsRest , PatKind , Path , QSelf , RangeEnd , RangeSyntax , Stmt , StmtKind ,
21
23
} ;
22
24
use rustc_ast_pretty:: pprust;
23
- use rustc_errors:: { Applicability , Diag , PResult } ;
25
+ use rustc_errors:: { Applicability , Diag , PResult , StashKey } ;
24
26
use rustc_session:: errors:: ExprParenthesesNeeded ;
25
27
use rustc_span:: source_map:: { respan, Spanned } ;
26
28
use rustc_span:: symbol:: { kw, sym, Ident } ;
@@ -422,10 +424,15 @@ impl<'a> Parser<'a> {
422
424
|| self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
423
425
&& self . look_ahead ( 1 , Token :: is_range_separator) ;
424
426
427
+ let span = expr. span ;
428
+
425
429
return Some ( (
426
- self . dcx ( )
427
- . emit_err ( UnexpectedExpressionInPattern { span : expr. span , is_bound } ) ,
428
- expr. span ,
430
+ self . dcx ( ) . stash_err (
431
+ span,
432
+ StashKey :: ExprInPat ,
433
+ UnexpectedExpressionInPattern { span, is_bound } ,
434
+ ) ,
435
+ span,
429
436
) ) ;
430
437
}
431
438
}
@@ -434,6 +441,161 @@ impl<'a> Parser<'a> {
434
441
None
435
442
}
436
443
444
+ pub ( super ) fn maybe_emit_stashed_expr_in_pat ( & mut self , stmt : & Stmt ) {
445
+ if self . dcx ( ) . has_errors ( ) . is_none ( ) {
446
+ return ;
447
+ }
448
+
449
+ // WIP: once a fn body has been parsed, we walk through all its patterns,
450
+ // and emit now what errors `maybe_recover_trailing_expr()` stashed,
451
+ // with suggestions depending on which statement the pattern is.
452
+
453
+ struct PatVisitor < ' a > {
454
+ /// `self`
455
+ parser : & ' a Parser < ' a > ,
456
+ /// The current statement.
457
+ stmt : & ' a Stmt ,
458
+ /// The current match arm.
459
+ arm : Option < & ' a Arm > ,
460
+ /// The current struct field.
461
+ field : Option < & ' a PatField > ,
462
+ }
463
+
464
+ impl < ' a > Visitor < ' a > for PatVisitor < ' a > {
465
+ fn visit_arm ( & mut self , a : & ' a Arm ) -> Self :: Result {
466
+ self . arm = Some ( a) ;
467
+ walk_arm ( self , a) ;
468
+ self . arm = None ;
469
+ }
470
+
471
+ fn visit_pat_field ( & mut self , fp : & ' a PatField ) -> Self :: Result {
472
+ self . field = Some ( fp) ;
473
+ walk_pat_field ( self , fp) ;
474
+ self . field = None ;
475
+ }
476
+
477
+ fn visit_pat ( & mut self , p : & ' a Pat ) -> Self :: Result {
478
+ // Looks for stashed `ExprInPat` errors in `stash_span`, and emit them with suggestions.
479
+ // `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
480
+ // ```txt
481
+ // &mut x.y
482
+ // -----^^^ `stash_span`
483
+ // |
484
+ // `expr_span`
485
+ // ```
486
+ let emit_now = |that : & Self , stash_span : Span , expr_span : Span | -> Self :: Result {
487
+ that. parser . dcx ( ) . try_steal_modify_and_emit_err (
488
+ stash_span,
489
+ StashKey :: ExprInPat ,
490
+ |err| {
491
+ let sm = that. parser . psess . source_map ( ) ;
492
+ let stmt = that. stmt ;
493
+ let line_lo = sm. span_extend_to_line ( stmt. span ) . shrink_to_lo ( ) ;
494
+ let indentation = sm. indentation_before ( stmt. span ) . unwrap_or_default ( ) ;
495
+ let expr = that. parser . span_to_snippet ( expr_span) . unwrap ( ) ;
496
+
497
+ err. span . replace ( stash_span, expr_span) ;
498
+
499
+ if let StmtKind :: Let ( local) = & stmt. kind {
500
+ // If we have an `ExprInPat`, the user tried to assign a value to another value,
501
+ // which doesn't makes much sense.
502
+ match & local. kind {
503
+ LocalKind :: Decl => { }
504
+ LocalKind :: Init ( _) => { }
505
+ LocalKind :: InitElse ( _, _) => { }
506
+ }
507
+ } else {
508
+ // help: use an arm guard `if val == expr`
509
+ if let Some ( arm) = & self . arm {
510
+ let ( ident, ident_span) = match self . field {
511
+ Some ( field) => (
512
+ field. ident . to_string ( ) ,
513
+ field. ident . span . to ( expr_span) ,
514
+ ) ,
515
+ None => ( "val" . to_owned ( ) , expr_span) ,
516
+ } ;
517
+
518
+ match & arm. guard {
519
+ None => {
520
+ err. subdiagnostic (
521
+ UnexpectedExpressionInPatternArmSugg :: CreateGuard {
522
+ ident_span,
523
+ pat_hi : arm. pat . span . shrink_to_hi ( ) ,
524
+ ident,
525
+ expr : expr. clone ( ) ,
526
+ } ,
527
+ ) ;
528
+ }
529
+ Some ( guard) => {
530
+ err. subdiagnostic (
531
+ UnexpectedExpressionInPatternArmSugg :: UpdateGuard {
532
+ ident_span,
533
+ guard_lo : guard. span . shrink_to_lo ( ) ,
534
+ guard_hi : guard. span . shrink_to_hi ( ) ,
535
+ ident,
536
+ expr : expr. clone ( ) ,
537
+ } ,
538
+ ) ;
539
+ }
540
+ }
541
+ }
542
+
543
+ // help: extract the expr into a `const VAL: _ = expr`
544
+ let ident = match self . field {
545
+ Some ( field) => field. ident . as_str ( ) . to_uppercase ( ) ,
546
+ None => "VAL" . to_owned ( ) ,
547
+ } ;
548
+ err. subdiagnostic ( UnexpectedExpressionInPatternConstSugg {
549
+ stmt_lo : line_lo,
550
+ ident_span : expr_span,
551
+ expr,
552
+ ident,
553
+ indentation,
554
+ } ) ;
555
+
556
+ // help: wrap the expr in a `const { expr }`
557
+ // FIXME(inline_const_pat): once stabilized, remove this check and remove the `(requires #[feature(inline_const_pat)]` note from the message
558
+ if that. parser . psess . unstable_features . is_nightly_build ( ) {
559
+ err. subdiagnostic (
560
+ UnexpectedExpressionInPatternInlineConstSugg {
561
+ start_span : expr_span. shrink_to_lo ( ) ,
562
+ end_span : expr_span. shrink_to_hi ( ) ,
563
+ } ,
564
+ ) ;
565
+ }
566
+ }
567
+ } ,
568
+ ) ;
569
+ } ; // end of `emit_now` closure, we're back in `visit_pat`
570
+
571
+ match & p. kind {
572
+ // Base expression
573
+ PatKind :: Err ( _) => emit_now ( self , p. span , p. span ) ,
574
+ // Sub-patterns
575
+ PatKind :: Box ( subpat) | PatKind :: Ref ( subpat, _)
576
+ if matches ! ( subpat. kind, PatKind :: Err ( _) ) =>
577
+ {
578
+ emit_now ( self , subpat. span , p. span )
579
+ }
580
+ // Sub-expressions
581
+ PatKind :: Range ( start, end, _) => {
582
+ if let Some ( start) = start {
583
+ emit_now ( self , start. span , start. span ) ;
584
+ }
585
+
586
+ if let Some ( end) = end {
587
+ emit_now ( self , end. span , end. span ) ;
588
+ }
589
+ }
590
+ // Walk continuation
591
+ _ => walk_pat ( self , p) ,
592
+ }
593
+ }
594
+ } // end of `PatVisitor` impl, we're back in `maybe_emit_stashed_expr_in_pat`
595
+
596
+ PatVisitor { parser : self , stmt, arm : None , field : None } . visit_stmt ( stmt) ;
597
+ }
598
+
437
599
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
438
600
/// allowed).
439
601
fn parse_pat_with_range_pat (
@@ -583,7 +745,11 @@ impl<'a> Parser<'a> {
583
745
584
746
match self . parse_range_end ( ) {
585
747
Some ( form) => self . parse_pat_range_begin_with ( begin, form) ?,
586
- None => PatKind :: Lit ( begin) ,
748
+ None => match & begin. kind {
749
+ // Avoid `PatKind::Lit(ExprKind::Err)`
750
+ ExprKind :: Err ( guar) => PatKind :: Err ( * guar) ,
751
+ _ => PatKind :: Lit ( begin) ,
752
+ } ,
587
753
}
588
754
}
589
755
Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
@@ -755,7 +921,25 @@ impl<'a> Parser<'a> {
755
921
756
922
Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
757
923
None => pat,
758
- Some ( ( guar, _) ) => PatKind :: Err ( guar) ,
924
+ Some ( ( guar, _) ) => {
925
+ // We just recovered a bigger expression, so cancel its children
926
+ // (e.g. `(1 + 2) * 3`, cancel “`1 + 2` is not a pattern”).
927
+ match pat {
928
+ PatKind :: Paren ( pat) => {
929
+ self . dcx ( ) . steal_err ( pat. span , StashKey :: ExprInPat , guar) ;
930
+ }
931
+
932
+ PatKind :: Tuple ( fields) => {
933
+ for pat in fields {
934
+ self . dcx ( ) . steal_err ( pat. span , StashKey :: ExprInPat , guar) ;
935
+ }
936
+ }
937
+
938
+ _ => unreachable ! ( ) ,
939
+ }
940
+
941
+ PatKind :: Err ( guar)
942
+ }
759
943
} )
760
944
}
761
945
0 commit comments