@@ -480,11 +480,83 @@ pub struct LocalDecl<'tcx> {
480
480
/// Source info of the local.
481
481
pub source_info : SourceInfo ,
482
482
483
- /// The *lexical * visibility scope the local is defined
483
+ /// The *syntactic * visibility scope the local is defined
484
484
/// in. If the local was defined in a let-statement, this
485
485
/// is *within* the let-statement, rather than outside
486
486
/// of it.
487
- pub lexical_scope : VisibilityScope ,
487
+ ///
488
+ /// This is needed because visibility scope of locals within a let-statement
489
+ /// is weird.
490
+ ///
491
+ /// The reason is that we want the local to be *within* the let-statement
492
+ /// for lint purposes, but we want the local to be *after* the let-statement
493
+ /// for names-in-scope purposes.
494
+ ///
495
+ /// That's it, if we have a let-statement like the one in this
496
+ /// function:
497
+ /// ```
498
+ /// fn foo(x: &str) {
499
+ /// #[allow(unused_mut)]
500
+ /// let mut x: u32 = { // <- one unused mut
501
+ /// let mut y: u32 = x.parse().unwrap();
502
+ /// y + 2
503
+ /// };
504
+ /// drop(x);
505
+ /// }
506
+ /// ```
507
+ ///
508
+ /// Then, from a lint point of view, the declaration of `x: u32`
509
+ /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the
510
+ /// lint scopes are the same as the AST/HIR nesting.
511
+ ///
512
+ /// However, from a name lookup point of view, the scopes look more like
513
+ /// as if the let-statements were `match` expressions:
514
+ ///
515
+ /// ```
516
+ /// fn foo(x: &str) {
517
+ /// match {
518
+ /// match x.parse().unwrap() {
519
+ /// y => y + 2
520
+ /// }
521
+ /// } {
522
+ /// x => drop(x)
523
+ /// };
524
+ /// }
525
+ /// ```
526
+ ///
527
+ /// We care about the name-lookup scopes for debuginfo - if the
528
+ /// debuginfo instruction pointer is at the call to `x.parse()`, we
529
+ /// want `x` to refer to `x: &str`, but if it is at the call to
530
+ /// `drop(x)`, we want it to refer to `x: u32`.
531
+ ///
532
+ /// To allow both uses to work, we need to have more than a single scope
533
+ /// for a local. We have the `syntactic_scope` represent the
534
+ /// "syntactic" lint scope (with a variable being under its let
535
+ /// block) while the source-info scope represents the "local variable"
536
+ /// scope (where the "rest" of a block is under all prior let-statements).
537
+ ///
538
+ /// The end result looks like this:
539
+ ///
540
+ /// ROOT SCOPE
541
+ /// │{ argument x: &str }
542
+ /// │
543
+ /// │ │{ #[allow(unused_mut] } // this is actually split into 2 scopes
544
+ /// │ │ // in practice because I'm lazy.
545
+ /// │ │
546
+ /// │ │← x.syntactic_scope
547
+ /// │ │← `x.parse().unwrap()`
548
+ /// │ │
549
+ /// │ │ │← y.syntactic_scope
550
+ /// │ │
551
+ /// │ │ │{ let y: u32 }
552
+ /// │ │ │
553
+ /// │ │ │← y.source_info.scope
554
+ /// │ │ │← `y + 2`
555
+ /// │
556
+ /// │ │{ let x: u32 }
557
+ /// │ │← x.source_info.scope
558
+ /// │ │← `drop(x)` // this accesses `x: u32`
559
+ pub syntactic_scope : VisibilityScope ,
488
560
}
489
561
490
562
impl < ' tcx > LocalDecl < ' tcx > {
@@ -499,7 +571,7 @@ impl<'tcx> LocalDecl<'tcx> {
499
571
span,
500
572
scope : ARGUMENT_VISIBILITY_SCOPE
501
573
} ,
502
- lexical_scope : ARGUMENT_VISIBILITY_SCOPE ,
574
+ syntactic_scope : ARGUMENT_VISIBILITY_SCOPE ,
503
575
internal : false ,
504
576
is_user_variable : false
505
577
}
@@ -516,7 +588,7 @@ impl<'tcx> LocalDecl<'tcx> {
516
588
span,
517
589
scope : ARGUMENT_VISIBILITY_SCOPE
518
590
} ,
519
- lexical_scope : ARGUMENT_VISIBILITY_SCOPE ,
591
+ syntactic_scope : ARGUMENT_VISIBILITY_SCOPE ,
520
592
internal : true ,
521
593
is_user_variable : false
522
594
}
@@ -534,7 +606,7 @@ impl<'tcx> LocalDecl<'tcx> {
534
606
span,
535
607
scope : ARGUMENT_VISIBILITY_SCOPE
536
608
} ,
537
- lexical_scope : ARGUMENT_VISIBILITY_SCOPE ,
609
+ syntactic_scope : ARGUMENT_VISIBILITY_SCOPE ,
538
610
internal : false ,
539
611
name : None , // FIXME maybe we do want some name here?
540
612
is_user_variable : false
0 commit comments