Skip to content

Commit acb7943

Browse files
committed
Ensure all parsed tokens are allocated in a ParsingSyntaxArena
1 parent 9fcd8f3 commit acb7943

File tree

7 files changed

+59
-51
lines changed

7 files changed

+59
-51
lines changed

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -550,16 +550,12 @@ extension RawSyntax {
550550
textRange: Range<SyntaxText.Index>,
551551
presence: SourcePresence,
552552
tokenDiagnostic: TokenDiagnostic?,
553-
arena: __shared SyntaxArena
553+
arena: __shared ParsingSyntaxArena
554554
) -> RawSyntax {
555555
assert(
556556
arena.contains(text: wholeText),
557557
"token text must be managed by the arena"
558558
)
559-
assert(
560-
arena is ParsingSyntaxArena || textRange == wholeText.indices,
561-
"arena must be able to parse trivia"
562-
)
563559
let payload = RawSyntaxData.ParsedToken(
564560
tokenKind: kind,
565561
wholeText: wholeText,

Sources/SwiftSyntax/Raw/RawSyntaxNodeProtocol.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public struct RawTokenSyntax: RawSyntaxNodeProtocol {
183183
textRange: Range<SyntaxText.Index>,
184184
presence: SourcePresence,
185185
tokenDiagnostic: TokenDiagnostic?,
186-
arena: __shared SyntaxArena
186+
arena: __shared ParsingSyntaxArena
187187
) {
188188
let raw = RawSyntax.parsedToken(
189189
kind: kind,
@@ -205,7 +205,7 @@ public struct RawTokenSyntax: RawSyntaxNodeProtocol {
205205
trailingTriviaPieces: [RawTriviaPiece] = [],
206206
presence: SourcePresence,
207207
tokenDiagnostic: TokenDiagnostic? = nil,
208-
arena: __shared SyntaxArena
208+
arena: __shared ParsingSyntaxArena
209209
) {
210210
if leadingTriviaPieces.isEmpty && trailingTriviaPieces.isEmpty {
211211
// Create it via `RawSyntax.parsedToken()`.

Sources/SwiftSyntax/Raw/RawSyntaxTokenView.swift

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public struct RawSyntaxTokenView: Sendable {
9595
public var leadingRawTriviaPieces: [RawTriviaPiece] {
9696
switch raw.rawData.payload {
9797
case .parsedToken(let dat):
98-
let arena = raw.arena.parsingArena!
98+
let arena = raw.arena as! ParsingSyntaxArena
9999
return arena.parseTrivia(source: dat.leadingTriviaText, position: .leading)
100100
case .materializedToken(let dat):
101101
return Array(dat.leadingTrivia)
@@ -108,7 +108,7 @@ public struct RawSyntaxTokenView: Sendable {
108108
public var trailingRawTriviaPieces: [RawTriviaPiece] {
109109
switch raw.rawData.payload {
110110
case .parsedToken(let dat):
111-
let arena = raw.arena.parsingArena!
111+
let arena = raw.arena as! ParsingSyntaxArena
112112
return arena.parseTrivia(source: dat.trailingTriviaText, position: .trailing)
113113
case .materializedToken(let dat):
114114
return Array(dat.trailingTrivia)
@@ -204,8 +204,21 @@ public struct RawSyntaxTokenView: Sendable {
204204
arena.addChild(self.raw.arenaReference)
205205
switch raw.rawData.payload {
206206
case .parsedToken(var payload):
207-
payload.presence = newValue
208-
return RawSyntax(arena: arena, payload: .parsedToken(payload))
207+
if arena === self.raw.arena {
208+
payload.presence = newValue
209+
return RawSyntax(arena: arena, payload: .parsedToken(payload))
210+
}
211+
// If the modified token is allocated in a different arena, it might have
212+
// a different or no `parseTrivia` function. We thus cannot use a
213+
// `parsedToken` anymore.
214+
return .makeMaterializedToken(
215+
kind: formKind(),
216+
leadingTrivia: formLeadingTrivia(),
217+
trailingTrivia: formTrailingTrivia(),
218+
presence: newValue,
219+
tokenDiagnostic: tokenDiagnostic,
220+
arena: arena
221+
)
209222
case .materializedToken(var payload):
210223
payload.presence = newValue
211224
return RawSyntax(arena: arena, payload: .materializedToken(payload))
@@ -274,8 +287,21 @@ public struct RawSyntaxTokenView: Sendable {
274287
arena.addChild(self.raw.arenaReference)
275288
switch raw.rawData.payload {
276289
case .parsedToken(var dat):
277-
dat.tokenDiagnostic = tokenDiagnostic
278-
return RawSyntax(arena: arena, payload: .parsedToken(dat))
290+
if arena === self.raw.arena {
291+
dat.tokenDiagnostic = tokenDiagnostic
292+
return RawSyntax(arena: arena, payload: .parsedToken(dat))
293+
}
294+
// If the modified token is allocated in a different arena, it might have
295+
// a different or no `parseTrivia` function. We thus cannot use a
296+
// `parsedToken` anymore.
297+
return .makeMaterializedToken(
298+
kind: formKind(),
299+
leadingTrivia: formLeadingTrivia(),
300+
trailingTrivia: formTrailingTrivia(),
301+
presence: presence,
302+
tokenDiagnostic: tokenDiagnostic,
303+
arena: arena
304+
)
279305
case .materializedToken(var dat):
280306
dat.tokenDiagnostic = tokenDiagnostic
281307
return RawSyntax(arena: arena, payload: .materializedToken(dat))

Sources/SwiftSyntax/SyntaxArena.swift

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -71,33 +71,6 @@ public class SyntaxArena {
7171
}
7272
}
7373

74-
/// If this arena or any of its child arenas is a ``ParsingSyntaxArena``
75-
/// return one of these arenas, otherwise return `nil`.
76-
///
77-
/// If the arena has multiple child nodes that are ``ParsingSyntaxArena``s, it
78-
/// is undefined which one will be returned.
79-
///
80-
/// The use case for this is to get the trivia parsing function of the arena.
81-
/// Parsed tokens created by `SwiftParser` automatically reside in a
82-
/// ``ParsingSyntaxArena`` but if they are modified (e.g. using the `with`
83-
/// functions), they might reside in a new arena. But we still want to be able
84-
/// to retrieve trivia from those modified tokens, which requires calling into
85-
/// the `parseTrivia` function of the ``ParsingSyntaxArena`` that created the
86-
/// token. Since the modified syntax arena needs to keep the original
87-
/// ``ParsingSyntaxArena`` alive, we can search this arena’s `childRefs` for
88-
/// the ``ParsingSyntaxArena`` that created the token.
89-
var parsingArena: ParsingSyntaxArena? {
90-
if let parsingArena = self as? ParsingSyntaxArena {
91-
return parsingArena
92-
}
93-
for child in childRefs {
94-
if let parsingArena = child.value.parsingArena {
95-
return parsingArena
96-
}
97-
}
98-
return nil
99-
}
100-
10174
/// Allocates a buffer of `RawSyntax?` with the given count, then returns the
10275
/// uninitialized memory range as a `UnsafeMutableBufferPointer<RawSyntax?>`.
10376
func allocateRawSyntaxBuffer(count: Int) -> UnsafeMutableBufferPointer<RawSyntax?> {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_spi(RawSyntax) import SwiftSyntax
14+
15+
/// Dummy trivia parsing function.
16+
func dummyParseToken(source: SyntaxText, position: TriviaPosition) -> [RawTriviaPiece] {
17+
// Emit a single `unexpectedText` trivia of the whole trivia text.
18+
return [.unexpectedText(source)]
19+
}

Tests/SwiftSyntaxTest/RawSyntaxTests.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@_spi(RawSyntax) import SwiftSyntax
1414
import XCTest
1515

16-
fileprivate func cannedStructDecl(arena: SyntaxArena) -> RawStructDeclSyntax {
16+
fileprivate func cannedStructDecl(arena: ParsingSyntaxArena) -> RawStructDeclSyntax {
1717
let structKW = RawTokenSyntax(
1818
kind: .keyword,
1919
text: arena.intern("struct"),
@@ -68,7 +68,7 @@ fileprivate func cannedStructDecl(arena: SyntaxArena) -> RawStructDeclSyntax {
6868
final class RawSyntaxTests: XCTestCase {
6969

7070
func testFactory() {
71-
withExtendedLifetime(SyntaxArena()) { arena in
71+
withExtendedLifetime(ParsingSyntaxArena(parseTriviaFunction: dummyParseToken)) { arena in
7272
let structDecl = cannedStructDecl(arena: arena)
7373
XCTAssertEqual(
7474
"\(structDecl.raw)",
@@ -81,7 +81,7 @@ final class RawSyntaxTests: XCTestCase {
8181
}
8282

8383
func testAccessor() {
84-
withExtendedLifetime(SyntaxArena()) { arena in
84+
withExtendedLifetime(ParsingSyntaxArena(parseTriviaFunction: dummyParseToken)) { arena in
8585
let structDecl = cannedStructDecl(arena: arena)
8686
XCTAssertEqual(structDecl.name.tokenKind, .identifier)
8787
XCTAssertEqual(structDecl.structKeyword.tokenText, "struct")
@@ -96,7 +96,7 @@ final class RawSyntaxTests: XCTestCase {
9696
}
9797

9898
func testMaterializedToken() {
99-
withExtendedLifetime(SyntaxArena()) { arena in
99+
withExtendedLifetime(ParsingSyntaxArena(parseTriviaFunction: dummyParseToken)) { arena in
100100
let ident = RawTokenSyntax(
101101
kind: .identifier,
102102
text: arena.intern("foo"),
@@ -118,12 +118,6 @@ final class RawSyntaxTests: XCTestCase {
118118
}
119119

120120
func testParsedToken() {
121-
// Dummy trivia parsing function.
122-
func dummyParseToken(source: SyntaxText, position: TriviaPosition) -> [RawTriviaPiece] {
123-
// Emit a single `unexpectedText` trivia of the whole trivia text.
124-
return [.unexpectedText(source)]
125-
}
126-
127121
withExtendedLifetime(ParsingSyntaxArena(parseTriviaFunction: dummyParseToken)) { arena in
128122
let ident = RawTokenSyntax(
129123
kind: .identifier,

Tests/SwiftSyntaxTest/SourceLocationConverterTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fileprivate func assertPresumedSourceLocation(
3636

3737
final class SourceLocationConverterTests: XCTestCase {
3838
func testInvalidUtf8() {
39-
let eofToken = withExtendedLifetime(SyntaxArena()) { arena in
39+
let eofToken = withExtendedLifetime(ParsingSyntaxArena(parseTriviaFunction: dummyParseToken)) { arena in
4040
let leadingTriviaText = [UInt8(0xfd)].withUnsafeBufferPointer { buf in
4141
arena.intern(SyntaxText(buffer: SyntaxArenaAllocatedBufferPointer(buf)))
4242
}

0 commit comments

Comments
 (0)