@@ -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+ } ;
811use crate :: fluent_generated as fluent;
912use crate :: maybe_whole;
1013use ast:: token:: IdentIsRaw ;
@@ -13,7 +16,7 @@ use rustc_ast::ptr::P;
1316use rustc_ast:: token:: { self , Delimiter , TokenKind } ;
1417use rustc_ast:: tokenstream:: { DelimSpan , TokenStream , TokenTree } ;
1518use 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 } ;
1720use rustc_ast:: { self as ast} ;
1821use rustc_ast_pretty:: pprust;
1922use 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) ;
0 commit comments