Skip to content

Commit f1ad703

Browse files
committed
[CS] Avoid skipping SingleValueStmtExpr branch with ReturnStmt for completion
We still need to solve a branch with a ReturnStmt to avoid leaving the contextual result type unbound. This isn't currently legal anyway, so isn't likely to come up often in practice, but make sure we can still solve.
1 parent 8e460d1 commit f1ad703

File tree

6 files changed

+41
-5
lines changed

6 files changed

+41
-5
lines changed

include/swift/AST/Stmt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ class BraceStmt final : public Stmt,
225225

226226
ASTNode findAsyncNode();
227227

228+
/// Whether the body contains an explicit `return` statement. This computation
229+
/// is cached.
230+
bool hasExplicitReturnStmt(ASTContext &ctx) const;
231+
228232
/// If this brace contains a single ASTNode, or a \c #if that has a single active
229233
/// element, returns it. This will always be the last element of the brace.
230234
/// Otherwise returns \c nullptr.

lib/AST/Stmt.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ ASTNode BraceStmt::findAsyncNode() {
302302
return asyncFinder.getAsyncNode();
303303
}
304304

305+
bool BraceStmt::hasExplicitReturnStmt(ASTContext &ctx) const {
306+
return evaluateOrDefault(ctx.evaluator,
307+
BraceHasExplicitReturnStmtRequest{this}, false);
308+
}
309+
305310
static bool hasSingleActiveElement(ArrayRef<ASTNode> elts) {
306311
return elts.size() == 1;
307312
}

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,9 +1289,7 @@ bool AnyFunctionRef::bodyHasExplicitReturnStmt() const {
12891289
return false;
12901290
}
12911291

1292-
auto &ctx = getAsDeclContext()->getASTContext();
1293-
return evaluateOrDefault(ctx.evaluator,
1294-
BraceHasExplicitReturnStmtRequest{body}, false);
1292+
return body->hasExplicitReturnStmt(getAsDeclContext()->getASTContext());
12951293
}
12961294

12971295
void AnyFunctionRef::getExplicitReturnStmts(

lib/Sema/CSSyntacticElement.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,13 @@ static bool isViableElement(ASTNode element,
309309
// Skip if we're doing completion for a SingleValueStmtExpr, and have a
310310
// brace that doesn't involve a single expression, and doesn't have a
311311
// code completion token, as it won't contribute to the type of the
312-
// SingleValueStmtExpr.
312+
// SingleValueStmtExpr. We also need to skip if the body has a ReturnStmt,
313+
// which isn't something that's currently allowed, but is necessary to
314+
// correctly infer the contextual type without leaving it unbound.
313315
if (isForSingleValueStmtCompletion &&
314316
!SingleValueStmtExpr::hasResult(braceStmt) &&
315-
!cs.containsIDEInspectionTarget(braceStmt)) {
317+
!cs.containsIDEInspectionTarget(braceStmt) &&
318+
!braceStmt->hasExplicitReturnStmt(cs.getASTContext())) {
316319
return false;
317320
}
318321
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %batch-code-completion
2+
3+
struct S {
4+
var str: String
5+
}
6+
7+
_ = {
8+
let k = if .random() {
9+
return ""
10+
} else {
11+
S()
12+
}
13+
// Make sure we can still infer 'k' here.
14+
return k.#^COMPLETE_ON_SVE_WITH_RET^#
15+
// COMPLETE_ON_SVE_WITH_RET: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: str[#String#]; name=str
16+
}
17+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// {"kind":"complete","original":"0bd7af1f","signature":"swift::constraints::TypeVarRefCollector::walkToStmtPre(swift::Stmt*)","signatureAssert":"Assertion failed: (result), function getClosureType"}
2+
// RUN: %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s
3+
{
4+
let a =
5+
if <#expression#> {
6+
return
7+
}
8+
return #^^#
9+
}

0 commit comments

Comments
 (0)