Skip to content

Commit 35e028e

Browse files
committed
Suppress more false-positive 'self is unused' warnings
1 parent dfa1fda commit 35e028e

File tree

3 files changed

+49
-22
lines changed

3 files changed

+49
-22
lines changed

include/swift/AST/Expr.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,7 @@ class DeclRefExpr : public Expr {
11691169
ConcreteDeclRef D;
11701170
DeclNameLoc Loc;
11711171
ActorIsolation implicitActorHopTarget;
1172+
ValueDecl *DeclForUsageAnalysis = nullptr;
11721173

11731174
public:
11741175
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
@@ -1247,6 +1248,20 @@ class DeclRefExpr : public Expr {
12471248
Bits.DeclRefExpr.FunctionRefKind = static_cast<unsigned>(refKind);
12481249
}
12491250

1251+
/// The decl that should be used for usage analysis of this DRE.
1252+
/// This is potentially different from `getDecl()`, to handle
1253+
/// cases where performing usage analysis on the actual decl
1254+
/// causes false-positive warnings.
1255+
ValueDecl *getDeclForUsageAnalysis() {
1256+
if (DeclForUsageAnalysis) {
1257+
return DeclForUsageAnalysis;
1258+
}
1259+
1260+
return getDecl();
1261+
}
1262+
1263+
void setDeclForUsageAnalysis(ValueDecl *vd) { DeclForUsageAnalysis = vd; }
1264+
12501265
static bool classof(const Expr *E) {
12511266
return E->getKind() == ExprKind::DeclRef;
12521267
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,8 +1580,15 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15801580
// let self = .someOptionalVariable else { return }` or `let self =
15811581
// someUnrelatedVariable`. If self hasn't been unwrapped yet and is still
15821582
// an optional, we would have already hit an error elsewhere.
1583+
//
1584+
// Always run the `implicitWeakSelfReferenceIsValid` check, since it
1585+
// annotates applicable decls / stmts with state used in
1586+
// `VarDeclUsageChecker`, and we need that state even in closures that
1587+
// don't directly capture self weakly (but are inner closures of a closure
1588+
// that does capture self weakly).
1589+
auto isValid = implicitWeakSelfReferenceIsValid(DRE, inClosure);
15831590
if (closureHasWeakSelfCapture(inClosure)) {
1584-
return !implicitWeakSelfReferenceIsValid(DRE, inClosure);
1591+
return !isValid;
15851592
}
15861593

15871594
// Defensive check for type. If the expression doesn't have type here, it
@@ -1621,7 +1628,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16211628
}
16221629

16231630
static bool
1624-
implicitWeakSelfReferenceIsValid(const DeclRefExpr *DRE,
1631+
implicitWeakSelfReferenceIsValid(DeclRefExpr *DRE,
16251632
const AbstractClosureExpr *inClosure) {
16261633
ASTContext &Ctx = DRE->getDecl()->getASTContext();
16271634
auto selfDecl = DRE->getDecl();
@@ -1640,6 +1647,11 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16401647
return false;
16411648
}
16421649

1650+
// When `VarDeclUsageChecker` checks this DRE, we need to use
1651+
// the base we looked up here instead, since otherwise
1652+
// we'll emit confusing false-positive warnings.
1653+
DRE->setDeclForUsageAnalysis(lookupResult);
1654+
16431655
selfDecl = lookupResult;
16441656
}
16451657

@@ -3537,7 +3549,7 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
35373549

35383550
// If we found a decl that is being assigned to, then mark it.
35393551
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3540-
addMark(DRE->getDecl(), Flags);
3552+
addMark(DRE->getDeclForUsageAnalysis(), Flags);
35413553
return;
35423554
}
35433555

@@ -3622,10 +3634,10 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
36223634
// If this is a DeclRefExpr found in a random place, it is a load of the
36233635
// vardecl.
36243636
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3625-
addMark(DRE->getDecl(), RK_Read);
3637+
addMark(DRE->getDeclForUsageAnalysis(), RK_Read);
36263638

36273639
// If the Expression is a read of a getter, track for diagnostics
3628-
if (auto VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3640+
if (auto VD = dyn_cast<VarDecl>(DRE->getDeclForUsageAnalysis())) {
36293641
AssociatedGetterRefExpr.insert(std::make_pair(VD, DRE));
36303642
}
36313643
}
@@ -3699,7 +3711,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) {
36993711
// conservatively mark it read and written. This will silence "variable
37003712
// unused" and "could be marked let" warnings for it.
37013713
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
3702-
VDUC.addMark(DRE->getDecl(), RK_Read|RK_Written);
3714+
VDUC.addMark(DRE->getDeclForUsageAnalysis(), RK_Read | RK_Written);
37033715
else if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(E)) {
37043716
auto name = declRef->getName();
37053717
auto loc = declRef->getLoc();

test/expr/closure/closures.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ class ExplicitSelfRequiredTest {
176176
doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}}
177177
doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}}
178178
doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
179-
doVoidStuff({ [self = ExplicitSelfRequiredTest()] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}}
180-
doStuff({ [self = ExplicitSelfRequiredTest()] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}}
179+
doVoidStuff({ [self = ExplicitSelfRequiredTest()] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
180+
doStuff({ [self = ExplicitSelfRequiredTest()] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
181181

182182
// Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self."
183183
doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}}
@@ -186,8 +186,8 @@ class ExplicitSelfRequiredTest {
186186
doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}}
187187
doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}}
188188
doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
189-
doVoidStuff({ [self = ExplicitSelfRequiredTest()] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}}
190-
doStuff({ [self = ExplicitSelfRequiredTest()] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}}
189+
doVoidStuff({ [self = ExplicitSelfRequiredTest()] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
190+
doStuff({ [self = ExplicitSelfRequiredTest()] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
191191
doVoidStuff { _ = self.method() }
192192
doVoidStuff { [self] in _ = method() }
193193
doVoidStuff { [self = self] in _ = method() }
@@ -261,12 +261,12 @@ class ExplicitSelfRequiredTest {
261261
// because its `sawError` flag is set to true. To preserve the "capture 'y' was never used" warnings
262262
// above, we put these cases in their own method.
263263
func weakSelfError() {
264-
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
265-
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
266-
doStuff({ [weak self] in x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
267-
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
268-
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
269-
doStuff({ [weak self] in method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
264+
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
265+
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
266+
doStuff({ [weak self] in x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
267+
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
268+
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
269+
doStuff({ [weak self] in method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
270270
}
271271
}
272272

@@ -759,14 +759,14 @@ public class TestImplicitSelfForWeakSelfCapture {
759759
}
760760

761761
doVoidStuff { [weak self] in
762-
guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
762+
guard let self = self else { return }
763763
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
764764
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
765765
}
766766
}
767767

768768
doVoidStuff { [weak self] in
769-
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
769+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
770770
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
771771
}
772772

@@ -784,25 +784,25 @@ public class TestImplicitSelfForWeakSelfCapture {
784784

785785
doVoidStuff { [weak self] in
786786
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
787-
guard let self = self else { return } // // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
787+
guard let self = self else { return }
788788
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
789789
}
790790

791791
doVoidStuffNonEscaping { [weak self] in
792792
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
793-
guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
793+
guard let self = self else { return }
794794
method() // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
795795
}
796796

797797
doVoidStuffNonEscaping { [weak self] in
798-
guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
798+
guard let self = self else { return }
799799
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
800800
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
801801
}
802802
}
803803

804804
doVoidStuffNonEscaping { [weak self] in
805-
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
805+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
806806
method() // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
807807
}
808808
}

0 commit comments

Comments
 (0)