Skip to content

Commit b443118

Browse files
authored
Merge pull request #37709 from ahoppen/pr/fallback-typecheck-result-builder
[CodeComplete] Fall back to typechecking a single expression result builder doesn't typecheck
2 parents dd543e6 + 583ed8d commit b443118

File tree

2 files changed

+196
-5
lines changed

2 files changed

+196
-5
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,11 +1922,18 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator,
19221922
if (Type builderType = getResultBuilderType(func)) {
19231923
auto optBody =
19241924
TypeChecker::applyResultBuilderBodyTransform(func, builderType);
1925-
if (!optBody || !*optBody)
1926-
return true;
1927-
// Wire up the function body now.
1928-
func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked);
1929-
return false;
1925+
if (optBody && *optBody) {
1926+
// Wire up the function body now.
1927+
func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked);
1928+
return false;
1929+
}
1930+
// FIXME: We failed to apply the result builder transform. Fall back to
1931+
// just type checking the node that contains the code completion token.
1932+
// This may be missing some context from the result builder but in
1933+
// practice it often contains sufficient information to provide a decent
1934+
// level of code completion that's better than providing nothing at all.
1935+
// The proper solution would be to only partially type check the result
1936+
// builder so that this fall back would not be necessary.
19301937
} else if (func->hasSingleExpressionBody() &&
19311938
func->getResultInterfaceType()->isVoid()) {
19321939
// The function returns void. We don't need an explicit return, no matter
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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+
@resultBuilder
5+
struct TupleBuilder<T> {
6+
static func buildBlock() -> () { }
7+
8+
static func buildBlock(_ t1: T) -> T {
9+
return t1
10+
}
11+
12+
static func buildBlock(_ t1: T, _ t2: T) -> (T, T) {
13+
return (t1, t2)
14+
}
15+
static func buildBlock(_ t1: T, _ t2: T, _ t3: T) -> (T, T, T) {
16+
return (t1, t2, t3)
17+
}
18+
}
19+
20+
func buildStringTuple<Result>(@TupleBuilder<String> x: () -> Result) {}
21+
22+
enum StringFactory {
23+
static func makeString(x: String) -> String { return x }
24+
}
25+
26+
enum Letters {
27+
case a
28+
case b
29+
case c
30+
}
31+
32+
let MyConstantString = "MyConstant"
33+
let MyConstantBool = true
34+
35+
func testGlobalLookup() {
36+
@TupleBuilder<String> var x1 {
37+
#^GLOBAL_LOOKUP^#
38+
// GLOBAL_LOOKUP: Begin completions
39+
// GLOBAL_LOOKUP: Decl[GlobalVar]/CurrModule: MyConstantString[#String#];
40+
// GLOBAL_LOOKUP: End completions
41+
}
42+
43+
@TupleBuilder<String> var x2 {
44+
if true {
45+
#^GLOBAL_LOOKUP_IN_IF_BODY?check=GLOBAL_LOOKUP^#
46+
}
47+
}
48+
49+
@TupleBuilder<String> var x3 {
50+
if {
51+
#^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^#
52+
}
53+
}
54+
55+
@TupleBuilder<String> var x4 {
56+
guard else {
57+
#^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^#
58+
}
59+
}
60+
61+
@TupleBuilder<String> var x5 {
62+
"hello: \(#^GLOBAL_LOOKUP_IN_STRING_LITERAL^#)"
63+
// GLOBAL_LOOKUP_IN_STRING_LITERAL: Begin completions
64+
// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule/TypeRelation[Convertible]: MyConstantString[#String#];
65+
// GLOBAL_LOOKUP_IN_STRING_LITERAL: End completions
66+
}
67+
68+
@TupleBuilder<String> var x5 {
69+
if #^GLOBAL_LOOKUP_IN_IF_CONDITION^# {
70+
// GLOBAL_LOOKUP_IN_IF_CONDITION: Begin completions
71+
// GLOBAL_LOOKUP_IN_IF_CONDITION: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: MyConstantBool[#Bool#]; name=MyConstantBool
72+
// GLOBAL_LOOKUP_IN_IF_CONDITION: End completions
73+
}
74+
}
75+
}
76+
77+
func testStaticMemberLookup() {
78+
@TupleBuilder<String> var x1 {
79+
StringFactory.#^COMPLETE_STATIC_MEMBER^#
80+
// COMPLETE_STATIC_MEMBER: Begin completions
81+
// COMPLETE_STATIC_MEMBER: Decl[StaticMethod]/CurrNominal: makeString({#x: String#})[#String#];
82+
// COMPLETE_STATIC_MEMBER: End completions
83+
}
84+
85+
@TupleBuilder<String> var x2 {
86+
if true {
87+
StringFactory.#^COMPLETE_STATIC_MEMBER_IN_IF_BODY?check=COMPLETE_STATIC_MEMBER^#
88+
}
89+
}
90+
91+
@TupleBuilder<String> var x3 {
92+
"hello: \(StringFactory.#^COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL?check=COMPLETE_STATIC_MEMBER;xfail=rdar78015646^#)"
93+
}
94+
}
95+
96+
struct FooStruct {
97+
var instanceVar : Int
98+
init(_: Int = 0) { }
99+
func boolGen() -> Bool { return false }
100+
func intGen() -> Int { return 1 }
101+
}
102+
103+
func testPatternMatching() {
104+
@TupleBuilder<String> var x1 {
105+
let x = Letters.b
106+
if case .#^COMPLETE_PATTERN_MATCHING_IN_IF?check=COMPLETE_CASE^# = x {
107+
// COMPLETE_CASE: Begin completions
108+
// COMPLETE_CASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: a[#Letters#];
109+
// COMPLETE_CASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: b[#Letters#];
110+
// COMPLETE_CASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: c[#Letters#];
111+
// COMPLETE_CASE: End completions
112+
}
113+
}
114+
115+
@TupleBuilder<String> var x2 {
116+
let x = Letters.a
117+
switch x {
118+
case .#^COMPLETE_CASE_IN_SWITCH?check=COMPLETE_CASE^#:
119+
break
120+
}
121+
}
122+
123+
@TupleBuilder<String> var x3 {
124+
let x: FooStruct? = FooStruct()
125+
guard case .#^GUARD_CASE_PATTERN_1?check=OPTIONAL_FOOSTRUCT^# = x {}
126+
// OPTIONAL_FOOSTRUCT: Begin completions, 9 items
127+
// OPTIONAL_FOOSTRUCT-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#FooStruct?#]; name=nil
128+
// OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional<FooStruct>#]; name=none
129+
// OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#FooStruct#})[#Optional<FooStruct>#]; name=some(FooStruct)
130+
// FIXME: 'FooStruct' members should not be shown.
131+
// OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#FooStruct#]; name=init()
132+
// OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init({#Int#})[#FooStruct#]; name=init(Int)
133+
// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: boolGen({#(self): FooStruct#})[#() -> Bool#]; name=boolGen(self: FooStruct)
134+
// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: intGen({#(self): FooStruct#})[#() -> Int#]; name=intGen(self: FooStruct)
135+
// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional<FooStruct>#})[#((FooStruct) throws -> U) -> U?#]; name=map(self: Optional<FooStruct>)
136+
// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional<FooStruct>#})[#((FooStruct) throws -> U?) -> U?#]; name=flatMap(self: Optional<FooStruct>)
137+
// OPTIONAL_FOOSTRUCT-NOT: init({#(some):
138+
// OPTIONAL_FOOSTRUCT-NOT: init({#nilLiteral:
139+
// OPTIONAL_FOOSTRUCT: End completions
140+
}
141+
142+
@TupleBuilder<String> var x4 {
143+
let x: FooStruct? = FooStruct()
144+
guard case .#^GUARD_CASE_PATTERN_2?check=OPTIONAL_FOOSTRUCT^#some() = x {}
145+
}
146+
}
147+
148+
func testCompleteFunctionArgumentLabels() {
149+
@TupleBuilder<String> var x1 {
150+
StringFactory.makeString(#^FUNCTION_ARGUMENT_LABEL^#)
151+
// FUNCTION_ARGUMENT_LABEL: Begin completions, 1 item
152+
// FUNCTION_ARGUMENT_LABEL: Decl[StaticMethod]/CurrNominal: ['(']{#x: String#}[')'][#String#];
153+
// FUNCTION_ARGUMENT_LABEL: End completions
154+
}
155+
}
156+
157+
func testCompleteFunctionArgument() {
158+
@TupleBuilder<String> var x1 {
159+
StringFactory.makeString(x: #^ARGUMENT_LOOKUP^#)
160+
// ARGUMENT_LOOKUP: Begin completions
161+
// ARGUMENT_LOOKUP: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: MyConstantString[#String#];
162+
// ARGUMENT_LOOKUP: End completions
163+
}
164+
165+
@TupleBuilder<String> var x2 {
166+
if true {
167+
StringFactory.makeString(x: #^ARGUMENT_LOOKUP_IN_IF_BODY?check=ARGUMENT_LOOKUP^#)
168+
}
169+
}
170+
}
171+
172+
func testCompleteErrorTypeInCatch() {
173+
enum Error4 : Error {
174+
case E1
175+
case E2(Int32)
176+
}
177+
@TupleBuilder<String> var x1 {
178+
do {} catch Error4.#^CATCH2^#
179+
}
180+
// CATCH2: Begin completions
181+
// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]; name=E1
182+
// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]; name=E2(Int32)
183+
// CATCH2: End completions
184+
}

0 commit comments

Comments
 (0)