Skip to content

Commit 94a9876

Browse files
committed
[IDE] Show completion results if member is followed by trailing closure
When completing `items.#^COMPLETE^# { $0.content }`, we are stopping parsing of the expression after the code completion token and are thus left with `{ $0.content }` in `parseDeclVar`, which interprets the `{` as the start of an accessor clause, wrapping the entire variable in an accessor decl and thus causing code completion to not show any results. To avoid this issue, consume any postfix expressions after the code completion token and discard them. This assures that the trailing closure gets consumed before it can be interpreted as an accessor decl. Fixes rdar://77259087 [SR-14544]
1 parent 38a8b00 commit 94a9876

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

lib/Parse/ParseExpr.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,19 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
11701170
CodeCompletion->completeDotExpr(CCExpr, /*DotLoc=*/TokLoc);
11711171
}
11721172
consumeToken(tok::code_complete);
1173+
1174+
// Parse and discard remaining suffixes.
1175+
// e.g.
1176+
// closureReceiver {
1177+
// baseExpr.<complete> { $0 }
1178+
// }
1179+
// In this case, we want to consume the trailing closure because
1180+
// otherwise it will get parsed as a get-set clause on a variable
1181+
// declared by `baseExpr.<complete>` which is complete garbage.
1182+
bool hasBindOptional = false;
1183+
parseExprPostfixSuffix(makeParserResult(CCExpr), isExprBasic,
1184+
periodHasKeyPathBehavior, hasBindOptional);
1185+
11731186
return makeParserCodeCompletionResult(CCExpr);
11741187
}
11751188

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
public struct ItemWrapper {
5+
let content: Int
6+
}
7+
8+
struct MyArray {
9+
func map(transform: (ItemWrapper) -> Int) {}
10+
}
11+
12+
func sink(receiveValue: (MyArray) -> Void) {
13+
fatalError()
14+
}
15+
16+
func foo() {
17+
sink { items in
18+
let a = items.#^COMPLETE_WITHOUT_SPACE?check=CHECK^#map{ $0.content }
19+
let b = items.#^COMPLETE_WITH_SPACE?check=CHECK^# map{ $0.content }
20+
let c = items.#^COMPLETE_WITH_SPACE_AND_PARENS?check=CHECK^# map({ $0.content })
21+
let d = items.#^COMPLETE_WITHOUT_SPACE_BUT_PARENS?check=CHECK^#map({ $0.content })
22+
let e = items.#^COMPLETE_WITHOUT_MAP?check=CHECK^# { $0.content }
23+
let f = items.#^COMPLETE_WITHOUT_MAP_BUT_PARENS?check=CHECK^# ({ $0.content })
24+
}
25+
}
26+
27+
// CHECK: Begin completions, 2 items
28+
// CHECK-DAG: Keyword[self]/CurrNominal: self[#MyArray#];
29+
// CHECK-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: map({#transform: (ItemWrapper) -> Int##(ItemWrapper) -> Int#})[#Void#];
30+
// CHECK: End completions

0 commit comments

Comments
 (0)