Skip to content

Commit 0b9c005

Browse files
authored
Merge pull request #803 from rintaro/syntaxrewriter-keeparena
Keep 'SyntaxArena's of rewritten children alive.
2 parents ca99b28 + 6c17323 commit 0b9c005

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ open class SyntaxRewriter {
199199
// nodes are being collected.
200200
var newLayout: ContiguousArray<RawSyntax?>?
201201

202+
// Rewritten children just to keep their 'SyntaxArena' alive until they are
203+
// wrapped with 'Syntax'
204+
var rewrittens: ContiguousArray<Syntax> = []
205+
202206
let syntaxNode = node._syntaxNode
203207

204208
// Incrementing i manually is faster than using .enumerated()
@@ -237,6 +241,7 @@ open class SyntaxRewriter {
237241

238242
// Now that we know we have a new layout in which we collect rewritten
239243
// nodes, add it.
244+
rewrittens.append(rewritten)
240245
newLayout!.append(rewritten.raw)
241246
} else {
242247
// The node was not changed by the rewriter. Only store it if a previous
@@ -249,13 +254,15 @@ open class SyntaxRewriter {
249254

250255
if let newLayout = newLayout {
251256
// A child node was rewritten. Build the updated node.
252-
257+
253258
// Sanity check, ensure the new children are the same length.
254259
assert(newLayout.count == node.raw.layoutView!.children.count)
255-
260+
256261
let newRaw = node.raw.layoutView!.replacingLayout(with: Array(newLayout), arena: .default)
257-
let newNode = SyntaxType(Syntax(SyntaxData.forRoot(newRaw)))!
258-
return newNode
262+
// 'withExtendedLifetime' to keep 'SyntaxArena's of them alive until here.
263+
return withExtendedLifetime(rewrittens) {
264+
SyntaxType(Syntax(SyntaxData.forRoot(newRaw)))!
265+
}
259266
} else {
260267
// No child node was rewritten. So no need to change this node as well.
261268
return node

Sources/SwiftSyntax/gyb_generated/SyntaxRewriter.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5870,6 +5870,10 @@ open class SyntaxRewriter {
58705870
// nodes are being collected.
58715871
var newLayout: ContiguousArray<RawSyntax?>?
58725872

5873+
// Rewritten children just to keep their 'SyntaxArena' alive until they are
5874+
// wrapped with 'Syntax'
5875+
var rewrittens: ContiguousArray<Syntax> = []
5876+
58735877
let syntaxNode = node._syntaxNode
58745878

58755879
// Incrementing i manually is faster than using .enumerated()
@@ -5908,6 +5912,7 @@ open class SyntaxRewriter {
59085912

59095913
// Now that we know we have a new layout in which we collect rewritten
59105914
// nodes, add it.
5915+
rewrittens.append(rewritten)
59115916
newLayout!.append(rewritten.raw)
59125917
} else {
59135918
// The node was not changed by the rewriter. Only store it if a previous
@@ -5920,13 +5925,15 @@ open class SyntaxRewriter {
59205925

59215926
if let newLayout = newLayout {
59225927
// A child node was rewritten. Build the updated node.
5923-
5928+
59245929
// Sanity check, ensure the new children are the same length.
59255930
assert(newLayout.count == node.raw.layoutView!.children.count)
5926-
5931+
59275932
let newRaw = node.raw.layoutView!.replacingLayout(with: Array(newLayout), arena: .default)
5928-
let newNode = SyntaxType(Syntax(SyntaxData.forRoot(newRaw)))!
5929-
return newNode
5933+
// 'withExtendedLifetime' to keep 'SyntaxArena's of them alive until here.
5934+
return withExtendedLifetime(rewrittens) {
5935+
SyntaxType(Syntax(SyntaxData.forRoot(newRaw)))!
5936+
}
59305937
} else {
59315938
// No child node was rewritten. So no need to change this node as well.
59325939
return node

Tests/SwiftParserTest/StringInterpolation.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,26 @@ final class StringInterpolationTests: XCTestCase {
6464
"""
6565
XCTAssertTrue(structNode.is(StructDeclSyntax.self))
6666
}
67+
68+
func testRewriter() throws {
69+
let sourceFile = try Parser.parse(source: """
70+
class Foo {
71+
func method() {}
72+
}
73+
""")
74+
class Rewriter: SyntaxRewriter {
75+
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
76+
let newFunc = DeclSyntax("func newName() {}")
77+
.withLeadingTrivia(node.leadingTrivia!)
78+
.withTrailingTrivia(node.trailingTrivia!)
79+
return DeclSyntax(newFunc)
80+
}
81+
}
82+
let rewrittenSourceFile = Rewriter().visit(sourceFile)
83+
XCTAssertEqual(rewrittenSourceFile.description, """
84+
class Foo {
85+
func newName() {}
86+
}
87+
""")
88+
}
6789
}

0 commit comments

Comments
 (0)