Skip to content

Commit fe309fc

Browse files
committed
Move completion handler usage to availablity
The completion handler async replacement moved from being its own attribute to being an availability attribute. This patch moves the checking from being its own pass to being part of the availability checking under the async availability checking diagnostic pass.
1 parent dc8cdcb commit fe309fc

File tree

5 files changed

+80
-88
lines changed

5 files changed

+80
-88
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5187,67 +5187,6 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E,
51875187
const_cast<Expr *>(E)->walk(Walker);
51885188
}
51895189

5190-
namespace {
5191-
5192-
class CompletionHandlerUsageChecker final : public ASTWalker {
5193-
ASTContext &ctx;
5194-
5195-
public:
5196-
CompletionHandlerUsageChecker(ASTContext &ctx) : ctx(ctx) {}
5197-
5198-
bool walkToDeclPre(Decl *D) override { return !isa<PatternBindingDecl>(D); }
5199-
5200-
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
5201-
if (expr->getType().isNull())
5202-
return {false, expr}; // Something failed to typecheck, bail out
5203-
5204-
if (auto *closure = dyn_cast<ClosureExpr>(expr))
5205-
return {closure->isBodyAsync(), closure};
5206-
5207-
if (auto *call = dyn_cast<ApplyExpr>(expr)) {
5208-
if (auto *fn = dyn_cast<DeclRefExpr>(call->getFn())) {
5209-
if (auto *afd = dyn_cast<AbstractFunctionDecl>(fn->getDecl())) {
5210-
auto *asyncFunc = afd->getAsyncAlternative();
5211-
if (!asyncFunc)
5212-
return {false, call};
5213-
ctx.Diags.diagnose(call->getLoc(), diag::warn_use_async_alternative);
5214-
5215-
if (auto *accessor = dyn_cast<AccessorDecl>(asyncFunc)) {
5216-
SmallString<32> name;
5217-
llvm::raw_svector_ostream os(name);
5218-
accessor->printUserFacingName(os);
5219-
ctx.Diags.diagnose(asyncFunc->getLoc(),
5220-
diag::descriptive_decl_declared_here, name);
5221-
} else {
5222-
ctx.Diags.diagnose(asyncFunc->getLoc(), diag::decl_declared_here,
5223-
asyncFunc->getName());
5224-
}
5225-
}
5226-
}
5227-
}
5228-
return {true, expr};
5229-
}
5230-
};
5231-
5232-
} // namespace
5233-
5234-
void swift::checkFunctionAsyncUsage(AbstractFunctionDecl *decl) {
5235-
if (!decl->isAsyncContext())
5236-
return;
5237-
CompletionHandlerUsageChecker checker(decl->getASTContext());
5238-
BraceStmt *body = decl->getBody();
5239-
if (body)
5240-
body->walk(checker);
5241-
}
5242-
5243-
void swift::checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl) {
5244-
CompletionHandlerUsageChecker checker(decl->getASTContext());
5245-
for (Expr *init : decl->initializers()) {
5246-
if (auto closure = dyn_cast_or_null<ClosureExpr>(init))
5247-
closure->walk(checker);
5248-
}
5249-
}
5250-
52515190
//===----------------------------------------------------------------------===//
52525191
// High-level entry points.
52535192
//===----------------------------------------------------------------------===//

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,12 +3573,11 @@ bool ExprAvailabilityWalker::diagnoseDeclRefAvailability(
35733573
return false;
35743574
}
35753575

