Skip to content

Commit 1e4dbc7

Browse files
committed
Return a keyPathInParent for syntax collections
Previously, `keyPathInParent` returned `nil` for nodes that occurred in `SyntaxCollection`s. Return a subscript-based key path instead. rdar://111944659
1 parent e990c2a commit 1e4dbc7

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

Sources/SwiftSyntax/Syntax.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public enum SyntaxNodeStructure {
2020
/// The node contains a fixed number of children which can be accessed by these key paths.
2121
case layout([AnyKeyPath])
2222

23-
/// The node is a `SyntaxCollection` of the given type.
23+
/// The node is a `SyntaxCollection` with elements of the given type.
2424
case collection(SyntaxProtocol.Type)
2525

2626
/// The node can contain a single node with one of the listed types.
@@ -299,10 +299,14 @@ public extension SyntaxProtocol {
299299
guard let parent = self.parent else {
300300
return nil
301301
}
302-
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
303-
return nil
302+
switch parent.kind.syntaxNodeType.structure {
303+
case .layout(let childrenKeyPaths):
304+
return childrenKeyPaths[data.indexInParent]
305+
case .collection(_):
306+
return (parent.asProtocol(SyntaxProtocol.self) as! any SyntaxCollection).keyPath(for: self.indexInParent)
307+
case .choices:
308+
preconditionFailure("The parent of a syntax node should always be a concrete node and not one with choices")
304309
}
305-
return childrenKeyPaths[data.indexInParent]
306310
}
307311

308312
@available(*, deprecated, message: "Use previousToken(viewMode:) instead")
@@ -823,3 +827,12 @@ extension ReversedTokenSequence: CustomReflectable {
823827
/// replaced by the ``Syntax`` type.
824828
@available(*, unavailable, message: "use 'Syntax' instead")
825829
public struct SyntaxNode {}
830+
831+
extension SyntaxCollection {
832+
/// Implementation detail of ``SyntaxProtocol/keyPathInParent``.
833+
///
834+
/// I couldn't find a way to express this without an extension on ``SyntaxCollection``.
835+
fileprivate func keyPath(for index: SyntaxChildrenIndex) -> AnyKeyPath {
836+
return \Self[index]
837+
}
838+
}

Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,17 @@ public class SyntaxCollectionsTests: XCTestCase {
146146
XCTAssertEqual("\(relems[1])", "1")
147147
XCTAssertEqual("\(relems[0])", "2")
148148
}
149+
150+
public func testKeyPathInParent() throws {
151+
let arrayElementList = ArrayElementListSyntax([
152+
integerLiteralElement(0),
153+
integerLiteralElement(1),
154+
integerLiteralElement(2),
155+
])
156+
157+
let element = arrayElementList[1]
158+
let keyPath = try XCTUnwrap(element.keyPathInParent)
159+
XCTAssert(type(of: keyPath).rootType == ArrayElementListSyntax.self)
160+
XCTAssert(type(of: keyPath).valueType == ArrayElementSyntax.self)
161+
}
149162
}

0 commit comments

Comments
 (0)