Skip to content

Commit 3721e45

Browse files
author
Nathan Hawes
committed
[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 that doesn't actually appear in the source code, but the document structure request didn't filter it out, resulting in bogus calls and brace ranges in its responses. Resolves rdar://problem/55183943
1 parent 11b9972 commit 3721e45

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

lib/IDE/SyntaxModel.cpp

Lines changed: 16 additions & 1 deletion
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
void visitSourceFile(SourceFile &SrcFile, ArrayRef<SyntaxNode> Tokens);
@@ -513,13 +515,14 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
513515
SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange());
514516
pushStructureNode(SN, E);
515517
} else if (auto *Tup = dyn_cast<TupleExpr>(E)) {
518+
auto *ParentE = Parent.getAsExpr();
516519
if (isCurrentCallArgExpr(Tup)) {
517520
for (unsigned I = 0; I < Tup->getNumElements(); ++ I) {
518521
SourceLoc NameLoc = Tup->getElementNameLoc(I);
519522
if (NameLoc.isValid())
520523
passTokenNodesUntil(NameLoc, PassNodesBehavior::ExcludeNodeAtLocation);
521524
}
522-
} else {
525+
} else if (!ParentE || !isa<InterpolatedStringLiteralExpr>(ParentE)) {
523526
SyntaxStructureNode SN;
524527
SN.Kind = SyntaxStructureKind::TupleExpression;
525528
SN.Range = charSourceRangeFromSourceRange(SM, Tup->getSourceRange());
@@ -554,6 +557,18 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
554557
subExpr->walk(*this);
555558
}
556559
return { false, walkToExprPost(SE) };
560+
} else if (auto *ISL = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
561+
// Don't visit the child expressions directly. Instead visit the arguments
562+
// of each appendStringLiteral/appendInterpolation CallExpr so we don't
563+
// try to output structure nodes for those calls.
564+
llvm::SaveAndRestore<ASTWalker::ParentTy> SetParent(Parent, E);
565+
ISL->forEachSegment(Ctx, [&](bool isInterpolation, CallExpr *CE) {
566+
if (isInterpolation) {
567+
if (auto *Arg = CE->getArg())
568+
Arg->walk(*this);
569+
}
570+
});
571+
return { false, walkToExprPost(E) };
557572
}
558573

559574
return { true, E };

test/IDE/structure.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,39 @@ myFunc(foo: 0,
293293
bar: baz == 0)
294294
// CHECK: <call><name>myFunc</name>(<arg><name>foo</name>: 0</arg>,
295295
// CHECK: <arg><name>bar</name>: baz == 0</arg>)</call>
296+
297+
298+
enum FooEnum {
299+
// CHECK: <enum>enum <name>FooEnum</name> {
300+
case blah(x: () -> () = {
301+
// CHECK: <enum-case>case <enum-elem><name>blah(<param><name>x</name>: <type>() -> ()</type> = <closure><brace>{
302+
@Tuples func foo(x: MyStruc) {}
303+
// CHECK: @Tuples <ffunc>func <name>foo(<param><name>x</name>: <type>MyStruc</type></param>)</name> {}</ffunc>
304+
})
305+
// CHECK: }</brace></closure></param>)</name></enum-elem></enum-case>
306+
}
307+
// CHECK: }</enum>
308+
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+
330+
fourthCall(a: @escaping () -> Int)
331+
// CHECK: <call><name>fourthCall</name>(<arg><name>a</name>: @escaping () -> Int</arg>)</call>

0 commit comments

Comments
 (0)