Skip to content

Commit 8df4ea0

Browse files
committed
Improve quality of 'capture self explicitly to enable implicit self in this closure' and 'variable other than self captured here under the name self does not enable implicit self' diagnostics
1 parent 94dcf9b commit 8df4ea0

File tree

4 files changed

+156
-102
lines changed

4 files changed

+156
-102
lines changed

lib/AST/Expr.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,18 +1363,16 @@ bool CaptureListEntry::isSimpleSelfCapture(bool excludeWeakCaptures) const {
13631363
auto *expr = PBD->getInit(0);
13641364

13651365
if (auto *attr = Var->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
1366-
if (attr->get() == ReferenceOwnership::Weak) {
1367-
if (excludeWeakCaptures) {
1368-
return false;
1369-
}
1370-
1371-
// If not excluding weak captures, look through any InjectIntoOptionalExpr
1372-
if (auto injectExpr = dyn_cast_or_null<InjectIntoOptionalExpr>(expr)) {
1373-
expr = injectExpr->getSubExpr();
1374-
}
1366+
if (attr->get() == ReferenceOwnership::Weak && excludeWeakCaptures) {
1367+
return false;
13751368
}
13761369
}
13771370

1371+
// Look through any ImplicitConversionExpr that may contain the DRE
1372+
if (auto conversion = dyn_cast_or_null<ImplicitConversionExpr>(expr)) {
1373+
expr = conversion->getSubExpr();
1374+
}
1375+
13781376
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
13791377
if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
13801378
return VD->getName() == ctx.Id_self;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 93 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,7 +1738,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
17381738

17391739
static bool selfDeclAllowsImplicitSelf(const ValueDecl *selfDecl,
17401740
const AbstractClosureExpr *inClosure,
1741-
bool validateParentClosures = true) {
1741+
bool validateParentClosures = true,
1742+
bool validateSelfRebindings = true) {
17421743
ASTContext &ctx = inClosure->getASTContext();
17431744

17441745
auto requiresSelfQualification =
@@ -1767,22 +1768,18 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
17671768
// be a closure nested in some parent closure with a `weak self`
17681769
// capture, so we should always validate the conditional statement
17691770
// that defines self if present.
1770-
if (auto conditionalStmt = parentConditionalStmt(selfDecl)) {
1771-
if (!hasValidSelfRebinding(conditionalStmt, inClosure)) {
1772-
return false;
1771+
if (validateSelfRebindings) {
1772+
if (auto conditionalStmt = parentConditionalStmt(selfDecl)) {
1773+
if (!hasValidSelfRebinding(conditionalStmt, inClosure)) {
1774+
return false;
1775+
}
17731776
}
17741777
}
17751778

1776-
// In Swift 5 mode, due to backwards compatibility requirements,
1777-
// non-escaping closures use an implicit self lookup behavior
1778-
// that makes self non-optional before being unwrapped.
1779-
// - In Swift 5 mode we have to manually validate that the self decl
1780-
// has been unwrapped in a permitted way.
1781-
// - In Swift 6 mode this check isn't necessary because a self decl
1782-
// that hasn't been unwrapped yet is Optional as expected, so this
1783-
// would have already failed to compile elsewhere.
1784-
if (!ctx.LangOpts.isSwiftVersionAtLeast(6) &&
1785-
closureHasWeakSelfCapture(inClosure) &&
1779+
// If this closure has a `weak self` capture, require that the
1780+
// closure unwraps self. If not, implicit self is not allowed
1781+
// in this closure or in any nested closure.
1782+
if (closureHasWeakSelfCapture(inClosure) &&
17861783
!hasValidSelfRebinding(parentConditionalStmt(selfDecl), inClosure)) {
17871784
return false;
17881785
}
@@ -1832,7 +1829,17 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
18321829

18331830
static bool implicitSelfDisallowedDueToInvalidParent(
18341831
const ValueDecl *selfDecl, const AbstractClosureExpr *inClosure) {
1832+
return parentClosureDisallowingImplicitSelf(selfDecl, inClosure) !=
1833+
nullptr;
1834+
}
1835+
1836+
static const AbstractClosureExpr *
1837+
parentClosureDisallowingImplicitSelf(const ValueDecl *selfDecl,
1838+
const AbstractClosureExpr *inClosure) {
18351839
ASTContext &ctx = inClosure->getASTContext();
1840+
if (!selfDecl) {
1841+
return nullptr;
1842+
}
18361843

18371844
// Find the outer decl that determines what self refers to in this
18381845
// closure.
@@ -1892,7 +1899,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
18921899
// this closure nor any of its parents disallow it).
18931900
auto parent = parentClosure(outerClosure);
18941901
if (!parent) {
1895-
return false;
1902+
return nullptr;
18961903
}
18971904

18981905
outerClosure = parent;
@@ -1901,19 +1908,44 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
19011908
outerClosure->getSourceRange().contains(
19021909
outerSelfDecl->getLoc(true));
19031910

1911+
// We can stop searching because we found the first outer closure
1912+
// that contains the outer self decl.
19041913
if (outerClosureContainsSelfDecl) {
1905-
// We can stop searching because we found the first outer closure
1906-
// that contains the outer self decl
1907-
return !selfDeclAllowsImplicitSelf(outerSelfDecl, outerClosure);
1914+
// Check whether implicit self is disallowed due to this specific
1915+
// closure, or if its disallowed due to some parent of this closure,
1916+
// so we can return the specific closure that is invalid.
1917+
auto implicitSelfDisallowedDueToThisClosure =
1918+
!selfDeclAllowsImplicitSelf(outerSelfDecl, outerClosure,
1919+
/*validateParentClosures:*/ false);
1920+
1921+
auto outerParentDisallowingImplicitSelf =
1922+
parentClosureDisallowingImplicitSelf(outerSelfDecl, outerClosure);
1923+
1924+
if (implicitSelfDisallowedDueToThisClosure) {
1925+
return outerClosure;
1926+
} else if (outerParentDisallowingImplicitSelf) {
1927+
return outerParentDisallowingImplicitSelf;
1928+
} else {
1929+
return nullptr;
1930+
}
19081931
}
19091932

19101933
// Optionally validate this intermediate closure before continuing
19111934
// to search upwards. Since we're already validating the chain of
19121935
// parent closures, we don't need to do that separate for this closure.
19131936
if (validateIntermediateParents) {
1914-
if (!selfDeclAllowsImplicitSelf(selfDecl, outerClosure,
1915-
/*validateParentClosures*/ false)) {
1916-
return true;
1937+
// If the self decl is defined in a `let self = self` unwrapping
1938+
// condition, only validate that condition once we reach the weak
1939+
// closure that actually contains the condition. This makes sure
1940+
// that we ultimately return that parent closure as invalid, instead
1941+
// of this closure that could itself be valid.
1942+
auto validateSelfRebindings = closureHasWeakSelfCapture(outerClosure);
1943+
1944+
if (!selfDeclAllowsImplicitSelf(
1945+
selfDecl, outerClosure,
1946+
/*validateParentClosures*/ false,
1947+
/*validateSelfRebindings*/ validateSelfRebindings)) {
1948+
return outerClosure;
19171949
}
19181950
}
19191951
} while (true);
@@ -2151,6 +2183,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
21512183
};
21522184

21532185
SourceLoc memberLoc = SourceLoc();
2186+
const ValueDecl *selfDecl;
21542187
if (auto *MRE = dyn_cast<MemberRefExpr>(E))
21552188
if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase(), ACE)) {
21562189
auto baseName = MRE->getMember().getDecl()->getBaseName();
@@ -2159,6 +2192,10 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
21592192
diag::property_use_in_closure_without_explicit_self,
21602193
baseName.getIdentifier())
21612194
.warnUntilSwiftVersionIf(shouldOnlyWarn(MRE->getBase()), 6);
2195+
2196+
if (auto DRE = dyn_cast_or_null<DeclRefExpr>(MRE->getBase())) {
2197+
selfDecl = DRE->getDecl();
2198+
}
21622199
}
21632200

21642201
// Handle method calls with a specific diagnostic + fixit.
@@ -2171,10 +2208,14 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
21712208
diag::method_call_in_closure_without_explicit_self,
21722209
MethodExpr->getDecl()->getBaseIdentifier())
21732210
.warnUntilSwiftVersionIf(shouldOnlyWarn(DSCE->getBase()), 6);
2211+
2212+
if (auto DRE = dyn_cast_or_null<DeclRefExpr>(DSCE->getBase())) {
2213+
selfDecl = DRE->getDecl();
2214+
}
21742215
}
21752216

21762217
if (memberLoc.isValid()) {
2177-
emitFixIts(Diags, memberLoc, ACE);
2218+
emitFixIts(Diags, memberLoc, selfDecl, ACE);
21782219
return Action::SkipNode(E);
21792220
}
21802221

@@ -2232,38 +2273,51 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
22322273
}
22332274

