@@ -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 , UnexpectedExpressionInPatternConstSugg } ;
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,7 +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 ;
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 } ;
17
20
use rustc_ast:: { self as ast} ;
18
21
use rustc_ast_pretty:: pprust;
19
22
use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , PResult , StashKey } ;
@@ -2365,9 +2368,19 @@ impl<'a> Parser<'a> {
2365
2368
} ;
2366
2369
2367
2370
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
+
2368
2375
struct PatVisitor < ' a > {
2376
+ /// `self`
2369
2377
parser : & ' a Parser < ' a > ,
2378
+ /// The current statement.
2370
2379
stmt : Option < & ' a Stmt > ,
2380
+ /// The current match arm.
2381
+ arm : Option < & ' a Arm > ,
2382
+ /// The current struct field.
2383
+ field : Option < & ' a PatField > ,
2371
2384
}
2372
2385
2373
2386
impl < ' a > Visitor < ' a > for PatVisitor < ' a > {
@@ -2377,36 +2390,140 @@ impl<'a> Parser<'a> {
2377
2390
walk_stmt ( self , s)
2378
2391
}
2379
2392
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
+ }
2404
2398
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 ;
2406
2403
}
2407
- }
2408
2404
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) ;
2410
2527
}
2411
2528
2412
2529
attrs. extend ( inner_attrs) ;
0 commit comments