Skip to content

Commit 3bf1f07

Browse files
authored
Merge pull request swiftlang#36481 from etcwilde/ewilde/suggest-async-function
[Concurrency] Emit warning suggesting async function
2 parents ba3d450 + a4a427e commit 3bf1f07

11 files changed

+315
-48
lines changed

include/swift/AST/Decl.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,62 @@ class PatternBindingDecl final : public Decl,
16821682
return const_cast<PatternBindingDecl*>(this)->getMutablePatternList();
16831683
}
16841684

1685+
/// Clean up walking the initializers for the pattern
1686+
class InitIterator {
1687+
1688+
const PatternBindingDecl &decl;
1689+
unsigned currentPatternEntryIndex;
1690+
1691+
void next() { ++currentPatternEntryIndex; }
1692+
1693+
public:
1694+
using value_type = Expr *;
1695+
using pointer = value_type;
1696+
using reference = value_type;
1697+
using difference_type = unsigned;
1698+
1699+
InitIterator(const PatternBindingDecl &decl, unsigned start = 0)
1700+
: decl(decl), currentPatternEntryIndex(start) {}
1701+
1702+
InitIterator &operator++() {
1703+
next();
1704+
return *this;
1705+
}
1706+
1707+
InitIterator operator++(int) {
1708+
InitIterator newIterator(decl, currentPatternEntryIndex);
1709+
newIterator.next();
1710+
return newIterator;
1711+
}
1712+
1713+
pointer operator->() { return decl.getInit(currentPatternEntryIndex); }
1714+
1715+
pointer operator*() { return decl.getInit(currentPatternEntryIndex); }
1716+
1717+
difference_type operator-(const InitIterator &other) {
1718+
return currentPatternEntryIndex - other.currentPatternEntryIndex;
1719+
}
1720+
1721+
bool operator==(const InitIterator &other) const {
1722+
return &decl == &other.decl &&
1723+
currentPatternEntryIndex == other.currentPatternEntryIndex;
1724+
}
1725+
1726+
bool operator!=(const InitIterator &other) const {
1727+
return !(*this == other);
1728+
}
1729+
};
1730+
1731+
InitIterator beginInits() const { return InitIterator(*this); }
1732+
1733+
InitIterator endInits() const {
1734+
return InitIterator(*this, getNumPatternEntries());
1735+
}
1736+
1737+
llvm::iterator_range<InitIterator> initializers() const {
1738+
return llvm::make_range(beginInits(), endInits());
1739+
}
1740+
16851741
void setInitStringRepresentation(unsigned i, StringRef str) {
16861742
getMutablePatternList()[i].setInitStringRepresentation(str);
16871743
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3377,6 +3377,9 @@ ERROR(attr_completion_handler_async_ambiguous_function,none,
33773377
ERROR(attr_completion_handler_async_no_suitable_function,none,
33783378
"no corresponding async function named %0", (DeclNameRef))
33793379

3380+
WARNING(warn_use_async_alternative,none,
3381+
"consider using asynchronous alternative function",())
3382+
33803383
//------------------------------------------------------------------------------
33813384
// MARK: Type Check Expressions
33823385
//------------------------------------------------------------------------------

include/swift/AST/TypeCheckRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,6 +2936,26 @@ class ConditionalRequirementsRequest
29362936
bool isCached() const { return true; }
29372937
};
29382938

2939+
class TypeCheckCompletionHandlerAsyncAttrRequest
2940+
: public SimpleRequest<TypeCheckCompletionHandlerAsyncAttrRequest,
2941+
bool(AbstractFunctionDecl *,
2942+
CompletionHandlerAsyncAttr *),
2943+
RequestFlags::Cached> {
2944+
public:
2945+
using SimpleRequest::SimpleRequest;
2946+
2947+
private:
2948+
friend SimpleRequest;
2949+
2950+
bool evaluate(Evaluator &evaluator,
2951+
AbstractFunctionDecl *attachedFucntionDecl,
2952+
CompletionHandlerAsyncAttr *attr) const;
2953+
2954+
public:
2955+
// Caching
2956+
bool isCached() const { return true; }
2957+
};
2958+
29392959
void simple_display(llvm::raw_ostream &out, Type value);
29402960
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);
29412961
void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,6 @@ SWIFT_REQUEST(TypeChecker, SynthesizeMainFunctionRequest,
323323
SWIFT_REQUEST(TypeChecker, GetImplicitSendableRequest,
324324
NormalProtocolConformance *(NominalTypeDecl *),
325325
Cached, NoLocationInfo)
326+
SWIFT_REQUEST(TypeChecker, TypeCheckCompletionHandlerAsyncAttrRequest,
327+
bool (AbstractFunctionDecl *, CompletionHandlerAsyncAttr *),
328+
Cached, NoLocationInfo)

lib/Sema/MiscDiagnostics.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/NameLookupRequests.h"
2525
#include "swift/AST/Pattern.h"
2626
#include "swift/AST/SourceFile.h"
27+
#include "swift/AST/TypeCheckRequests.h"
2728
#include "swift/Basic/Defer.h"
2829
#include "swift/Basic/SourceManager.h"
2930
#include "swift/Basic/Statistic.h"
@@ -4617,6 +4618,68 @@ static void diagnoseComparisonWithNaN(const Expr *E, const DeclContext *DC) {
46174618
const_cast<Expr *>(E)->walk(Walker);
46184619
}
46194620

4621+
namespace {
4622+
4623+
class CompletionHandlerUsageChecker final : public ASTWalker {
4624+
ASTContext &ctx;
4625+
4626+
public:
4627+
CompletionHandlerUsageChecker(ASTContext &ctx) : ctx(ctx) {}
4628+
4629+
bool walkToDeclPre(Decl *D) override { return !isa<PatternBindingDecl>(D); }
4630+
4631+
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
4632+
if (expr->getType().isNull())
4633+
return {false, expr}; // Something failed to typecheck, bail out
4634+
4635+
if (ClosureExpr *closure = dyn_cast<ClosureExpr>(expr))
4636+
return {closure->isBodyAsync(), closure};
4637+
4638+
if (ApplyExpr *call = dyn_cast<ApplyExpr>(expr)) {
4639+
if (DeclRefExpr *fnDeclExpr = dyn_cast<DeclRefExpr>(call->getFn())) {
4640+
ValueDecl *fnDecl = fnDeclExpr->getDecl();
4641+
CompletionHandlerAsyncAttr *asyncAltAttr =
4642+
fnDecl->getAttrs().getAttribute<CompletionHandlerAsyncAttr>();
4643+
if (asyncAltAttr) {
4644+
// Ensure that the attribute typechecks,
4645+
// this also resolves the async function decl.
4646+
if (!evaluateOrDefault(
4647+
ctx.evaluator,
4648+
TypeCheckCompletionHandlerAsyncAttrRequest{
4649+
cast<AbstractFunctionDecl>(fnDecl), asyncAltAttr},
4650+
false)) {
4651+
return {false, call};
4652+
}
4653+
ctx.Diags.diagnose(call->getLoc(), diag::warn_use_async_alternative);
4654+
ctx.Diags.diagnose(asyncAltAttr->AsyncFunctionDecl->getLoc(),
4655+
diag::decl_declared_here,
4656+
asyncAltAttr->AsyncFunctionDecl->getName());
4657+
}
4658+
}
4659+
}
4660+
return {true, expr};
4661+
}
4662+
};
4663+
4664+
} // namespace
4665+
4666+
void swift::checkFunctionAsyncUsage(AbstractFunctionDecl *decl) {
4667+
if (!decl->isAsyncContext())
4668+
return;
4669+
CompletionHandlerUsageChecker checker(decl->getASTContext());
4670+
BraceStmt *body = decl->getBody();
4671+
if (body)
4672+
body->walk(checker);
4673+
}
4674+
4675+
void swift::checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl) {
4676+
CompletionHandlerUsageChecker checker(decl->getASTContext());
4677+
for (Expr *init : decl->initializers()) {
4678+
if (auto closure = dyn_cast_or_null<ClosureExpr>(init))
4679+
closure->walk(checker);
4680+
}
4681+
}
4682+
46204683
//===----------------------------------------------------------------------===//
46214684
// High-level entry points.
46224685
//===----------------------------------------------------------------------===//

