Skip to content

Commit 165a379

Browse files
authored
Merge pull request #60663 from etcwilde/ewilde/completion-handler-async-cleanups
Fix completion handler replacement warnings
2 parents fedf8e8 + 144395e commit 165a379

7 files changed

+106
-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/TypeCheckAttr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,6 +2733,10 @@ static void lookupReplacedDecl(DeclNameRef replacedDeclName,
27332733
SmallVectorImpl<ValueDecl *> &results) {
27342734
auto *declCtxt = replacement->getDeclContext();
27352735

2736+
// Hop up to the FileUnit if we're in top-level code
2737+
if (auto *toplevel = dyn_cast<TopLevelCodeDecl>(declCtxt))
2738+
declCtxt = toplevel->getDeclContext();
2739+
27362740
// Look at the accessors' storage's context.
27372741
if (auto *accessor = dyn_cast<AccessorDecl>(replacement)) {
27382742
auto *storage = accessor->getStorage();

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)