@@ -1538,11 +1538,13 @@ static bool closureHasWeakSelfCapture(const AbstractClosureExpr *ACE) {
1538
1538
return false ;
1539
1539
}
1540
1540
1541
- // / Whether or not implicit self DREs in this closure are incorrect,
1542
- // / and need to be looked-up manually. This applies to weak self
1543
- // / closures in Swift 5 mode, where the AST is incorrect.
1541
+ // / Whether or not impliciy self decls in this closure require manual
1542
+ // / lookup in order to perform diagnostics with the semantics described
1543
+ // / in SE-0365. This is necessary for closures that capture self weakly
1544
+ // / in Swift 5 language modes, since implicit self decls in weak self
1545
+ // // closures has different semantics in Swift 5 than in Swift 6.
1544
1546
static bool
1545
- closureHasIncorrectImplicitSelfDecls (const AbstractClosureExpr *ACE) {
1547
+ closureRequiresManualLookupForImplicitSelf (const AbstractClosureExpr *ACE) {
1546
1548
if (ACE->getASTContext ().LangOpts .isSwiftVersionAtLeast (6 )) {
1547
1549
return false ;
1548
1550
}
@@ -1561,15 +1563,16 @@ static bool isImplicitSelf(const Expr *E) {
1561
1563
return DRE->getDecl ()->getName ().isSimpleName (Ctx.Id_self );
1562
1564
}
1563
1565
1564
- // When inside a closure with incorrect implicit self decls
1566
+ // When inside a closure that requires manual lookup for implicit self,
1565
1567
// (any closure with a weak self capture, in Swift 5 mode),
1566
1568
// we have to manually lookup what self refers to at the location
1567
- // of a given DRE instead of using the decl in the AST (which is incorrect).
1568
- static ValueDecl *valueDeclForDRE (const DeclRefExpr *DRE,
1569
- const AbstractClosureExpr *ACE) {
1569
+ // according to the semantics of Swift 6 in order to avoid confusing
1570
+ // 'variable self is unused' warnings in Swift 5 mode.
1571
+ static ValueDecl *lookupSelfDeclIfNecessary (const DeclRefExpr *DRE,
1572
+ const AbstractClosureExpr *ACE) {
1570
1573
ASTContext &Ctx = DRE->getDecl ()->getASTContext ();
1571
1574
1572
- if (closureHasIncorrectImplicitSelfDecls (ACE) && isImplicitSelf (DRE)) {
1575
+ if (closureRequiresManualLookupForImplicitSelf (ACE) && isImplicitSelf (DRE)) {
1573
1576
auto lookupResult = ASTScope::lookupSingleLocalDecl (
1574
1577
ACE->getParentSourceFile (), DeclName (Ctx.Id_self ), DRE->getLoc ());
1575
1578
@@ -1680,7 +1683,24 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
1680
1683
static bool
1681
1684
implicitWeakSelfReferenceIsValid (const DeclRefExpr *DRE,
1682
1685
const AbstractClosureExpr *inClosure) {
1683
- auto selfDecl = valueDeclForDRE (DRE, inClosure);
1686
+ // Implicit self DREs have different semantics in Swift 5 language modes
1687
+ // than they have in Swift 6 mode. Take this example:
1688
+ //
1689
+ // doVoidStuff { [weak self] in
1690
+ // guard let self else { return }
1691
+ // method()
1692
+ // }
1693
+ //
1694
+ // In Swift 6, the implicit self DRE refers to the self decl
1695
+ // defined in the guard statement. In Swift 5 mode, the implicit
1696
+ // self DRE instead refers to closure's self `ParamDecl`.
1697
+ //
1698
+ // In order to perform these diagnostics according to the semantics
1699
+ // defined in SE-0365, we need to know the decl that the DRE refers
1700
+ // to in Swift 6. In Swift 5 mode, that requires performing a manual
1701
+ // lookup of what implicit self refers to at this location (according
1702
+ // to the semantics of Swift 6).
1703
+ auto selfDecl = lookupSelfDeclIfNecessary (DRE, inClosure);
1684
1704
ASTContext &Ctx = selfDecl->getASTContext ();
1685
1705
1686
1706
// Check if the implicit self decl refers to a var in a conditional stmt
@@ -2706,20 +2726,42 @@ class VarDeclUsageChecker : public ASTWalker {
2706
2726
VarDecls[vd] |= Flag;
2707
2727
}
2708
2728
2709
- // When inside a closure with incorrect implicit self decls
2710
- // (any closure with a weak self capture, in Swift 5 mode),
2711
- // we have to manually lookup what self refers to at this location
2712
- // rather than using the decl in the AST. Otherwise the usage checker
2713
- // will emit incorrect "value 'self' was defined but never used" warnings .
2714
- ValueDecl *getDecl (DeclRefExpr *DRE) {
2729
+ // / The `ValueDecl` to use when performing usage analysis of the given DRE.
2730
+ // / When inside a closure that requires manual lookup for implcit self decls
2731
+ // / (a closure that captures self weakly in Swift 5 language modes), we
2732
+ // / look up the `ValueDecl` that implicit self refers to according to
2733
+ // / Swift 6 semantics .
2734
+ ValueDecl *getDeclForUsageAnalysis (DeclRefExpr *DRE) {
2715
2735
if (ClosureStack.size () == 0 ) {
2716
2736
return DRE->getDecl ();
2717
2737
}
2718
2738
2719
2739
auto closure = ClosureStack[ClosureStack.size () - 1 ];
2720
2740
assert (closure);
2721
2741
2722
- return valueDeclForDRE (DRE, closure);
2742
+ if (isImplicitSelf (DRE)) {
2743
+ // Implicit self DREs have different semantics in Swift 5 language modes
2744
+ // than they have in Swift 6 mode. Take this example:
2745
+ //
2746
+ // doVoidStuff { [weak self] in
2747
+ // guard let self else { return }
2748
+ // method()
2749
+ // }
2750
+ //
2751
+ // In Swift 6, the implicit self DRE refers to the self decl
2752
+ // defined in the guard statement. In Swift 5 mode, the implicit
2753
+ // self DRE instead refers to closure's self `ParamDecl`.
2754
+ //
2755
+ // If we perform usage analysis using Swift 5 semantics, we'd
2756
+ // get a confusing "value 'self' was defined but never used;
2757
+ // consider replacing with boolean test" warning. To suppress this
2758
+ // warning, we instead perform usage analysis using Swift 6 semantics
2759
+ // (by performing a manual lookup of what implicit self refers to
2760
+ // at this location according to Swift 6 semantics).
2761
+ return lookupSelfDeclIfNecessary (DRE, closure);
2762
+ } else {
2763
+ return DRE->getDecl ();
2764
+ }
2723
2765
}
2724
2766
2725
2767
void markBaseOfStorageUse (Expr *E, ConcreteDeclRef decl, unsigned flags);
@@ -3633,7 +3675,7 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
3633
3675
3634
3676
// If we found a decl that is being assigned to, then mark it.
3635
3677
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3636
- addMark (getDecl (DRE), Flags);
3678
+ addMark (getDeclForUsageAnalysis (DRE), Flags);
3637
3679
return ;
3638
3680
}
3639
3681
@@ -3718,7 +3760,7 @@ ASTWalker::PreWalkResult<Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
3718
3760
// If this is a DeclRefExpr found in a random place, it is a load of the
3719
3761
// vardecl.
3720
3762
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3721
- addMark (getDecl (DRE), RK_Read);
3763
+ addMark (getDeclForUsageAnalysis (DRE), RK_Read);
3722
3764
3723
3765
// If the Expression is a read of a getter, track for diagnostics
3724
3766
if (auto VD = dyn_cast<VarDecl>(DRE->getDecl ())) {
@@ -3809,7 +3851,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) {
3809
3851
// conservatively mark it read and written. This will silence "variable
3810
3852
// unused" and "could be marked let" warnings for it.
3811
3853
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
3812
- VDUC.addMark (VDUC.getDecl (DRE), RK_Read | RK_Written);
3854
+ VDUC.addMark (VDUC.getDeclForUsageAnalysis (DRE), RK_Read | RK_Written);
3813
3855
else if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(E)) {
3814
3856
auto name = declRef->getName ();
3815
3857
auto loc = declRef->getLoc ();
0 commit comments