Skip to content

Commit ef43c48

Browse files
authored
Merge pull request #38947 from rjmccall/nested-self-is-a-warning
Only warn about self capture in nested closures until Swift 6
2 parents d50a37d + 5eecc6a commit ef43c48

File tree

5 files changed

+95
-10
lines changed

5 files changed

+95
-10
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,18 @@ namespace swift {
535535
/// until the next major language version.
536536
InFlightDiagnostic &warnUntilSwiftVersion(unsigned majorVersion);
537537

538+
/// Conditionally limit the diagnostic behavior to warning until
539+
/// the specified version. If the condition is false, no limit is
540+
/// imposed, meaning (presumably) it is treated as an error.
541+
///
542+
/// This helps stage in fixes for stricter diagnostics as warnings
543+
/// until the next major language version.
544+
InFlightDiagnostic &warnUntilSwiftVersionIf(bool shouldLimit,
545+
unsigned majorVersion) {
546+
if (!shouldLimit) return *this;
547+
return warnUntilSwiftVersion(majorVersion);
548+
}
549+
538550
/// Wraps this diagnostic in another diagnostic. That is, \p wrapper will be
539551
/// emitted in place of the diagnostic that otherwise would have been
540552
/// emitted.

lib/Sema/MiscDiagnostics.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16391639
memberLoc = MRE->getLoc();
16401640
Diags.diagnose(memberLoc,
16411641
diag::property_use_in_closure_without_explicit_self,
1642-
baseName.getIdentifier());
1642+
baseName.getIdentifier())
1643+
.warnUntilSwiftVersionIf(Closures.size() > 1, 6);
16431644
}
16441645

16451646
// Handle method calls with a specific diagnostic + fixit.
@@ -1650,7 +1651,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16501651
memberLoc = DSCE->getLoc();
16511652
Diags.diagnose(DSCE->getLoc(),
16521653
diag::method_call_in_closure_without_explicit_self,
1653-
MethodExpr->getDecl()->getBaseIdentifier());
1654+
MethodExpr->getDecl()->getBaseIdentifier())
1655+
.warnUntilSwiftVersionIf(Closures.size() > 1, 6);
16541656
}
16551657

16561658
if (memberLoc.isValid()) {
@@ -1660,7 +1662,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16601662

16611663
// Catch any other implicit uses of self with a generic diagnostic.
16621664
if (isImplicitSelfParamUseLikelyToCauseCycle(E, ACE))
1663-
Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure);
1665+
Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure)
1666+
.warnUntilSwiftVersionIf(Closures.size() > 1, 6);
16641667

16651668
return { true, E };
16661669
}

test/attr/attr_noescape.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,25 +91,25 @@ class SomeClass {
9191

9292
let _: () -> Void = { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}}
9393
func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
94-
let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
94+
let inner2 = { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
9595
let _ = inner2
9696
func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
9797
let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}}
9898
let _ = multi2
99-
doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
99+
doesEscape { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
100100
takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}}
101101
doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
102102
takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}}
103103
}
104104

105105
doesEscape { //expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}}
106106
func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
107-
let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
107+
let inner2 = { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}}
108108
_ = inner2
109109
func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
110110
let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}}
111111
_ = multi2
112-
doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
112+
doesEscape { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
113113
takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}}
114114
doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}}
115115
takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}}

test/expr/closure/closures.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ class ExplicitSelfRequiredTest {
172172
doStuff({ [unowned(unsafe) self] in x+1 })
173173
doStuff({ [unowned self = self] in x+1 })
174174
doStuff({ x+1 }) // 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}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}}
175-
doVoidStuff({ doStuff({ x+1 })}) // 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}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
175+
doVoidStuff({ doStuff({ x+1 })}) // expected-warning {{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}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
176176
doVoidStuff({ x += 1 }) // 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}} {{19-19=self.}}
177177
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.}}
178178
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.}}
@@ -544,7 +544,7 @@ class SR14120 {
544544
func test2() {
545545
doVoidStuff { [self] in
546546
doVoidStuff {
547-
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
547+
// expected-warning@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
548548
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
549549
// expected-note@+1 {{reference 'self.' explicitly}}
550550
operation()
@@ -580,7 +580,7 @@ class SR14120 {
580580
doVoidStuff { [self] in
581581
doVoidStuff { [self] in
582582
doVoidStuff {
583-
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
583+
// expected-warning@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
584584
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
585585
// expected-note@+1 {{reference 'self.' explicitly}}
586586
operation()
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 6
2+
3+
func doStuff(_ fn : @escaping () -> Int) {}
4+
func doVoidStuff(_ fn : @escaping () -> ()) {}
5+
func doVoidStuffNonEscaping(_ fn: () -> ()) {}
6+
7+
class ExplicitSelfRequiredTest {
8+
var x = 42
9+
func method() {
10+
doVoidStuff({ doStuff({ x+1 })}) // 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}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
11+
}
12+
}
13+
14+
class SR14120 {
15+
func operation() {}
16+
17+
func test1() {
18+
doVoidStuff { [self] in
19+
operation()
20+
}
21+
}
22+
23+
func test2() {
24+
doVoidStuff { [self] in
25+
doVoidStuff {
26+
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
27+
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
28+
// expected-note@+1 {{reference 'self.' explicitly}}
29+
operation()
30+
}
31+
}
32+
}
33+
34+
func test3() {
35+
doVoidStuff { [self] in
36+
doVoidStuff { [self] in
37+
operation()
38+
}
39+
}
40+
}
41+
42+
func test4() {
43+
doVoidStuff { [self] in
44+
doVoidStuff {
45+
self.operation()
46+
}
47+
}
48+
}
49+
50+
func test5() {
51+
doVoidStuff { [self] in
52+
doVoidStuffNonEscaping {
53+
operation()
54+
}
55+
}
56+
}
57+
58+
func test6() {
59+
doVoidStuff { [self] in
60+
doVoidStuff { [self] in
61+
doVoidStuff {
62+
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
63+
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
64+
// expected-note@+1 {{reference 'self.' explicitly}}
65+
operation()
66+
}
67+
}
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)