Skip to content

Commit 3d06f0d

Browse files
committed
[CodeComplete] Improve ExpectsNonVoid computation
If we know we have a Void expected type, don't set `ExpectsNonVoid` to `true`.
1 parent 0cc5297 commit 3d06f0d

File tree

2 files changed

+82
-14
lines changed

2 files changed

+82
-14
lines changed

lib/IDE/PostfixCompletion.cpp

+18-14
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,27 @@ void PostfixCompletionCallback::sawSolutionImpl(
189189

190190
bool BaseIsStaticMetaType = S.isStaticallyDerivedMetatype(ParsedExpr);
191191

192+
bool ExpectsNonVoid = false;
192193
SmallVector<Type, 4> ExpectedTypes;
193194
if (ExpectedTy) {
194195
ExpectedTypes.push_back(ExpectedTy);
195-
}
196-
197-
bool ExpectsNonVoid = false;
198-
ExpectsNonVoid |= ExpectedTy && !ExpectedTy->isVoid();
199-
ExpectsNonVoid |=
200-
!ParentExpr && CS.getContextualTypePurpose(CompletionExpr) != CTP_Unused;
201-
202-
for (auto SAT : S.targets) {
203-
if (ExpectsNonVoid) {
204-
// ExpectsNonVoid is already set. No need to iterate further.
205-
break;
206-
}
207-
if (SAT.second.getAsExpr() == CompletionExpr) {
208-
ExpectsNonVoid |= SAT.second.getExprContextualTypePurpose() != CTP_Unused;
196+
ExpectsNonVoid = !ExpectedTy->isVoid();
197+
} else {
198+
// If we don't know what the expected type is, assume it must be non-Void
199+
// if we have a contextual type that is not unused. This prevents us from
200+
// suggesting Void values for e.g bindings without explicit types.
201+
ExpectsNonVoid |= !ParentExpr &&
202+
CS.getContextualTypePurpose(CompletionExpr) != CTP_Unused;
203+
204+
for (auto SAT : S.targets) {
205+
if (ExpectsNonVoid) {
206+
// ExpectsNonVoid is already set. No need to iterate further.
207+
break;
208+
}
209+
if (SAT.second.getAsExpr() == CompletionExpr) {
210+
ExpectsNonVoid |=
211+
SAT.second.getExprContextualTypePurpose() != CTP_Unused;
212+
}
209213
}
210214
}
211215

test/IDE/complete_single_expression_return.swift

+64
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,55 @@ struct TestSingleExprClosureGlobal {
128128
// TestSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
129129
}
130130

131+
struct TestSingleExprClosureBinding {
132+
func void() -> Void {}
133+
func str() -> String { return "" }
134+
func int() -> Int { return 0 }
135+
136+
func test() -> Int {
137+
let fn = {
138+
self.#^TestSingleExprClosureBinding^#
139+
}
140+
return fn()
141+
}
142+
// Void is always valid in an implicit single expr closure.
143+
// TestSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
144+
// TestSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
145+
// TestSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
146+
}
147+
148+
struct TestExplicitSingleExprClosureBinding {
149+
func void() -> Void {}
150+
func str() -> String { return "" }
151+
func int() -> Int { return 0 }
152+
153+
func test() {
154+
let fn = {
155+
return self.#^TestExplicitSingleExprClosureBinding^#
156+
}
157+
}
158+
// FIXME: Because we have an explicit return, and no expected type, we shouldn't suggest Void.
159+
// TestExplicitSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
160+
// TestExplicitSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
161+
// TestExplicitSingleExprClosureBinding-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
162+
}
163+
164+
struct TestExplicitSingleExprClosureBindingWithContext {
165+
func void() -> Void {}
166+
func str() -> String { return "" }
167+
func int() -> Int { return 0 }
168+
169+
func test() {
170+
let fn: () -> Void = {
171+
return self.#^TestExplicitSingleExprClosureBindingWithContext^#
172+
}
173+
}
174+
// We know Void is valid.
175+
// TestExplicitSingleExprClosureBindingWithContext-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
176+
// TestExplicitSingleExprClosureBindingWithContext-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
177+
// TestExplicitSingleExprClosureBindingWithContext-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: void()[#Void#];
178+
}
179+
131180
struct TestNonSingleExprClosureGlobal {
132181
func void() -> Void {}
133182
func str() -> String { return "" }
@@ -190,6 +239,21 @@ struct TestSingleExprFunc {
190239
// TestSingleExprFunc-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
191240
}
192241

242+
struct TestSingleExprFuncReturnVoid {
243+
func void() -> Void {}
244+
func str() -> String { return "" }
245+
func int() -> Int { return 0 }
246+
247+
func test() {
248+
return self.#^TestSingleExprFuncReturnVoid^#
249+
}
250+
251+
// Void is the only possible type that can be used here.
252+
// TestSingleExprFuncReturnVoid-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
253+
// TestSingleExprFuncReturnVoid-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
254+
// TestSingleExprFuncReturnVoid-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: void()[#Void#];
255+
}
256+
193257
struct TestSingleExprFuncUnresolved {
194258
enum MyEnum { case myEnum }
195259
enum NotMine { case notMine }

0 commit comments

Comments
 (0)