Skip to content

Commit 7a80547

Browse files
committed
[code-completion] Fix compiler crash on implicit member expression in string interpolation
Resetting Lexer to the start of the line result re-tokenizning whole string literal as a single token, and go past artificial EOF. This used to end up with assertion failure or infinite-loop in no-assertion builds. Quick fix for: rdar://problem/36881302
1 parent a7ff0da commit 7a80547

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

lib/Parse/ParseExpr.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,20 +1632,25 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
16321632
Context.getIdentifier("_"), /*implicit=*/false);
16331633
auto Result = makeParserResult(Expr);
16341634
if (CodeCompletion) {
1635-
std::vector<StringRef> Identifiers;
1636-
1637-
// Move lexer to the start of the current line.
1638-
L->backtrackToState(L->getStateForBeginningOfTokenLoc(
1639-
L->getLocForStartOfLine(SourceMgr, Tok.getLoc())));
16401635

1636+
// FIXME: Code-completion should be able to find the contextual type
1637+
// from AST.
1638+
std::vector<StringRef> Identifiers;
16411639
bool HasReturn = false;
1642-
1643-
// Until we see the code completion token, collect identifiers.
1644-
for (L->lex(Tok); !Tok.is(tok::code_complete); consumeToken()) {
1645-
if (!HasReturn)
1646-
HasReturn = Tok.is(tok::kw_return);
1647-
if (Tok.is(tok::identifier)) {
1648-
Identifiers.push_back(Tok.getText());
1640+
{
1641+
ParserPositionRAII PPR(*this);
1642+
// Move lexer to the start of the current line.
1643+
L->backtrackToState(L->getStateForBeginningOfTokenLoc(
1644+
L->getLocForStartOfLine(SourceMgr, Tok.getLoc())));
1645+
1646+
// Until we see the code completion token, collect identifiers.
1647+
for (L->lex(Tok); !Tok.isAny(tok::code_complete, tok::eof);
1648+
consumeTokenWithoutFeedingReceiver()) {
1649+
if (!HasReturn)
1650+
HasReturn = Tok.is(tok::kw_return);
1651+
if (Tok.is(tok::identifier)) {
1652+
Identifiers.push_back(Tok.getText());
1653+
}
16491654
}
16501655
}
16511656
CodeCompletion->completeUnresolvedMember(Expr, Identifiers, HasReturn);

test/IDE/complete_unresolved_members.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NON_OPT_SET_2 | %FileCheck %s -check-prefix=NON_OPT_SET_1
5151
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NON_OPT_SET_3 | %FileCheck %s -check-prefix=NON_OPT_SET_1
5252

53+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERPOLATION_1 | %FileCheck %s -check-prefix=STRING_INTERPOLATION_1
54+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERPOLATION_INVALID
5355
enum SomeEnum1 {
5456
case South
5557
case North
@@ -366,3 +368,14 @@ func testNonOptSet() {
366368
func testNonOptSet() -> NonOptSet {
367369
return .#^NON_OPT_SET_3^#
368370
}
371+
372+
func testInStringInterpolation() {
373+
enum MyEnum { case foo, bar }
374+
func takeEnum(_ e: MyEnum) -> MyEnum { return e }
375+
let x = "enum: \(takeEnum(.#^STRING_INTERPOLATION_1^#))"
376+
let y = "enum: \(.#^STRING_INTERPOLATION_INVALID^#)" // Dont'crash.
377+
}
378+
// STRING_INTERPOLATION_1: Begin completions
379+
// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: foo[#MyEnum#];
380+
// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: bar[#MyEnum#];
381+
// STRING_INTERPOLATION_1: End completions

0 commit comments

Comments
 (0)