Skip to content

Commit d8d8db9

Browse files
committed
[CodeComplete] Properly handle if/switch exprs
Run PreCheckFunctionBodyRequest to ensure we insert an implicit return for an if/switch if needed, and ensure we don't try and type-check an element in a SingleValueStmtExpr separately, as it should be type-checked as a whole by the constraint system. This ensures we can propagate a contextual type from outside an if/switch expression for code completion.
1 parent bf3c807 commit d8d8db9

File tree

2 files changed

+130
-5
lines changed

2 files changed

+130
-5
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2396,6 +2396,13 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
23962396
return false;
23972397
}
23982398
}
2399+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
2400+
if (AFD->hasBody() && !AFD->isBodyTypeChecked()) {
2401+
// Pre-check the function body if needed.
2402+
(void)evaluateOrDefault(evaluator, PreCheckFunctionBodyRequest{AFD},
2403+
nullptr);
2404+
}
2405+
}
23992406
}
24002407

24012408
// Find innermost ASTNode at Loc from DC. Results the reference to the found
@@ -2519,6 +2526,11 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
25192526
if (isa<TapExpr>(E))
25202527
return Action::SkipChildren(E);
25212528

2529+
// Don't walk into SingleValueStmtExprs, they should be type-checked as
2530+
// a whole.
2531+
if (isa<SingleValueStmtExpr>(E))
2532+
return Action::SkipChildren(E);
2533+
25222534
if (auto closure = dyn_cast<ClosureExpr>(E)) {
25232535
// NOTE: When a client wants to type check a closure signature, it
25242536
// requests with closure's 'getLoc()' location.
@@ -2599,11 +2611,6 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
25992611
// thus the transform couldn't be applied. Perform code completion
26002612
// pretending there was no result builder to recover.
26012613
}
2602-
} else if (func->hasSingleExpressionBody() &&
2603-
func->getResultInterfaceType()->isVoid()) {
2604-
// The function returns void. We don't need an explicit return, no matter
2605-
// what the type of the expression is. Take the inserted return back out.
2606-
func->getBody()->setLastElement(func->getSingleExpressionBody());
26072614
}
26082615
}
26092616

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t
3+
4+
enum E {
5+
case e
6+
case f(Int)
7+
}
8+
9+
func ifExprDotReturn() -> E {
10+
if .random() {
11+
.#^DOT1?check=DOT^#
12+
} else {
13+
.e
14+
}
15+
}
16+
17+
func switchExprDotReturn() -> E {
18+
switch Bool.random() {
19+
case true:
20+
.e
21+
case false:
22+
.#^DOT2?check=DOT^#
23+
}
24+
}
25+
26+
func ifExprDotClosureReturn() -> E {
27+
let x: E = {
28+
if .random() {
29+
.e
30+
} else {
31+
.#^DOT3?check=DOT^#
32+
}
33+
}()
34+
return x
35+
}
36+
37+
func switchExprDotClosureReturn() -> E {
38+
let x: E = {
39+
switch Bool.random() {
40+
case true:
41+
.#^DOT4?check=DOT^#
42+
case false:
43+
.e
44+
}
45+
}()
46+
return x
47+
}
48+
49+
func ifExprBranchInferenceReturn1() -> E {
50+
let fn = {
51+
if .random() {
52+
E.e
53+
} else {
54+
.#^DOT5?check=DOT^#
55+
}
56+
}
57+
return fn()
58+
}
59+
60+
func switchExprBranchInferenceReturn1() -> E {
61+
let fn = {
62+
switch Bool.random() {
63+
case true:
64+
E.e
65+
case false:
66+
.#^DOT6?check=DOT^#
67+
}
68+
}
69+
return fn()
70+
}
71+
72+
func ifExprBranchInferenceReturn2() -> E {
73+
let fn = {
74+
if .random() {
75+
.#^DOT7?check=DOT^#
76+
} else {
77+
E.e
78+
}
79+
}
80+
return fn()
81+
}
82+
83+
func switchExprBranchInferenceReturn2() -> E {
84+
let fn = {
85+
switch Bool.random() {
86+
case true:
87+
.#^DOT8?check=DOT^#
88+
case false:
89+
E.e
90+
}
91+
}
92+
return fn()
93+
}
94+
95+
func ifExprBinding() -> E {
96+
let x: E =
97+
if .random() {
98+
.e
99+
} else {
100+
.#^DOT9?check=DOT^#
101+
}
102+
return x
103+
}
104+
105+
func switchExprBinding() -> E {
106+
let x: E =
107+
switch Bool.random() {
108+
case true:
109+
.e
110+
case false:
111+
.#^DOT10?check=DOT^#
112+
}
113+
return x
114+
}
115+
116+
// DOT: Begin completions, 2 items
117+
// DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: e[#E#]; name=e
118+
// DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: f({#Int#})[#E#]; name=f()

0 commit comments

Comments
 (0)