Skip to content

Commit aa0305e

Browse files
author
Nathan Hawes
committed
[Parse][IDE] Fix document structure request output for interpolated string literals
InterpolatedStringLiteralExpr has a TapExpr, which contains a BraceStmt containing the builder CallExprs to the builder appendInterpolation / appendStringLiteral methods used to construct the final string. This is all implementation detail, but the BraceStmt wasn't marked implicit and had a valid (but incorrect) SourceRange, so the document structure request treated it as any other BraceStmt and made a structure node for it. The invalid range ended up tripping an assertion in the document structure walker in swift-ide-test. This patch corrects the BraceStmts produced by interpolated strings to be marked implicit and have the correct range, and also updates ModelASTWalker to skip over the internal details of interpolated strings when walking them (the CallExprs were also being emitted).
1 parent 5ad5305 commit aa0305e

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

lib/IDE/SyntaxModel.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ class ModelASTWalker : public ASTWalker {
242242
const LangOptions &LangOpts;
243243
const SourceManager &SM;
244244
unsigned BufferID;
245+
ASTContext &Ctx;
245246
std::vector<StructureElement> SubStructureStack;
246247
SourceLoc LastLoc;
247248
static const std::regex &getURLRegex(StringRef Protocol);
@@ -262,6 +263,7 @@ class ModelASTWalker : public ASTWalker {
262263
LangOpts(File.getASTContext().LangOpts),
263264
SM(File.getASTContext().SourceMgr),
264265
BufferID(File.getBufferID().getValue()),
266+
Ctx(File.getASTContext()),
265267
Walker(Walker) { }
266268

267269
// FIXME: Remove this
@@ -521,13 +523,14 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
521523
SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange());
522524
pushStructureNode(SN, E);
523525
} else if (auto *Tup = dyn_cast<TupleExpr>(E)) {
526+
auto *ParentE = Parent.getAsExpr();
524527
if (isCurrentCallArgExpr(Tup)) {
525528
for (unsigned I = 0; I < Tup->getNumElements(); ++ I) {
526529
SourceLoc NameLoc = Tup->getElementNameLoc(I);
527530
if (NameLoc.isValid())
528531
passTokenNodesUntil(NameLoc, PassNodesBehavior::ExcludeNodeAtLocation);
529532
}
530-
} else {
533+
} else if (!ParentE || !isa<InterpolatedStringLiteralExpr>(ParentE)) {
531534
SyntaxStructureNode SN;
532535
SN.Kind = SyntaxStructureKind::TupleExpression;
533536
SN.Range = charSourceRangeFromSourceRange(SM, Tup->getSourceRange());
@@ -562,6 +565,18 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
562565
subExpr->walk(*this);
563566
}
564567
return { false, walkToExprPost(SE) };
568+
} else if (auto *ISL = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
569+
// Don't visit the child expressions directly. Instead visit the arguments
570+
// of each appendStringLiteral/appendInterpolation CallExpr so we don't
571+
// try to output structure nodes for those calls.
572+
llvm::SaveAndRestore<ASTWalker::ParentTy> SetParent(Parent, E);
573+
ISL->forEachSegment(Ctx, [&](bool isInterpolation, CallExpr *CE) {
574+
if (isInterpolation) {
575+
if (auto *Arg = CE->getArg())
576+
Arg->walk(*this);
577+
}
578+
});
579+
return { false, walkToExprPost(E) };
565580
}
566581

567582
return { true, E };
@@ -1166,7 +1181,8 @@ bool ModelASTWalker::shouldPassBraceStructureNode(BraceStmt *S) {
11661181
return (!dyn_cast_or_null<AbstractFunctionDecl>(Parent.getAsDecl()) &&
11671182
!dyn_cast_or_null<TopLevelCodeDecl>(Parent.getAsDecl()) &&
11681183
!dyn_cast_or_null<CaseStmt>(Parent.getAsStmt()) &&
1169-
S->getSourceRange().isValid());
1184+
S->getSourceRange().isValid() &&
1185+
!S->isImplicit());
11701186
}
11711187

11721188
ModelASTWalker::PassUntilResult

lib/Parse/ParseExpr.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,6 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
19161916

19171917
// The start location of the entire string literal.
19181918
SourceLoc Loc = Tok.getLoc();
1919-
SourceLoc EndLoc = Loc.getAdvancedLoc(Tok.getLength());
19201919

19211920
StringRef OpenDelimiterStr, OpenQuoteStr, CloseQuoteStr, CloseDelimiterStr;
19221921
unsigned DelimiterLength = Tok.getCustomDelimiterLen();
@@ -2036,8 +2035,8 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
20362035
Status = parseStringSegments(Segments, EntireTok, InterpolationVar,
20372036
Stmts, LiteralCapacity, InterpolationCount);
20382037

2039-
auto Body = BraceStmt::create(Context, Loc, Stmts, EndLoc,
2040-
/*implicit=*/false);
2038+
auto Body = BraceStmt::create(Context, Loc, Stmts, /*endLoc=*/Loc,
2039+
/*implicit=*/true);
20412040
AppendingExpr = new (Context) TapExpr(nullptr, Body);
20422041
}
20432042

test/IDE/structure.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,27 @@ enum FooEnum {
306306
}
307307
// CHECK: }</enum>
308308

309+
firstCall("\(1)", 1)
310+
// CHECK: <call><name>firstCall</name>(<arg>"\(1)"</arg>, <arg>1</arg>)</call>
311+
312+
secondCall("\(a: {struct Foo {let x = 10}; return Foo().x}())", 1)
313+
// CHECK: <call><name>secondCall</name>(<arg>"\(a: <call><name><closure><brace>{<struct>struct <name>Foo</name> {<property>let <name>x</name> = 10</property>}</struct>; return <call><name>Foo</name>()</call>.x}</brace></closure></name>()</call>)"</arg>, <arg>1</arg>)</call>
314+
315+
thirdCall("""
316+
\("""
317+
\({
318+
return a()
319+
}())
320+
""")
321+
""")
322+
// CHECK: <call><name>thirdCall</name>("""
323+
// CHECK-NEXT: \("""
324+
// CHECK-NEXT: \(<call><name><closure>{
325+
// CHECK-NEXT: return <call><name>a</name>()</call>
326+
// CHECK-NEXT: }</closure></name>()</call>)
327+
// CHECK-NEXT: """)
328+
// CHECK-NEXT: """)</call>
329+
309330
fourthCall(a: @escaping () -> Int)
310331
// CHECK: <call><name>fourthCall</name>(<arg><name>a</name>: @escaping () -> Int</arg>)</call>
332+

0 commit comments

Comments
 (0)