22342275
/// Emit any fix-its for this error.
2235-
void emitFixIts(DiagnosticEngine &Diags,
2236-
SourceLoc memberLoc,
2237-
const AbstractClosureExpr *ACE) {
2276+
void emitFixIts(DiagnosticEngine &Diags, SourceLoc memberLoc,
2277+
const ValueDecl *selfDecl, const AbstractClosureExpr *ACE) {
2278+
// These fix-its have to be diagnosed on the closure that requires,
2279+
// but is currently missing, self qualification. It's possible that
2280+
// ACE doesn't require self qualification (e.g. because it's
2281+
// non-escaping) but is nested inside a closure that does require self
2282+
// qualification. In that case we have to emit the fixit for the parent
2283+
// closure.
2284+
// - Even if this closure requires self qualification, if there's an
2285+
// invalid parent we emit the diagnostic on that parent first.
2286+
// To enable implicit self you'd have to fix the parent anyway.
2287+
// This lets us avoid bogus diagnostics on this closure when
2288+
// it's actually _just_ the parent that's invalid.
2289+
auto closureForDiagnostics = ACE;
2290+
if (auto invalidParentClosure =
2291+
parentClosureDisallowingImplicitSelf(selfDecl, ACE)) {
2292+
// Don't do this for escaping autoclosures, which are never allowed
2293+
// to use implicit self, even after fixing any invalid parents.
2294+
auto isEscapingAutoclosure =
2295+
isa<AutoClosureExpr>(ACE) &&
2296+
isClosureRequiringSelfQualification(ACE, Ctx);
2297+
if (!isEscapingAutoclosure) {
2298+
closureForDiagnostics = invalidParentClosure;
2299+
}
2300+
}
2301+
22382302
// This error can be fixed by either capturing self explicitly (if in an
22392303
// explicit closure), or referencing self explicitly.
2240-
if (auto *CE = dyn_cast<const ClosureExpr>(ACE)) {
2241-
// These fix-its have to be diagnosed on the closure that requires,
2242-
// but is currently missing, self qualification. It's possible that
2243-
// ACE doesn't require self qualification (e.g. because it's
2244-
// non-escaping) but is nested inside a closure that does require self
2245-
// qualification. In that case we have to emit the fixit for the parent
2246-
// closure.
2247-
auto closureForDiagnostics =
2248-
closestParentClosureRequiringSelfQualification(CE);
2249-
2250-
if (diagnoseAlmostMatchingCaptures(Diags, memberLoc,
2251-
closureForDiagnostics)) {
2304+
if (auto *CE = dyn_cast<const ClosureExpr>(closureForDiagnostics)) {
2305+
if (diagnoseAlmostMatchingCaptures(Diags, memberLoc, CE)) {
22522306
// Bail on the rest of the diagnostics. Offering the option to
22532307
// capture 'self' explicitly will result in an error, and using
22542308
// 'self.' explicitly will be accessing something other than the
22552309
// self param.
22562310
return;
22572311
}
2258-
emitFixItsForExplicitClosure(Diags, memberLoc, closureForDiagnostics);
2312+
emitFixItsForExplicitClosure(Diags, memberLoc, CE);
22592313
} else {
22602314
// If this wasn't an explicit closure, just offer the fix-it to
22612315
// reference self explicitly.
22622316
Diags.diagnose(memberLoc, diag::note_reference_self_explicitly)
22632317
.fixItInsert(memberLoc, "self.");
22642318
}
22652319
}
2266-
2320+
22672321
/// Diagnose any captures which might have been an attempt to capture
22682322
/// \c self strongly, but do not actually enable implicit \c self. Returns
22692323
/// whether there were any such captures to diagnose.
@@ -2313,24 +2367,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
23132367
}
23142368
}
23152369

2316-
static const ClosureExpr *closestParentClosureRequiringSelfQualification(
2317-
const ClosureExpr *closureExpr) {
2318-
auto &ctx = closureExpr->getASTContext();
2319-
const ClosureExpr *closureForDiagnostic = closureExpr;
2320-
2321-
// Find the closest parent closure that requires self qualification
2322-
while (!isClosureRequiringSelfQualification(closureExpr, ctx)) {
2323-
auto parent = parentClosure(closureForDiagnostic);
2324-
if (!parent) {
2325-
break;
2326-
}
2327-
2328-
closureForDiagnostic = parent;
2329-
}
2330-
2331-
return closureForDiagnostic;
2332-
}
2333-
23342370
/// Emit a fix-it for inserting \c self into in existing capture list, along
23352371
/// with a trailing comma if needed. The fix-it will be attached to the
23362372
/// provided diagnostic \c diag.

0 commit comments

Comments
 (0)