@@ -4,7 +4,10 @@ use super::{
4
4
AttrWrapper , FollowedByType , ForceCollect , Parser , PathStyle , Recovered , Trailing ,
5
5
TrailingToken ,
6
6
} ;
7
- use crate :: errors:: { self , MacroExpandsToAdtField } ;
7
+ use crate :: errors:: {
8
+ self , MacroExpandsToAdtField , UnexpectedExpressionInPatternArmSugg ,
9
+ UnexpectedExpressionInPatternConstSugg , UnexpectedExpressionInPatternInlineConstSugg ,
10
+ } ;
8
11
use crate :: fluent_generated as fluent;
9
12
use crate :: maybe_whole;
10
13
use ast:: token:: IdentIsRaw ;
@@ -13,6 +16,7 @@ use rustc_ast::ptr::P;
13
16
use rustc_ast:: token:: { self , Delimiter , TokenKind } ;
14
17
use rustc_ast:: tokenstream:: { DelimSpan , TokenStream , TokenTree } ;
15
18
use rustc_ast:: util:: case:: Case ;
19
+ use rustc_ast:: visit:: { walk_arm, walk_pat, walk_pat_field, walk_stmt, Visitor } ;
16
20
use rustc_ast:: { self as ast} ;
17
21
use rustc_ast_pretty:: pprust;
18
22
use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , PResult , StashKey } ;
@@ -2362,6 +2366,168 @@ impl<'a> Parser<'a> {
2362
2366
}
2363
2367
( AttrVec :: new ( ) , None )
2364
2368
} ;
2369
+
2370
+ if let Some ( body) = & body
2371
+ && self . dcx ( ) . err_count ( ) > 0
2372
+ {
2373
+ // WIP: once a fn body has been parsed, we walk through all its patterns,
2374
+ // and emit now what errors `maybe_recover_trailing_expr()` stashed,
2375
+ // with suggestions depending on which statement the pattern is.
2376
+
2377
+ struct PatVisitor < ' a > {
2378
+ /// `self`
2379
+ parser : & ' a Parser < ' a > ,
2380
+ /// The current statement.
2381
+ stmt : Option < & ' a Stmt > ,
2382
+ /// The current match arm.
2383
+ arm : Option < & ' a Arm > ,
2384
+ /// The current struct field.
2385
+ field : Option < & ' a PatField > ,
2386
+ }
2387
+
2388
+ impl < ' a > Visitor < ' a > for PatVisitor < ' a > {
2389
+ fn visit_stmt ( & mut self , s : & ' a Stmt ) -> Self :: Result {
2390
+ self . stmt = Some ( s) ;
2391
+
2392
+ walk_stmt ( self , s)
2393
+ }
2394
+
2395
+ fn visit_arm ( & mut self , a : & ' a Arm ) -> Self :: Result {
2396
+ self . arm = Some ( a) ;
2397
+ walk_arm ( self , a) ;
2398
+ self . arm = None ;
2399
+ }
2400
+
2401
+ fn visit_pat_field ( & mut self , fp : & ' a PatField ) -> Self :: Result {
2402
+ self . field = Some ( fp) ;
2403
+ walk_pat_field ( self , fp) ;
2404
+ self . field = None ;
2405
+ }
2406
+
2407
+ fn visit_pat ( & mut self , p : & ' a Pat ) -> Self :: Result {
2408
+ // Looks for stashed `ExprInPat` errors in `stash_span`, and emit them with suggestions.
2409
+ // `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
2410
+ // ```txt
2411
+ // &mut x.y
2412
+ // -----^^^ `stash_span`
2413
+ // |
2414
+ // `expr_span`
2415
+ // ```
2416
+ let emit_now = |that : & Self ,
2417
+ stash_span : Span ,
2418
+ expr_span : Span |
2419
+ -> Self :: Result {
2420
+ that. parser . dcx ( ) . try_steal_modify_and_emit_err (
2421
+ stash_span,
2422
+ StashKey :: ExprInPat ,
2423
+ |err| {
2424
+ let sm = that. parser . psess . source_map ( ) ;
2425
+ let stmt = that. stmt . unwrap ( ) ;
2426
+ let line_lo = sm. span_extend_to_line ( stmt. span ) . shrink_to_lo ( ) ;
2427
+ let indentation =
2428
+ sm. indentation_before ( stmt. span ) . unwrap_or_default ( ) ;
2429
+ let expr = that. parser . span_to_snippet ( expr_span) . unwrap ( ) ;
2430
+
2431
+ err. span . replace ( stash_span, expr_span) ;
2432
+
2433
+ if let StmtKind :: Let ( local) = & stmt. kind {
2434
+ // If we have an `ExprInPat`, the user tried to assign a value to another value,
2435
+ // which doesn't makes much sense.
2436
+ match & local. kind {
2437
+ LocalKind :: Decl => { }
2438
+ LocalKind :: Init ( _) => { }
2439
+ LocalKind :: InitElse ( _, _) => { }
2440
+ }
2441
+ }
2442
+ else {
2443
+ // help: use an arm guard `if val == expr`
2444
+ if let Some ( arm) = & self . arm {
2445
+ let ( ident, ident_span) = match self . field {
2446
+ Some ( field) => ( field. ident . to_string ( ) , field. ident . span . to ( expr_span) ) ,
2447
+ None => ( "val" . to_owned ( ) , expr_span) ,
2448
+ } ;
2449
+
2450
+ match & arm. guard {
2451
+ None => {
2452
+ err. subdiagnostic ( & that. parser . dcx ( ) , UnexpectedExpressionInPatternArmSugg :: CreateGuard {
2453
+ ident_span,
2454
+ pat_hi : arm. pat . span . shrink_to_hi ( ) ,
2455
+ ident,
2456
+ expr : expr. clone ( ) ,
2457
+ } ) ;
2458
+ }
2459
+ Some ( guard) => {
2460
+ err. subdiagnostic ( & that. parser . dcx ( ) , UnexpectedExpressionInPatternArmSugg :: UpdateGuard {
2461
+ ident_span,
2462
+ guard_lo : guard. span . shrink_to_lo ( ) ,
2463
+ guard_hi : guard. span . shrink_to_hi ( ) ,
2464
+ ident,
2465
+ expr : expr. clone ( ) ,
2466
+ } ) ;
2467
+ }
2468
+ }
2469
+ }
2470
+
2471
+ // help: extract the expr into a `const VAL: _ = expr`
2472
+ let ident = match self . field {
2473
+ Some ( field) => field. ident . as_str ( ) . to_uppercase ( ) ,
2474
+ None => "VAL" . to_owned ( ) ,
2475
+ } ;
2476
+ err. subdiagnostic (
2477
+ & that. parser . dcx ( ) ,
2478
+ UnexpectedExpressionInPatternConstSugg {
2479
+ stmt_lo : line_lo,
2480
+ ident_span : expr_span,
2481
+ expr,
2482
+ ident,
2483
+ indentation,
2484
+ } ,
2485
+ ) ;
2486
+
2487
+ // help: wrap the expr in a `const { expr }`
2488
+ // FIXME(inline_const): once stabilized, remove this check and remove the `(requires #[feature(inline_const])` note from the message
2489
+ if that. parser . psess . unstable_features . is_nightly_build ( ) {
2490
+ err. subdiagnostic (
2491
+ & that. parser . dcx ( ) ,
2492
+ UnexpectedExpressionInPatternInlineConstSugg {
2493
+ start_span : expr_span. shrink_to_lo ( ) ,
2494
+ end_span : expr_span. shrink_to_hi ( ) ,
2495
+ } ,
2496
+ ) ;
2497
+ }
2498
+ }
2499
+ } ,
2500
+ ) ;
2501
+ } ; // end of `emit_now` closure, we're back in `visit_pat`
2502
+
2503
+ match & p. kind {
2504
+ // Base expression
2505
+ PatKind :: Err ( _) => emit_now ( self , p. span , p. span ) ,
2506
+ // Sub-patterns
2507
+ PatKind :: Box ( subpat) | PatKind :: Ref ( subpat, _)
2508
+ if matches ! ( subpat. kind, PatKind :: Err ( _) ) =>
2509
+ {
2510
+ emit_now ( self , subpat. span , p. span )
2511
+ }
2512
+ // Sub-expressions
2513
+ PatKind :: Range ( start, end, _) => {
2514
+ if let Some ( start) = start {
2515
+ emit_now ( self , start. span , start. span ) ;
2516
+ }
2517
+
2518
+ if let Some ( end) = end {
2519
+ emit_now ( self , end. span , end. span ) ;
2520
+ }
2521
+ }
2522
+ // Walk continuation
2523
+ _ => walk_pat ( self , p) ,
2524
+ }
2525
+ }
2526
+ } // end of `PatVisitor` impl, we're back in `parse_fn_body`
2527
+
2528
+ PatVisitor { parser : self , stmt : None , arm : None , field : None } . visit_block ( body) ;
2529
+ }
2530
+
2365
2531
attrs. extend ( inner_attrs) ;
2366
2532
Ok ( body)
2367
2533
}
0 commit comments