Skip to content

Commit 344a894

Browse files
authored
Merge pull request #73841 from jamieQ/diagnose-noasync-in-defer
[Sema]: improve noasync diagnostics in defer statement bodies
2 parents 2ce5a33 + e68c36b commit 344a894

File tree

2 files changed

+115
-7
lines changed

2 files changed

+115
-7
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration(
397397
return false;
398398
}
399399

400+
// Utility function to help determine if noasync diagnostics are still
401+
// appropriate even if a `DeclContext` returns `false` from `isAsyncContext()`.
402+
static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) {
403+
if (auto *D = DC->getAsDecl())
404+
if (auto *FD = dyn_cast<FuncDecl>(D))
405+
if (FD->isDeferBody())
406+
// If this is a defer body, we should delegate to its parent.
407+
return shouldTreatDeclContextAsAsyncForDiagnostics(DC->getParent());
408+
409+
return DC->isAsyncContext();
410+
}
411+
400412
namespace {
401413

402414
/// A class to walk the AST to build the type refinement context hierarchy.
@@ -3770,17 +3782,20 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
37703782
static bool
37713783
diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
37723784
const Expr *call, const ExportContext &Where) {
3773-
// If we are in a synchronous context, don't check it
3774-
if (!Where.getDeclContext()->isAsyncContext())
3785+
// If we are not in an (effective) async context, don't check it
3786+
if (!shouldTreatDeclContextAsAsyncForDiagnostics(Where.getDeclContext()))
37753787
return false;
37763788

37773789
ASTContext &ctx = Where.getDeclContext()->getASTContext();
37783790

3779-
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3780-
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3781-
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
3782-
ctx.Diags.diagnose(diagLoc, diag::warn_use_async_alternative);
3783-
asyncAlt->diagnose(diag::decl_declared_here, asyncAlt);
3791+
// Only suggest async alternatives if the DeclContext is truly async
3792+
if (Where.getDeclContext()->isAsyncContext()) {
3793+
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3794+
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3795+
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
3796+
ctx.Diags.diagnose(diagLoc, diag::warn_use_async_alternative);
3797+
asyncAlt->diagnose(diag::decl_declared_here, asyncAlt);
3798+
}
37843799
}
37853800
}
37863801

test/attr/attr_availability_noasync.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,96 @@ class TestClass {
6262
@available(*, noasync)
6363
deinit { }
6464
}
65+
66+
@available(SwiftStdlib 5.5, *)
67+
func test_defers_sync() {
68+
defer {
69+
defer { basicNoAsync() }
70+
basicNoAsync()
71+
}
72+
73+
func local_sync_func() {
74+
defer { basicNoAsync() }
75+
_ = ()
76+
}
77+
78+
func local_async_func() async {
79+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
80+
defer { basicNoAsync() }
81+
_ = ()
82+
}
83+
84+
let local_sync_closure = { () -> Void in
85+
defer { basicNoAsync() }
86+
_ = ()
87+
}
88+
_ = local_sync_closure
89+
90+
// local async closure
91+
let local_async_closure = { () async -> Void in
92+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
93+
defer { basicNoAsync() }
94+
_ = ()
95+
}
96+
_ = local_async_closure
97+
98+
var local_sync_var: Void {
99+
defer { basicNoAsync() }
100+
return ()
101+
}
102+
103+
var local_async_var: Void {
104+
get async {
105+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
106+
defer { basicNoAsync() }
107+
return ()
108+
}
109+
}
110+
}
111+
112+
@available(SwiftStdlib 5.5, *)
113+
func test_defer_async() async {
114+
defer {
115+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
116+
defer { basicNoAsync() }
117+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
118+
basicNoAsync()
119+
}
120+
121+
func local_sync_func() {
122+
defer { basicNoAsync() }
123+
_ = ()
124+
}
125+
126+
func local_async_func() async {
127+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
128+
defer { basicNoAsync() }
129+
_ = ()
130+
}
131+
132+
let local_sync_closure = { () -> Void in
133+
defer { basicNoAsync() }
134+
_ = ()
135+
}
136+
_ = local_sync_closure
137+
138+
let local_async_closure = { () async -> Void in
139+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
140+
defer { basicNoAsync() }
141+
_ = ()
142+
}
143+
_ = local_async_closure
144+
145+
var local_sync_var: Void {
146+
defer { basicNoAsync() }
147+
return ()
148+
}
149+
150+
var local_async_var: Void {
151+
get async {
152+
// expected-warning@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts; this is an error in the Swift 6 language mode}}
153+
defer { basicNoAsync() }
154+
return ()
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)