@@ -6,21 +6,23 @@ use crate::errors::{
66 InclusiveRangeExtraEquals , InclusiveRangeMatchArrow , InclusiveRangeNoEnd , InvalidMutInPattern ,
77 PatternOnWrongSideOfAt , RemoveLet , RepeatedMutInPattern , SwitchRefBoxOrder ,
88 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 ,
1213} ;
1314use crate :: parser:: expr:: { could_be_unclosed_char_literal, DestructuredFloat , LhsExpr } ;
1415use crate :: { maybe_recover_from_interpolated_ty_qpath, maybe_whole} ;
1516use rustc_ast:: mut_visit:: { noop_visit_pat, MutVisitor } ;
1617use rustc_ast:: ptr:: P ;
1718use rustc_ast:: token:: { self , BinOpToken , Delimiter , Token } ;
19+ use rustc_ast:: visit:: { walk_arm, walk_pat, walk_pat_field, Visitor } ;
1820use 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 ,
2123} ;
2224use rustc_ast_pretty:: pprust;
23- use rustc_errors:: { Applicability , Diag , PResult } ;
25+ use rustc_errors:: { Applicability , Diag , PResult , StashKey } ;
2426use rustc_session:: errors:: ExprParenthesesNeeded ;
2527use rustc_span:: source_map:: { respan, Spanned } ;
2628use rustc_span:: symbol:: { kw, sym, Ident } ;
@@ -422,10 +424,15 @@ impl<'a> Parser<'a> {
422424 || self . token . kind == token:: CloseDelim ( Delimiter :: Parenthesis )
423425 && self . look_ahead ( 1 , Token :: is_range_separator) ;
424426
427+ let span = expr. span ;
428+
425429 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,
429436 ) ) ;
430437 }
431438 }
@@ -434,6 +441,161 @@ impl<'a> Parser<'a> {
434441 None
435442 }
436443
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+
437599 /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
438600 /// allowed).
439601 fn parse_pat_with_range_pat (
@@ -583,7 +745,11 @@ impl<'a> Parser<'a> {
583745
584746 match self . parse_range_end ( ) {
585747 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+ } ,
587753 }
588754 }
589755 Err ( err) => return self . fatal_unexpected_non_pat ( err, expected) ,
@@ -755,7 +921,25 @@ impl<'a> Parser<'a> {
755921
756922 Ok ( match self . maybe_recover_trailing_expr ( open_paren. to ( self . prev_token . span ) , false ) {
757923 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+ }
759943 } )
760944 }
761945
0 commit comments