Skip to content

Commit 7b0f68c

Browse files
committed
[Diagnostics] Apply "unhandled throw" diagnostic for for-in loop in closures
Extract diagnostic into a method and use it while type-checking `for-in` in top-level code and in closures.
1 parent 8d1aeac commit 7b0f68c

File tree

4 files changed

+47
-18
lines changed

4 files changed

+47
-18
lines changed

lib/Sema/CSClosure.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//
1717
//===----------------------------------------------------------------------===//
1818

19+
#include "MiscDiagnostics.h"
1920
#include "TypeChecker.h"
2021
#include "swift/Sema/ConstraintSystem.h"
2122

@@ -1163,12 +1164,17 @@ class ClosureConstraintApplication
11631164

11641165
auto forEachTarget =
11651166
rewriteTarget(*cs.getSolutionApplicationTarget(forEachStmt));
1167+
11661168
if (!forEachTarget)
11671169
hadError = true;
11681170

11691171
auto body = visit(forEachStmt->getBody()).get<Stmt *>();
11701172
forEachStmt->setBody(cast<BraceStmt>(body));
11711173

1174+
// Check to see if the sequence expr is throwing (in async context),
1175+
// if so require the stmt to have a `try`.
1176+
hadError |= diagnoseUnhandledThrowsInAsyncContext(closure, forEachStmt);
1177+
11721178
return forEachStmt;
11731179
}
11741180

lib/Sema/MiscDiagnostics.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5133,3 +5133,33 @@ Optional<Identifier> TypeChecker::omitNeedlessWords(VarDecl *var) {
51335133

51345134
return None;
51355135
}
5136+
5137+
bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,
5138+
ForEachStmt *forEach) {
5139+
if (!forEach->getAwaitLoc().isValid())
5140+
return false;
5141+
5142+
auto &ctx = dc->getASTContext();
5143+
5144+
auto sequenceProto = TypeChecker::getProtocol(
5145+
ctx, forEach->getForLoc(), KnownProtocolKind::AsyncSequence);
5146+
5147+
if (!sequenceProto)
5148+
return false;
5149+
5150+
// fetch the sequence out of the statement
5151+
// else wise the value is potentially unresolved
5152+
auto Ty = forEach->getSequence()->getType();
5153+
auto module = dc->getParentModule();
5154+
auto conformanceRef = module->lookupConformance(Ty, sequenceProto);
5155+
5156+
if (conformanceRef.hasEffect(EffectKind::Throws) &&
5157+
forEach->getTryLoc().isInvalid()) {
5158+
ctx.Diags
5159+
.diagnose(forEach->getAwaitLoc(), diag::throwing_call_unhandled, "call")
5160+
.fixItInsert(forEach->getAwaitLoc(), "try");
5161+
return true;
5162+
}
5163+
5164+
return false;
5165+
}

lib/Sema/MiscDiagnostics.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace swift {
3232
class Stmt;
3333
class TopLevelCodeDecl;
3434
class ValueDecl;
35+
class ForEachStmt;
3536

3637
/// Emit diagnostics for syntactic restrictions on a given expression.
3738
void performSyntacticExprDiagnostics(
@@ -115,6 +116,12 @@ void fixItEncloseTrailingClosure(ASTContext &ctx,
115116
/// we emit a diagnostic suggesting the async call.
116117
void checkFunctionAsyncUsage(AbstractFunctionDecl *decl);
117118
void checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl);
119+
120+
/// Detect and diagnose a missing `try` in `for-in` loop sequence
121+
/// expression in async context (denoted with `await` keyword).
122+
bool diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,
123+
ForEachStmt *forEach);
124+
118125
} // namespace swift
119126

120127
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -613,24 +613,10 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) {
613613
if (!typeCheckExpression(target))
614614
return failed();
615615

616-
// check to see if the sequence expr is throwing (and async), if so require
617-
// the stmt to have a try loc
618-
if (stmt->getAwaitLoc().isValid()) {
619-
// fetch the sequence out of the statement
620-
// else wise the value is potentially unresolved
621-
auto Ty = stmt->getSequence()->getType();
622-
auto module = dc->getParentModule();
623-
auto conformanceRef = module->lookupConformance(Ty, sequenceProto);
624-
625-
if (conformanceRef.hasEffect(EffectKind::Throws) &&
626-
stmt->getTryLoc().isInvalid()) {
627-
auto &diags = dc->getASTContext().Diags;
628-
diags.diagnose(stmt->getAwaitLoc(), diag::throwing_call_unhandled, "call")
629-
.fixItInsert(stmt->getAwaitLoc(), "try");
630-
631-
return failed();
632-
}
633-
}
616+
// Check to see if the sequence expr is throwing (in async context),
617+
// if so require the stmt to have a `try`.
618+
if (diagnoseUnhandledThrowsInAsyncContext(dc, stmt))
619+
return failed();
634620

635621
return false;
636622
}

0 commit comments

Comments
 (0)