Skip to content

Commit 66d387d

Browse files
committed
Remove indexInParent from SyntaxProtocol
Clients should never need to worry about a node’s index within the parent, especially because `indexInParent` will return the index while also including `nil` nodes and that’s a view of the tree you cannot get anywhere.
1 parent a962805 commit 66d387d

File tree

10 files changed

+81
-156
lines changed

10 files changed

+81
-156
lines changed

CodeGeneration/Sources/SyntaxSupport/Classification.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,15 @@ public class SyntaxClassification {
2828
public class ChildClassification {
2929
public let parent: Node
3030
public let childIndex: Int
31-
public let isToken: Bool
32-
public let classification: SyntaxClassification?
33-
public let force: Bool
31+
public let child: Child
32+
public var isToken: Bool { child.isToken }
33+
public var classification: SyntaxClassification? { child.classification }
34+
public var force: Bool { child.forceClassification }
3435

3536
public init(node: Node, childIndex: Int, child: Child) {
3637
self.parent = node
3738
self.childIndex = childIndex
38-
self.isToken = child.syntaxKind.hasSuffix("Token")
39-
self.classification = child.classification
40-
self.force = child.forceClassification
39+
self.child = child
4140
}
4241
}
4342

CodeGeneration/Sources/generate-swiftsyntax/templates/basicformat/BasicFormatFile.swift

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
6262
DeclSyntax(
6363
"""
6464
open override func visitPre(_ node: Syntax) {
65-
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
65+
if let keyPath = node.keyPathInParent, shouldIndent(keyPath) {
6666
indentationLevel += 1
6767
}
6868
if let parent = node.parent, childrenSeparatedByNewline(parent) {
@@ -74,7 +74,7 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
7474
DeclSyntax(
7575
"""
7676
open override func visitPost(_ node: Syntax) {
77-
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
77+
if let keyPath = node.keyPathInParent, shouldIndent(keyPath) {
7878
indentationLevel -= 1
7979
}
8080
}
@@ -92,7 +92,7 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
9292
if requiresTrailingSpace(node) && trailingTrivia.isEmpty {
9393
trailingTrivia += .space
9494
}
95-
if let keyPath = getKeyPath(Syntax(node)), requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false), !shouldOmitNewline(node) {
95+
if let keyPath = node.keyPathInParent, requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false), !shouldOmitNewline(node) {
9696
leadingTrivia = .newline + leadingTrivia
9797
}
9898
var isOnNewline: Bool = (lastRewrittenToken?.trailingTrivia.pieces.last?.isNewline == true)
@@ -206,7 +206,7 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
206206
try FunctionDeclSyntax("open func requiresLeadingSpace(_ token: TokenSyntax) -> Bool") {
207207
StmtSyntax(
208208
"""
209-
if let keyPath = getKeyPath(token), let requiresLeadingSpace = requiresLeadingSpace(keyPath) {
209+
if let keyPath = token.keyPathInParent, let requiresLeadingSpace = requiresLeadingSpace(keyPath) {
210210
return requiresLeadingSpace
211211
}
212212
"""
@@ -268,7 +268,7 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
268268
try FunctionDeclSyntax("open func requiresTrailingSpace(_ token: TokenSyntax) -> Bool") {
269269
StmtSyntax(
270270
"""
271-
if let keyPath = getKeyPath(token), let requiresTrailingSpace = requiresTrailingSpace(keyPath) {
271+
if let keyPath = token.keyPathInParent, let requiresTrailingSpace = requiresTrailingSpace(keyPath) {
272272
return requiresTrailingSpace
273273
}
274274
"""
@@ -311,19 +311,5 @@ let basicFormatFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
311311
}
312312
}
313313
}
314-
315-
DeclSyntax(
316-
"""
317-
private func getKeyPath<T: SyntaxProtocol>(_ node: T) -> AnyKeyPath? {
318-
guard let parent = node.parent else {
319-
return nil
320-
}
321-
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
322-
return nil
323-
}
324-
return childrenKeyPaths[node.indexInParent]
325-
}
326-
"""
327-
)
328314
}
329315
}