lib/Sema/MiscDiagnostics.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ void fixItEncloseTrailingClosure(ASTContext &ctx,
108108
const CallExpr *call,
109109
Identifier closureLabel);
110110

111+
/// Check that we use the async version of a function where available
112+
///
113+
/// If a completion-handler function is called from an async context and it has
114+
/// a '@completionHandlerAsync' attribute, we emit a diagnostic suggesting the
115+
/// async call.
116+
void checkFunctionAsyncUsage(AbstractFunctionDecl *decl);
117+
void checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl);
111118
} // namespace swift
112119

113120
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

lib/Sema/TypeCheckAttr.cpp

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5609,7 +5609,18 @@ void TypeChecker::checkClosureAttributes(ClosureExpr *closure) {
56095609

56105610
void AttributeChecker::visitCompletionHandlerAsyncAttr(
56115611
CompletionHandlerAsyncAttr *attr) {
5612+
AbstractFunctionDecl *AFD = dyn_cast<AbstractFunctionDecl>(D);
5613+
if (!AFD)
5614+
return;
5615+
5616+
evaluateOrDefault(Ctx.evaluator,
5617+
TypeCheckCompletionHandlerAsyncAttrRequest{AFD, attr}, {});
5618+
}
56125619

5620+
bool TypeCheckCompletionHandlerAsyncAttrRequest::evaluate(
5621+
Evaluator &evaluator, AbstractFunctionDecl *attachedFunctionDecl,
5622+
CompletionHandlerAsyncAttr *attr) const {
5623+
auto &Diags = attachedFunctionDecl->getASTContext().Diags;
56135624
// Check phases:
56145625
// 1. Attached function shouldn't be async and should have enough args
56155626
// to have a completion handler
@@ -5621,30 +5632,29 @@ void AttributeChecker::visitCompletionHandlerAsyncAttr(
56215632
// - Do some sanity checking on types
56225633

56235634
// Phase 1: Typecheck the function the attribute is attached to
5624-
auto attachedFunctionDecl = cast<AbstractFunctionDecl>(D);
56255635
if (attachedFunctionDecl->hasAsync()) {
5626-
diagnose(attr->getLocation(),
5627-
diag::attr_completion_handler_async_handler_not_func, attr);
5628-
diagnose(attachedFunctionDecl->getAsyncLoc(),
5629-
diag::note_attr_function_declared_async);
5630-
return;
5636+
Diags.diagnose(attr->getLocation(),
5637+
diag::attr_completion_handler_async_handler_not_func, attr);
5638+
Diags.diagnose(attachedFunctionDecl->getAsyncLoc(),
5639+
diag::note_attr_function_declared_async);
5640+
return false;
56315641
}
56325642

56335643
const ParameterList *attachedFunctionParams =
56345644
attachedFunctionDecl->getParameters();
56355645
assert(attachedFunctionParams && "Attached function has no parameter list");
56365646
if (attachedFunctionParams->size() == 0) {
5637-
diagnose(attr->getLocation(),
5638-
diag::attr_completion_handler_async_handler_not_func, attr);
5639-
return;
5647+
Diags.diagnose(attr->getLocation(),
5648+
diag::attr_completion_handler_async_handler_not_func, attr);
5649+
return false;
56405650
}
56415651
size_t completionHandlerIndex = attr->CompletionHandlerIndexLoc.isValid()
56425652
? attr->CompletionHandlerIndex
56435653
: attachedFunctionParams->size() - 1;
56445654
if (attachedFunctionParams->size() < completionHandlerIndex) {
5645-
diagnose(attr->CompletionHandlerIndexLoc,
5646-
diag::attr_completion_handler_async_handler_out_of_range);
5647-
return;
5655+
Diags.diagnose(attr->CompletionHandlerIndexLoc,
5656+
diag::attr_completion_handler_async_handler_out_of_range);
5657+
return false;
56485658
}
56495659

56505660
// Phase 2: Typecheck the completion handler
@@ -5654,14 +5664,17 @@ void AttributeChecker::visitCompletionHandlerAsyncAttr(
56545664
AnyFunctionType *handlerType =
56555665
completionHandlerParamDecl->getType()->getAs<AnyFunctionType>();
56565666
if (!handlerType) {
5657-
diagnose(attr->getLocation(),
5658-
diag::attr_completion_handler_async_handler_not_func, attr);
5659-
diagnose(completionHandlerParamDecl->getTypeRepr()->getLoc(),
5660-
diag::note_attr_completion_handler_async_type_is_not_function,
5661-
completionHandlerParamDecl->getType())
5667+
Diags.diagnose(attr->getLocation(),
5668+
diag::attr_completion_handler_async_handler_not_func,
5669+
attr);
5670+
Diags
5671+
.diagnose(
5672+
completionHandlerParamDecl->getTypeRepr()->getLoc(),
5673+
diag::note_attr_completion_handler_async_type_is_not_function,
5674+
completionHandlerParamDecl->getType())
56625675
.highlight(
56635676
completionHandlerParamDecl->getTypeRepr()->getSourceRange());
5664-
return;
5677+
return false;
56655678
}
56665679

56675680
auto handlerTypeRepr =
@@ -5678,27 +5691,31 @@ void AttributeChecker::visitCompletionHandlerAsyncAttr(
56785691
const bool hasError = missingVoid | hasAutoclosure | !hasEscaping;
56795692

56805693
if (hasError) {
5681-
diagnose(attr->getLocation(),
5682-
diag::attr_completion_handler_async_handler_not_func, attr);
5694+
Diags.diagnose(attr->getLocation(),
5695+
diag::attr_completion_handler_async_handler_not_func,
5696+
attr);
56835697

56845698
if (missingVoid)
5685-
diagnose(completionHandlerParamDecl->getLoc(),
5686-
diag::note_attr_completion_function_must_return_void)
5699+
Diags
5700+
.diagnose(completionHandlerParamDecl->getLoc(),
5701+
diag::note_attr_completion_function_must_return_void)
56875702
.highlight(
56885703
completionHandlerParamDecl->getTypeRepr()->getSourceRange());
56895704

56905705
if (!hasEscaping)
5691-
diagnose(completionHandlerParamDecl->getLoc(),
5692-
diag::note_attr_completion_handler_async_handler_attr_req,
5693-
true, "escaping")
5706+
Diags
5707+
.diagnose(completionHandlerParamDecl->getLoc(),
5708+
diag::note_attr_completion_handler_async_handler_attr_req,
5709+
true, "escaping")
56945710
.highlight(
56955711
completionHandlerParamDecl->getTypeRepr()->getSourceRange());
56965712

56975713
if (hasAutoclosure)
5698-
diagnose(handlerTypeAttrs->getLoc(TAK_autoclosure),
5699-
diag::note_attr_completion_handler_async_handler_attr_req,
5700-
false, "autoclosure");
5701-
return;
5714+
Diags.diagnose(
5715+
handlerTypeAttrs->getLoc(TAK_autoclosure),
5716+
diag::note_attr_completion_handler_async_handler_attr_req, false,
5717+
"autoclosure");
5718+
return false;
57025719
}
57035720
}
57045721

@@ -5747,21 +5764,23 @@ void AttributeChecker::visitCompletionHandlerAsyncAttr(
57475764
}
57485765

57495766
if (candidates.empty()) {
5750-
diagnose(attr->AsyncFunctionNameLoc,
5751-
diag::attr_completion_handler_async_no_suitable_function,
5752-
attr->getAsyncFunctionName());
5753-
return;
5767+
Diags.diagnose(attr->AsyncFunctionNameLoc,
5768+
diag::attr_completion_handler_async_no_suitable_function,
5769+
attr->getAsyncFunctionName());
5770+
return false;
57545771
} else if (candidates.size() > 1) {
5755-
diagnose(attr->AsyncFunctionNameLoc,
5756-
diag::attr_completion_handler_async_ambiguous_function, attr,
5757-
attr->getAsyncFunctionName());
5772+
Diags.diagnose(attr->AsyncFunctionNameLoc,
5773+
diag::attr_completion_handler_async_ambiguous_function,
5774+
attr, attr->getAsyncFunctionName());
57585775

57595776
for (AbstractFunctionDecl *candidate : candidates) {
5760-
diagnose(candidate->getLoc(), diag::decl_declared_here,
5761-
candidate->getName());
5777+
Diags.diagnose(candidate->getLoc(), diag::decl_declared_here,
5778+
candidate->getName());
57625779
}
5763-
return;
5780+
return false;
57645781
}
57655782
attr->AsyncFunctionDecl = candidates.front();
57665783
}
5784+
5785+
return true;
57675786
}

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ Type getExplicitGlobalActor(ClosureExpr *closure);
238238
/// \returns true if an error occurred.
239239
bool checkSendableConformance(
240240
ProtocolConformance *conformance, SendableCheck check);
241-
242241
} // end namespace swift
243242

244243
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */

0 commit comments

Comments
 (0)