Skip to content

Commit ff895d2

Browse files
committed
[CodeCompletion] Fix completion in string literal interpolation at top-level
Mostly this was just returning the ParserStatus bits that we got from parseExprList from parseExprStringLiteral. The rest was just cleaning up places that didn't handle EOF very well, which is important here because the code completion token is buried in the string literal, so the primary lexer will walk past it. rdar://problem/17101944
1 parent 280a514 commit ff895d2

File tree

5 files changed

+33
-13
lines changed

5 files changed

+33
-13
lines changed

include/swift/Parse/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ class Parser {
11101110
ParserResult<Expr> parseExprSelector();
11111111
ParserResult<Expr> parseExprSuper();
11121112
ParserResult<Expr> parseExprConfiguration();
1113-
Expr *parseExprStringLiteral();
1113+
ParserResult<Expr> parseExprStringLiteral();
11141114

11151115
/// If the token is an escaped identifier being used as an argument
11161116
/// label, but doesn't need to be, diagnose it.

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1829,7 +1829,7 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition,
18291829
backtrackToPosition(BeginParserPosition);
18301830
SourceLoc BeginLoc = Tok.getLoc();
18311831
// Consume tokens up to code completion token.
1832-
while (Tok.isNot(tok::code_complete))
1832+
while (Tok.isNot(tok::code_complete, tok::eof))
18331833
consumeToken();
18341834

18351835
// Consume the code completion token, if there is one.

lib/Parse/ParseExpr.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
865865
SWIFT_FALLTHROUGH;
866866

867867
case tok::string_literal: // "foo"
868-
Result = makeParserResult(parseExprStringLiteral());
868+
Result = parseExprStringLiteral();
869869
break;
870870

871871
case tok::kw_nil:
@@ -1383,18 +1383,19 @@ createStringLiteralExprFromSegment(ASTContext &Ctx,
13831383

13841384
/// expr-literal:
13851385
/// string_literal
1386-
Expr *Parser::parseExprStringLiteral() {
1386+
ParserResult<Expr> Parser::parseExprStringLiteral() {
13871387
SmallVector<Lexer::StringSegment, 1> Segments;
13881388
L->getStringLiteralSegments(Tok, Segments);
13891389
SourceLoc Loc = consumeToken();
13901390

13911391
// The simple case: just a single literal segment.
13921392
if (Segments.size() == 1 &&
13931393
Segments.front().Kind == Lexer::StringSegment::Literal) {
1394-
return createStringLiteralExprFromSegment(Context, L, Segments.front(),
1395-
Loc);
1394+
return makeParserResult(
1395+
createStringLiteralExprFromSegment(Context, L, Segments.front(), Loc));
13961396
}
1397-
1397+
1398+
ParserStatus Status;
13981399
SmallVector<Expr*, 4> Exprs;
13991400
bool First = true;
14001401
for (auto Segment : Segments) {
@@ -1436,6 +1437,7 @@ Expr *Parser::parseExprStringLiteral() {
14361437
assert(Tok.is(tok::l_paren));
14371438

14381439
ParserResult<Expr> E = parseExprList(tok::l_paren, tok::r_paren);
1440+
Status |= E;
14391441
if (E.isNonNull()) {
14401442
Exprs.push_back(E.get());
14411443

@@ -1449,11 +1451,12 @@ Expr *Parser::parseExprStringLiteral() {
14491451
First = false;
14501452
}
14511453

1452-
if (Exprs.empty())
1453-
return new (Context) ErrorExpr(Loc);
1454+
if (Exprs.empty()) {
1455+
Status.setIsParseError();
1456+
return makeParserResult(Status, new (Context) ErrorExpr(Loc));
1457+
}
14541458

1455-
return new (Context) InterpolatedStringLiteralExpr(Loc,
1456-
Context.AllocateCopy(Exprs));
1459+
return makeParserResult(Status, new (Context) InterpolatedStringLiteralExpr(Loc, Context.AllocateCopy(Exprs)));
14571460
}
14581461

14591462
void Parser::diagnoseEscapedArgumentLabel(const Token &tok) {

lib/Parse/ParseStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition,
168168
backtrackToPosition(BeginParserPosition);
169169
SourceLoc BeginLoc = Tok.getLoc();
170170
// Consume tokens up to code completion token.
171-
while (Tok.isNot(tok::code_complete)) {
171+
while (Tok.isNot(tok::code_complete, tok::eof)) {
172172
consumeToken();
173173
}
174174
// Consume the code completion token, if there is one.

test/IDE/complete_at_top_level.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@
118118
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_GUARD_1 | FileCheck %s -check-prefix=TOP_LEVEL_GUARD
119119
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_GUARD_2 | FileCheck %s -check-prefix=TOP_LEVEL_GUARD
120120

121+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERP_1 | FileCheck %s -check-prefix=STRING_INTERP
122+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERP_2 | FileCheck %s -check-prefix=STRING_INTERP
123+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERP_3 | FileCheck %s -check-prefix=STRING_INTERP
124+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRING_INTERP_4 | FileCheck %s -check-prefix=STRING_INTERP
125+
121126
// Test code completion in top-level code.
122127
//
123128
// This test is not meant to test that we can correctly form all kinds of
@@ -420,7 +425,19 @@ func interstitial() {}
420425
// TOP_LEVEL_GUARD: Decl[LocalVar]/Local: guardedName[#Int#]; name=guardedName
421426

422427
func resyncParserB14() {}
423-
428+
429+
430+
"\(#^STRING_INTERP_1^#)"
431+
"\(1) \(#^STRING_INTERP_2^#) \(2)"
432+
var stringInterp = "\(#^STRING_INTERP_3^#)"
433+
_ = "" + "\(#^STRING_INTERP_4^#)" + ""
434+
// STRING_INTERP: Begin completions
435+
// STRING_INTERP-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#];
436+
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule: fooFunc1()[#Void#];
437+
// STRING_INTERP-DAG: Decl[GlobalVar]/Local: fooObject[#FooStruct#];
438+
// STRING_INTERP: End completions
439+
func resyncParserC1() {}
440+
424441

425442
//
426443
//===--- DON'T ADD ANY TESTS AFTER THIS LINE.

0 commit comments

Comments
 (0)