CodeGeneration/Sources/generate-swiftsyntax/templates/ideutils/SyntaxClassificationFile.swift

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,35 +53,17 @@ let syntaxClassificationFile = SourceFileSyntax(leadingTrivia: copyrightHeader)
5353
/// - childKind: The node syntax kind.
5454
/// - Returns: A pair of classification and whether it is "forced", or nil if
5555
/// no classification is attached.
56-
internal static func classify(
57-
parentKind: SyntaxKind, indexInParent: Int, childKind: SyntaxKind
58-
) -> (SyntaxClassification, Bool)?
56+
internal static func classify(_ keyPath: AnyKeyPath) -> (SyntaxClassification, Bool)?
5957
"""
6058
) {
61-
try IfExprSyntax(
62-
"""
63-
// Separate checks for token nodes (most common checks) versus checks for layout nodes.
64-
if childKind == .token
65-
"""
66-
) {
67-
try SwitchExprSyntax("switch (parentKind, indexInParent)") {
68-
for childClassification in node_child_classifications where childClassification.isToken {
69-
SwitchCaseSyntax("case (.\(raw: childClassification.parent.swiftSyntaxKind), \(raw: childClassification.childIndex)):") {
70-
StmtSyntax("return (.\(raw: childClassification.classification!.swiftName), \(raw: childClassification.force))")
71-
}
59+
try SwitchExprSyntax("switch keyPath") {
60+
for childClassification in node_child_classifications {
61+
SwitchCaseSyntax("case \\\(raw: childClassification.parent.type.syntaxBaseName).\(raw: childClassification.child.swiftName):") {
62+
StmtSyntax("return (.\(raw: childClassification.classification!.swiftName), \(raw: childClassification.force))")
7263
}
73-
74-
SwitchCaseSyntax("default: return nil")
7564
}
76-
} else: {
77-
try SwitchExprSyntax("switch (parentKind, indexInParent)") {
78-
for childClassification in node_child_classifications where !childClassification.isToken {
79-
SwitchCaseSyntax("case (.\(raw: childClassification.parent.swiftSyntaxKind), \(raw: childClassification.childIndex)):") {
80-
StmtSyntax("return (.\(raw: childClassification.classification!.swiftName), \(raw: childClassification.force))")
81-
}
82-
}
83-
84-
SwitchCaseSyntax("default: return nil")
65+
SwitchCaseSyntax("default:") {
66+
StmtSyntax("return nil")
8567
}
8668
}
8769
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparserdiagnostics/ChildNameForDiagnosticsFile.swift

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,11 @@ let childNameForDiagnosticFile = SourceFileSyntax(leadingTrivia: copyrightHeader
4040
}
4141
}
4242

43-
DeclSyntax(
44-
"""
45-
private func getKeyPath<T: SyntaxProtocol>(_ node: T) -> AnyKeyPath? {
46-
guard let parent = node.parent else {
47-
return nil
48-
}
49-
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
50-
return nil
51-
}
52-
return childrenKeyPaths[node.indexInParent]
53-
}
54-
"""
55-
)
56-
5743
DeclSyntax(
5844
"""
5945
extension SyntaxProtocol {
6046
var childNameInParent: String? {
61-
guard let keyPath = getKeyPath(self) else {
47+
guard let keyPath = self.keyPathInParent else {
6248
return nil
6349
}
6450
return childNameForDiagnostics(keyPath)

Sources/IDEUtils/SyntaxClassifier.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,8 @@ fileprivate extension SyntaxProtocol {
1717
var contextualClassif: (SyntaxClassification, Bool)? = nil
1818
var curData = Syntax(self)
1919
repeat {
20-
guard let parent = curData.parent else { break }
21-
contextualClassif = SyntaxClassification.classify(
22-
parentKind: parent.raw.kind,
23-
indexInParent: curData.indexInParent,
24-
childKind: raw.kind
25-
)
20+
guard let parent = curData.parent, let keyPath = curData.keyPathInParent else { break }
21+
contextualClassif = SyntaxClassification.classify(keyPath)
2622
curData = parent
2723
} while contextualClassif == nil
2824
return contextualClassif
@@ -35,7 +31,7 @@ extension TokenSyntax {
3531
let contextualClassification = self.contextualClassification
3632
let relativeOffset = leadingTriviaLength.utf8Length
3733
let absoluteOffset = position.utf8Offset + relativeOffset
38-
return TokenKindAndText(kind: rawTokenKind, text: tokenView.rawText).classify(
34+
return TokenKindAndText(kind: tokenView.rawKind, text: tokenView.rawText).classify(
3935
offset: absoluteOffset,
4036
contextualClassification: contextualClassification
4137
)
@@ -200,16 +196,17 @@ private struct ClassificationVisitor {
200196

201197
for case (let index, let child?) in children.enumerated() {
202198

203-
let classficiation = SyntaxClassification.classify(
204-
parentKind: descriptor.node.kind,
205-
indexInParent: index,
206-
childKind: child.kind
207-
)
199+
let classification: (SyntaxClassification, Bool)?
200+
if case .layout(let layout) = descriptor.node.kind.syntaxNodeType.structure {
201+
classification = SyntaxClassification.classify(layout[index])
202+
} else {
203+
classification = nil
204+
}
208205
let result = visit(
209206
.init(
210207
node: child,
211208
byteOffset: byteOffset,
212-
contextualClassification: classficiation ?? descriptor.contextualClassification
209+
contextualClassification: classification ?? descriptor.contextualClassification
213210
)
214211
)
215212
if result == .break {

Sources/IDEUtils/generated/SyntaxClassification.swift

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -65,52 +65,42 @@ extension SyntaxClassification {
6565
/// - childKind: The node syntax kind.
6666
/// - Returns: A pair of classification and whether it is "forced", or nil if
6767
/// no classification is attached.
68-
internal static func classify(
69-
parentKind: SyntaxKind, indexInParent: Int, childKind: SyntaxKind
70-
) -> (SyntaxClassification, Bool)? {
71-
// Separate checks for token nodes (most common checks) versus checks for layout nodes.
72-
if childKind == .token {
73-
switch (parentKind, indexInParent) {
74-
case (.availabilityVersionRestriction, 1):
75-
return (.keyword, false)
76-
case (.declModifier, 1):
77-
return (.attribute, false)
78-
case (.expressionSegment, 5):
79-
return (.stringInterpolationAnchor, true)
80-
case (.expressionSegment, 9):
81-
return (.stringInterpolationAnchor, true)
82-
case (.forInStmt, 5):
83-
return (.keyword, false)
84-
case (.ifConfigClause, 1):
85-
return (.buildConfigId, false)
86-
case (.ifConfigDecl, 3):
87-
return (.buildConfigId, false)
88-
case (.memberTypeIdentifier, 5):
89-
return (.typeIdentifier, false)
90-
case (.operatorDecl, 7):
91-
return (.operatorIdentifier, false)
92-
case (.precedenceGroupAssociativity, 1):
93-
return (.keyword, false)
94-
case (.precedenceGroupRelation, 1):
95-
return (.keyword, false)
96-
case (.simpleTypeIdentifier, 1):
97-
return (.typeIdentifier, false)
98-
default:
99-
return nil
100-
}
101-
}else {
102-
switch (parentKind, indexInParent) {
103-
case (.attribute, 3):
104-
return (.attribute, false)
105-
case (.availabilityVersionRestrictionListEntry, 1):
106-
return (.keyword, false)
107-
case (.ifConfigClause, 3):
108-
return (.buildConfigId, false)
109-
case (.operatorDecl, 3):
110-
return (.attribute, false)
111-
default:
112-
return nil
113-
}
68+
internal static func classify(_ keyPath: AnyKeyPath) -> (SyntaxClassification, Bool)? {
69+
switch keyPath {
70+
case \AttributeSyntax.attributeName:
71+
return (.attribute, false)
72+
case \AvailabilityVersionRestrictionListEntrySyntax.availabilityVersionRestriction:
73+
return (.keyword, false)
74+
case \AvailabilityVersionRestrictionSyntax.platform:
75+
return (.keyword, false)
76+
case \DeclModifierSyntax.name:
77+
return (.attribute, false)
78+
case \ExpressionSegmentSyntax.leftParen:
79+
return (.stringInterpolationAnchor, true)
80+
case \ExpressionSegmentSyntax.rightParen:
81+
return (.stringInterpolationAnchor, true)
82+
case \ForInStmtSyntax.awaitKeyword:
83+
return (.keyword, false)
84+
case \IfConfigClauseSyntax.poundKeyword:
85+
return (.buildConfigId, false)
86+
case \IfConfigClauseSyntax.condition:
87+
return (.buildConfigId, false)
88+
case \IfConfigDeclSyntax.poundEndif:
89+
return (.buildConfigId, false)
90+
case \MemberTypeIdentifierSyntax.name:
91+
return (.typeIdentifier, false)
92+
case \OperatorDeclSyntax.modifiers:
93+
return (.attribute, false)
94+
case \OperatorDeclSyntax.identifier:
95+
return (.operatorIdentifier, false)
96+
case \PrecedenceGroupAssociativitySyntax.associativityKeyword:
97+
return (.keyword, false)
98+
case \PrecedenceGroupRelationSyntax.higherThanOrLowerThan:
99+
return (.keyword, false)
100+
case \SimpleTypeIdentifierSyntax.name:
101+
return (.typeIdentifier, false)
102+
default:
103+
return nil
114104
}
115105
}
116106
}

Sources/SwiftBasicFormat/generated/BasicFormat.swift

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ open class BasicFormat: SyntaxRewriter {
3030
private var putNextTokenOnNewLine: Bool = false
3131

3232
open override func visitPre(_ node: Syntax) {
33-
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
33+
if let keyPath = node.keyPathInParent, shouldIndent(keyPath) {
3434
indentationLevel += 1
3535
}
3636
if let parent = node.parent, childrenSeparatedByNewline(parent) {
@@ -39,7 +39,7 @@ open class BasicFormat: SyntaxRewriter {
3939
}
4040

4141
open override func visitPost(_ node: Syntax) {
42-
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
42+
if let keyPath = node.keyPathInParent, shouldIndent(keyPath) {
4343
indentationLevel -= 1
4444
}
4545
}
@@ -53,7 +53,7 @@ open class BasicFormat: SyntaxRewriter {
5353
if requiresTrailingSpace(node) && trailingTrivia.isEmpty {
5454
trailingTrivia += .space
5555
}
56-
if let keyPath = getKeyPath(Syntax(node)), requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false), !shouldOmitNewline(node) {
56+
if let keyPath = node.keyPathInParent, requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false), !shouldOmitNewline(node) {
5757
leadingTrivia = .newline + leadingTrivia
5858
}
5959
var isOnNewline: Bool = (lastRewrittenToken?.trailingTrivia.pieces.last?.isNewline == true)
@@ -176,7 +176,7 @@ open class BasicFormat: SyntaxRewriter {
176176
}
177177

178178
open func requiresLeadingSpace(_ token: TokenSyntax) -> Bool {
179-
if let keyPath = getKeyPath(token), let requiresLeadingSpace = requiresLeadingSpace(keyPath) {
179+
if let keyPath = token.keyPathInParent, let requiresLeadingSpace = requiresLeadingSpace(keyPath) {
180180
return requiresLeadingSpace
181181
}
182182
switch (token.previousToken(viewMode: .sourceAccurate)?.tokenKind, token.tokenKind) {
@@ -231,7 +231,7 @@ open class BasicFormat: SyntaxRewriter {
231231
}
232232

233233
open func requiresTrailingSpace(_ token: TokenSyntax) -> Bool {
234-
if let keyPath = getKeyPath(token), let requiresTrailingSpace = requiresTrailingSpace(keyPath) {
234+
if let keyPath = token.keyPathInParent, let requiresTrailingSpace = requiresTrailingSpace(keyPath) {
235235
return requiresTrailingSpace
236236
}
237237
switch (token.tokenKind, token.nextToken(viewMode: .sourceAccurate)?.tokenKind) {
@@ -363,14 +363,4 @@ open class BasicFormat: SyntaxRewriter {
363363
return false
364364
}
365365
}
366-
367-
private func getKeyPath<T: SyntaxProtocol>(_ node: T) -> AnyKeyPath? {
368-
guard let parent = node.parent else {
369-
return nil
370-
}
371-
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
372-
return nil
373-
}
374-
return childrenKeyPaths[node.indexInParent]
375-
}
376366
}

Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -367,19 +367,9 @@ private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String? {
367367
}
368368
}
369369

370-
private func getKeyPath<T: SyntaxProtocol>(_ node: T) -> AnyKeyPath? {
371-
guard let parent = node.parent else {
372-
return nil
373-
}
374-
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
375-
return nil
376-
}
377-
return childrenKeyPaths[node.indexInParent]
378-
}
379-
380370
extension SyntaxProtocol {
381371
var childNameInParent: String? {
382-
guard let keyPath = getKeyPath(self) else {
372+
guard let keyPath = self.keyPathInParent else {
383373
return nil
384374
}
385375
return childNameForDiagnostics(keyPath)

0 commit comments

Comments
 (0)