Skip to content

Commit 9bd603b

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 2483165 commit 9bd603b

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
@@ -5140,3 +5140,33 @@ Optional<Identifier> TypeChecker::omitNeedlessWords(VarDecl *var) {
51405140

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

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
@@ -610,24 +610,10 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) {
610610
if (!typeCheckExpression(target))
611611
return failed();
612612

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

632618
return false;
633619
}

0 commit comments

Comments
 (0)