3576-
/// Diagnose uses of API annotated '@unavailableFromAsync' when used from
3577-
/// asynchronous contexts.
3578-
/// Returns true if a diagnostic was emitted, false otherwise.
3576+
/// Diagnose misuses of API in asynchronous contexts.
3577+
/// Returns true if a fatal diagnostic was emitted, false otherwise.
35793578
static bool
3580-
diagnoseDeclUnavailableFromAsync(const ValueDecl *D, SourceRange R,
3581-
const Expr *call, const ExportContext &Where) {
3579+
diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
3580+
const Expr *call, const ExportContext &Where) {
35823581
// FIXME: I don't think this is right, but I don't understand the issue well
35833582
// enough to fix it properly. If the decl context is an abstract
35843583
// closure, we need it to have a type assigned to it before we can
@@ -3612,6 +3611,25 @@ diagnoseDeclUnavailableFromAsync(const ValueDecl *D, SourceRange R,
36123611
return false;
36133612

36143613
ASTContext &ctx = Where.getDeclContext()->getASTContext();
3614+
3615+
if (const AbstractFunctionDecl *afd = dyn_cast<AbstractFunctionDecl>(D)) {
3616+
if (const AbstractFunctionDecl *asyncAlt = afd->getAsyncAlternative()) {
3617+
assert(call && "No call calling async alternative function");
3618+
ctx.Diags.diagnose(call->getLoc(), diag::warn_use_async_alternative);
3619+
3620+
if (auto *accessor = dyn_cast<AccessorDecl>(asyncAlt)) {
3621+
SmallString<32> name;
3622+
llvm::raw_svector_ostream os(name);
3623+
accessor->printUserFacingName(os);
3624+
ctx.Diags.diagnose(asyncAlt->getLoc(),
3625+
diag::descriptive_decl_declared_here, name);
3626+
} else {
3627+
ctx.Diags.diagnose(asyncAlt->getLoc(), diag::decl_declared_here,
3628+
asyncAlt->getName());
3629+
}
3630+
}
3631+
}
3632+
36153633
// @available(noasync) spelling
36163634
if (const AvailableAttr *attr = D->getAttrs().getNoAsync(ctx)) {
36173635
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
@@ -3676,7 +3694,7 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
36763694
if (diagnoseExplicitUnavailability(D, R, Where, call, Flags))
36773695
return true;
36783696

3679-
if (diagnoseDeclUnavailableFromAsync(D, R, call, Where))
3697+
if (diagnoseDeclAsyncAvailability(D, R, call, Where))
36803698
return true;
36813699

36823700
// Make sure not to diagnose an accessor's deprecation if we already

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -829,11 +829,7 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
829829

830830
if (hadError)
831831
PBD->setInvalid();
832-
833832
PBD->setInitializerChecked(patternNumber);
834-
835-
checkPatternBindingDeclAsyncUsage(PBD);
836-
837833
return hadError;
838834
}
839835

lib/Sema/TypeCheckStmt.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2114,7 +2114,6 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
21142114
TypeChecker::computeCaptures(AFD);
21152115
if (!AFD->getDeclContext()->isLocalContext()) {
21162116
checkFunctionActorIsolation(AFD);
2117-
checkFunctionAsyncUsage(AFD);
21182117
TypeChecker::checkFunctionEffects(AFD);
21192118
}
21202119

test/attr/attr_availability_async_rename.swift

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// REQUIRES: concurrency
22
// REQUIRES: objc_interop
33

4-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown -I %S/Inputs/custom-modules
5-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown -parse-as-library -I %S/Inputs/custom-modules
4+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs/custom-modules
5+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -parse-as-library -I %S/Inputs/custom-modules
66

77
import ObjcAsync
88

@@ -12,62 +12,74 @@ import ObjcAsync
1212
func goodFunc1(value: String, completionHandler: @escaping (Int) -> Void) {}
1313
@available(*, renamed: "asyncFunc(_:)")
1414
func goodFunc2(value: String, completionHandler: @escaping (Int) -> Void) {}
15-
// expected-note@+1 4 {{'asyncFunc' declared here}}
15+
// expected-note@+2 4 {{'asyncFunc' declared here}}
16+
@available(SwiftStdlib 5.5, *)
1617
func asyncFunc(_ text: String) async -> Int { }
1718

1819
// Ambiguous but only one is async
1920
@available(*, renamed: "overloaded()")
2021
func asyncOnlyOverload(completionHandler: @escaping () -> Void) { }
2122
func overloaded() { }
22-
// expected-note@+1 {{'overloaded()' declared here}}
23+
// expected-note@+2 {{'overloaded()' declared here}}
24+
@available(SwiftStdlib 5.5, *)
2325
func overloaded() async { }
2426

2527
// Renamed decl is ambiguous but the params only match a single case
2628
@available(*, renamed: "overloadedAsyncFunc(value:)")
2729
func nonAmbiguousFunc(value: Int, handler: @escaping () -> Void) {}
28-
// expected-note@+1 {{'overloadedAsyncFunc(value:)' declared here}}
30+
// expected-note@+2 {{'overloadedAsyncFunc(value:)' declared here}}
31+
@available(SwiftStdlib 5.5, *)
2932
func overloadedAsyncFunc(value: Int) async {}
33+
@available(SwiftStdlib 5.5, *)
3034
func overloadedAsyncFunc(value: String) async {}
3135

3236
// More parameters in async but they have defaults and different labels
3337
@available(*, renamed: "defaultedParamsStart(newArg:arg:)")
3438
func defaultedParamsStart(arg: Int, completionHandler: @escaping () -> Void) { }
35-
// expected-note@+1 {{'defaultedParamsStart(newArg:arg:)' declared here}}
39+
// expected-note@+2 {{'defaultedParamsStart(newArg:arg:)' declared here}}
40+
@available(SwiftStdlib 5.5, *)
3641
func defaultedParamsStart(newArg: String = "", arg: Int) async { }
3742

3843
@available(*, renamed: "defaultedParamsStart2(newArg:arg:)")
3944
func defaultedParamsStart2(arg: Int, completionHandler: @escaping () -> Void) { }
40-
// expected-note@+1 {{'defaultedParamsStart2(newArg:arg:)' declared here}}
45+
// expected-note@+2 {{'defaultedParamsStart2(newArg:arg:)' declared here}}
46+
@available(SwiftStdlib 5.5, *)
4147
func defaultedParamsStart2(newArg: Int = 0, arg: Int) async { }
4248

4349
@available(*, renamed: "defaultedParamsMiddle(arg1:newArg:arg2:)")
4450
func defaultedParamsMiddle(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { }
45-
// expected-note@+1 {{'defaultedParamsMiddle(arg1:newArg:arg2:)' declared here}}
51+
// expected-note@+2 {{'defaultedParamsMiddle(arg1:newArg:arg2:)' declared here}}
52+
@available(SwiftStdlib 5.5, *)
4653
func defaultedParamsMiddle(arg1: Int, newArg: String = "", arg2: Int) async { }
4754

4855
@available(*, renamed: "defaultedParamsMiddle2(arg1:newArg:arg2:)")
4956
func defaultedParamsMiddle2(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { }
50-
// expected-note@+1 {{'defaultedParamsMiddle2(arg1:newArg:arg2:)' declared here}}
57+
// expected-note@+2 {{'defaultedParamsMiddle2(arg1:newArg:arg2:)' declared here}}
58+
@available(SwiftStdlib 5.5, *)
5159
func defaultedParamsMiddle2(arg1: Int, newArg: Int = 0, arg2: Int) async { }
5260

5361
@available(*, renamed: "defaultedParamsEnd(arg:newArg:)")
5462
func defaultedParamsEnd(arg: Int, completionHandler: @escaping () -> Void) { }
55-
// expected-note@+1 {{'defaultedParamsEnd(arg:newArg:)' declared here}}
63+
// expected-note@+2 {{'defaultedParamsEnd(arg:newArg:)' declared here}}
64+
@available(SwiftStdlib 5.5, *)
5665
func defaultedParamsEnd(arg: Int, newArg: String = "") async { }
5766

5867
@available(*, renamed: "defaultedParamsEnd2(arg:newArg:)")
5968
func defaultedParamsEnd2(arg: Int, completionHandler: @escaping () -> Void) { }
60-
// expected-note@+1 {{'defaultedParamsEnd2(arg:newArg:)' declared here}}
69+
// expected-note@+2 {{'defaultedParamsEnd2(arg:newArg:)' declared here}}
70+
@available(SwiftStdlib 5.5, *)
6171
func defaultedParamsEnd2(arg: Int, newArg: Int = 0) async { }
6272

6373
@available(*, renamed: "defaultedParamsEnd3(newArg:arg:)")
6474
func defaultedParamsEnd3(arg: Int, completionHandler: @escaping () -> Void) { }
65-
// expected-note@+1 {{'defaultedParamsEnd3(newArg:arg:)' declared here}}
75+
// expected-note@+2 {{'defaultedParamsEnd3(newArg:arg:)' declared here}}
76+
@available(SwiftStdlib 5.5, *)
6677
func defaultedParamsEnd3(newArg: Int, arg: String = "") async { }
6778

6879
@available(*, renamed: "defaultedParamsEnd4(newArg:arg:)")
6980
func defaultedParamsEnd4(arg: Int, completionHandler: @escaping () -> Void) { }
70-
// expected-note@+1 {{'defaultedParamsEnd4(newArg:arg:)' declared here}}
81+
// expected-note@+2 {{'defaultedParamsEnd4(newArg:arg:)' declared here}}
82+
@available(SwiftStdlib 5.5, *)
7183
func defaultedParamsEnd4(newArg: Int, arg: Int = 0) async { }
7284

7385
@available(*, deprecated)
@@ -77,18 +89,22 @@ func defaultedParamsEnd4(newArg: Int, arg: Int = 0) async { }
7789
@available(macOS, deprecated: 12, renamed: "manyAttrsOld()")
7890
@available(*, deprecated)
7991
func manyAttrs(completionHandler: @escaping () -> Void) { }
80-
// expected-note@+1 {{'manyAttrsNew()' declared here}}
92+
// expected-note@+2 {{'manyAttrsNew()' declared here}}
93+
@available(SwiftStdlib 5.5, *)
8194
func manyAttrsNew() async { }
8295

8396
@available(macOS, introduced: 12, renamed: "platformOnlyNew()")
8497
func platformOnly(completionHandler: @escaping () -> Void) { }
85-
// expected-note@+1 {{'platformOnlyNew()' declared here}}
98+
// expected-note@+2 {{'platformOnlyNew()' declared here}}
99+
@available(SwiftStdlib 5.5, *)
86100
func platformOnlyNew() async { }
87101

102+
@available(SwiftStdlib 5.5, *)
88103
struct AnotherStruct {
89104
var otherInstanceProp: Int { get async { 1 } }
90105
}
91106

107+
@available(SwiftStdlib 5.5, *)
92108
struct SomeStruct {
93109
@available(*, renamed: "structFunc")
94110
func structFunc(continuation: @escaping () -> Void) { }
@@ -138,6 +154,7 @@ func badFunc(value: String, completionHandler: @escaping (Int) -> Void) {}
138154
// Not a completion handler
139155
@available(*, renamed: "notCompletionRenamed()")
140156
func notCompletion() {}
157+
@available(SwiftStdlib 5.5, *)
141158
func notCompletionRenamed() async {}
142159

143160
// Corresponding function isn't async
@@ -148,22 +165,27 @@ func completionNotAsyncRenamed() {}
148165
// Renamed decl is ambiguous and there's multiple matches
149166
@available(*, renamed: "asyncFuncDifferentParamNames")
150167
func ambiguousFunc(value: Int, handler: @escaping () -> Void) {}
168+
@available(SwiftStdlib 5.5, *)
151169
func asyncFuncDifferentParamNames(value: Int) async {}
170+
@available(SwiftStdlib 5.5, *)
152171
func asyncFuncDifferentParamNames(value2: Int) async {}
153172

154173
// Renamed decl doesn't have enough params
155174
@available(*, renamed: "fewerParamsFunc()")
156175
func fewerParamsFunc(value: Int, handler: @escaping () -> Void) {}
176+
@available(SwiftStdlib 5.5, *)
157177
func fewerParamsFunc() async {}
158178

159179
// Renamed decl has more params
160180
@available(*, renamed: "moreParamsFunc()")
161181
func moreParamsFunc(handler: @escaping () -> Void) {}
182+
@available(SwiftStdlib 5.5, *)
162183
func moreParamsFunc(value: Int) async {}
163184

164185
// Renamed decl params types don't match
165186
@available(*, renamed: "noMatchingParamsIntFunc(value:)")
166187
func noMatchingParamsFunc(value: Character, handler: @escaping () -> Void) {}
188+
@available(SwiftStdlib 5.5, *)
167189
func noMatchingParamsIntFunc(value: Int) async {}
168190

169191
// Matching function isn't async
@@ -173,33 +195,40 @@ func noMatchingSyncFunc(value: Int) {}
173195

174196
@available(*, renamed: "sameLabelsDifferentOrder(arg2:arg:)")
175197
func sameLabelsDifferentOrder(arg: Int, arg2: String, completionHandler: @escaping () -> Void) { }
198+
@available(SwiftStdlib 5.5, *)
176199
func sameLabelsDifferentOrder(arg2: String, arg: Int) async { }
177200

178201
@available(*, renamed: "handlerNotRemoved(newArg:completionHandler:)")
179202
func handlerNotRemoved(arg: Int, completionHandler: @escaping () -> Void) {}
203+
@available(SwiftStdlib 5.5, *)
180204
func handlerNotRemoved(newArg: Int, completionHandler: @escaping () -> Void) async {}
181205

182206
// Extra arguments. Even though there's defaults, they match the previous
183207
// labels so they shouldn't be skipped. Thus the functions do not match.
184208
@available(*, renamed: "defaultedParamsStartBad(arg:newArg:)")
185209
func defaultedParamsStartBad(arg: Int, completionHandler: @escaping () -> Void) { }
210+
@available(SwiftStdlib 5.5, *)
186211
func defaultedParamsStartBad(arg: String = "", newArg: Int) async { }
187212

188213
@available(*, renamed: "defaultedParamsStartBad2(arg:newArg:)")
189214
func defaultedParamsStartBad2(arg: Int, completionHandler: @escaping () -> Void) { }
215+
@available(SwiftStdlib 5.5, *)
190216
func defaultedParamsStartBad2(arg: Int = 0, newArg: Int) async { }
191217

192218
@available(*, renamed: "defaultedParamsMiddleBad(arg1:arg2:newArg:)")
193219
func defaultedParamsMiddleBad(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { }
220+
@available(SwiftStdlib 5.5, *)
194221
func defaultedParamsMiddleBad(arg1: Int, arg2: String = "", newArg: Int) async { }
195222

196223
@available(*, renamed: "defaultedParamsMiddleBad2(arg1:arg2:newArg:)")
197224
func defaultedParamsMiddleBad2(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { }
225+
@available(SwiftStdlib 5.5, *)
198226
func defaultedParamsMiddleBad2(arg1: Int, arg2: Int = 0, newArg: Int) async { }
199227

200228

201229
// Suggest using async alternative function in async context
202230

231+
@available(SwiftStdlib 5.5, *)
203232
func asyncContext(t: HandlerTest) async {
204233
// expected-warning@+1{{consider using asynchronous alternative function}}
205234
goodFunc1(value: "Hello") { _ in }
@@ -217,6 +246,14 @@ func asyncContext(t: HandlerTest) async {
217246

218247
let _ = await asyncFunc("World")
219248

249+
defer {
250+
goodFunc1(value: "Hello") { _ in }
251+
}
252+
253+
func syncFunc() {
254+
goodFunc1(value: "Hello") { _ in }
255+
}
256+
220257
// expected-warning@+1{{consider using asynchronous alternative function}}
221258
asyncOnlyOverload() { }
222259
// expected-warning@+1{{consider using asynchronous alternative function}}
@@ -237,7 +274,8 @@ func asyncContext(t: HandlerTest) async {
237274
defaultedParamsEnd3(arg: 1) { }
238275
// expected-warning@+1{{consider using asynchronous alternative function}}
239276
defaultedParamsEnd4(arg: 1) { }
240-
// expected-warning@+1{{consider using asynchronous alternative function}}
277+
// expected-warning@+2{{consider using asynchronous alternative function}}
278+
// expected-warning@+1{{'manyAttrs(completionHandler:)' is deprecated}}
241279
manyAttrs() { }
242280
// expected-warning@+1{{consider using asynchronous alternative function}}
243281
platformOnly() { }
@@ -287,6 +325,7 @@ func asyncContext(t: HandlerTest) async {
287325
t.asyncImportSame(1, replyTo: { _ in })
288326
}
289327

328+
@available(SwiftStdlib 5.5, *)
290329
func syncContext(t: HandlerTest) {
291330
goodFunc1(value: "Hello") { _ in }
292331
t.simple { _ in }
@@ -303,6 +342,7 @@ let asyncGlobalClosure = { () async -> () in
303342
goodFunc1(value: "neat") { _ in }
304343
}
305344

345+
@available(SwiftStdlib 5.5, *)
306346
class ClassCallingAsyncStuff {
307347
struct NestedStruct {
308348
@available(*, renamed: "structFunc()")

0 commit comments

Comments
 (0)