Skip to content

Commit a461ff2

Browse files
committed
[NFC-ish] Support child history for traits
It’s always been possible to specify deprecated children, but nothing was actually done with the info until now. Turns some manaully-generated decls into automatically-generated ones, but doesn’t change anything user-facing.
1 parent 34c4291 commit a461ff2

File tree

6 files changed

+123
-58
lines changed

6 files changed

+123
-58
lines changed

CodeGeneration/Sources/SyntaxSupport/CompatibilityLayers.swift

+61-14
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@ public struct CompatibilityLayers /*: Sendable*/ {
1818
/// Initializer signatures that are needed in the compatibility layer, in the order they ought to appear in the generated file.
1919
public var deprecatedInitSignaturesByNode: [SyntaxNodeKind: [InitSignature]] = [:]
2020

21-
internal init(nodes: [Node]) {
21+
/// Properties that are needed in the compatibility layer, in the order they ought to appear in the generated file.
22+
public var deprecatedVarsByTrait: [String: [Child]] = [:]
23+
24+
/// Initializer signatures that are needed in the compatibility layer, in the order they ought to appear in the generated file.
25+
public var deprecatedInitSignaturesByTrait: [String: [InitSignature]] = [:]
26+
27+
internal init(nodes: [Node], traits: [Trait]) {
2228
for node in nodes {
2329
realizeLayers(for: node)
2430
}
31+
for trait in traits {
32+
realizeLayers(for: trait)
33+
}
2534
}
2635

2736
private func extractedChildren(in layoutNode: LayoutNode) -> ArraySlice<Child> {
@@ -70,26 +79,60 @@ public struct CompatibilityLayers /*: Sendable*/ {
7079

7180
/// Compute and cache compatibility layer information for the given node, unless it is already present.
7281
private mutating func realizeLayers(for node: Node) {
73-
guard deprecatedVarsByNode[node.syntaxNodeKind] == nil && deprecatedInitSignaturesByNode[node.syntaxNodeKind] == nil, let layoutNode = node.layoutNode else {
82+
guard deprecatedVarsByNode[node.syntaxNodeKind] == nil, let layoutNode = node.layoutNode else {
83+
return
84+
}
85+
86+
let result = computeLayersFor(
87+
typeName: layoutNode.kind.rawValue,
88+
initialChildren: layoutNode.children,
89+
history: layoutNode.childHistory,
90+
areRequirements: false
91+
)
92+
93+
deprecatedVarsByNode[node.syntaxNodeKind] = result.vars
94+
deprecatedInitSignaturesByNode[node.syntaxNodeKind] = result.initSignatures
95+
}
96+
97+
private mutating func realizeLayers(for trait: Trait) {
98+
guard deprecatedVarsByTrait[trait.traitName] == nil else {
7499
return
75100
}
76101

102+
let result = computeLayersFor(
103+
typeName: trait.traitName,
104+
initialChildren: trait.children,
105+
history: trait.childHistory,
106+
areRequirements: true
107+
)
108+
109+
deprecatedVarsByTrait[trait.traitName] = result.vars
110+
deprecatedInitSignaturesByTrait[trait.traitName] = result.initSignatures
111+
}
112+
113+
/// Compute and cache compatibility layer information for the given children.
114+
private mutating func computeLayersFor(
115+
typeName: String,
116+
initialChildren: [Child],
117+
history: Child.History,
118+
areRequirements: Bool
119+
) -> (vars: [Child], initSignatures: [InitSignature]) {
77120
// The results that will ultimately be saved into the *ByNode dictionaries.
78121
var vars: [Child] = []
79122
var initSignatures: [InitSignature] = []
80123

81124
// Temporary working state for the loop.
82-
var children = layoutNode.children
125+
var children = initialChildren
83126
var knownVars = Set(children)
84127

85128
func firstIndexOfChild(named targetName: String) -> Int {
86129
guard let i = children.firstIndex(where: { $0.name == targetName }) else {
87-
fatalError("couldn't find '\(targetName)' in current children of \(node.syntaxNodeKind.rawValue): \(String(reflecting: children.map(\.name)))")
130+
fatalError("couldn't find '\(targetName)' in current children of \(typeName): \(String(reflecting: children.map(\.name)))")
88131
}
89132
return i
90133
}
91134

92-
for changeSet in layoutNode.childHistory {
135+
for changeSet in history {
93136
var unexpectedChildrenWithNewNames: Set<Child> = []
94137

95138
// First pass: Apply the changes explicitly specified in the change set.
@@ -99,12 +142,14 @@ public struct CompatibilityLayers /*: Sendable*/ {
99142
let replacementChildren = replacementChildren(for: children[i], by: refactoring)
100143
children.replaceSubrange(i...i, with: replacementChildren)
101144

102-
// Mark adjacent unexpected node children whose names have changed too.
103-
if currentName != (replacementChildren.first?.name ?? "") {
104-
unexpectedChildrenWithNewNames.insert(children[i - 1])
105-
}
106-
if currentName != (replacementChildren.last?.name ?? "") {
107-
unexpectedChildrenWithNewNames.insert(children[i + replacementChildren.count])
145+
if !areRequirements {
146+
// Mark adjacent unexpected node children whose names have changed too.
147+
if currentName != (replacementChildren.first?.name ?? "") {
148+
unexpectedChildrenWithNewNames.insert(children[i - 1])
149+
}
150+
if currentName != (replacementChildren.last?.name ?? "") {
151+
unexpectedChildrenWithNewNames.insert(children[i + replacementChildren.count])
152+
}
108153
}
109154
}
110155

@@ -127,11 +172,13 @@ public struct CompatibilityLayers /*: Sendable*/ {
127172
// Third pass: Append newly-created children to vars. We do this now so that changes from the first two passes are properly interleaved, preserving source order.
128173
vars += children.filter { knownVars.insert($0).inserted }
129174

130-
initSignatures.append(InitSignature(children: children))
175+
// We don't create compatibility layers for protocol requirement inits.
176+
if !areRequirements {
177+
initSignatures.append(InitSignature(children: children))
178+
}
131179
}
132180

133-
deprecatedVarsByNode[node.syntaxNodeKind] = vars
134-
deprecatedInitSignaturesByNode[node.syntaxNodeKind] = initSignatures
181+
return (vars, initSignatures)
135182
}
136183
}
137184

CodeGeneration/Sources/SyntaxSupport/SyntaxNodes.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ public let SYNTAX_NODE_MAP: [SyntaxNodeKind: Node] = Dictionary(
3636

3737
public let NON_BASE_SYNTAX_NODES = SYNTAX_NODES.filter { !$0.kind.isBase }
3838

39-
public let SYNTAX_COMPATIBILITY_LAYERS = CompatibilityLayers(nodes: SYNTAX_NODES)
39+
public let SYNTAX_COMPATIBILITY_LAYERS = CompatibilityLayers(nodes: SYNTAX_NODES, traits: TRAITS)

CodeGeneration/Sources/SyntaxSupport/Traits.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class Trait {
2222
public let protocolName: TokenSyntax
2323
public let documentation: SwiftSyntax.Trivia
2424
public let children: [Child]
25+
public let childHistory: Child.History
2526

2627
init(traitName: String, baseKind: SyntaxNodeKind? = nil, documentation: String? = nil, children: [Child], childHistory: Child.History = []) {
2728
precondition(baseKind?.isBase != false, "`baseKind` must be a base syntax node kind")
@@ -30,7 +31,7 @@ public class Trait {
3031
self.protocolName = .identifier("\(traitName)Syntax")
3132
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
3233
self.children = children
33-
// FIXME: We don't appear to have ever generated compatibility layers for children of traits!
34+
self.childHistory = childHistory
3435
}
3536
}
3637

@@ -98,6 +99,7 @@ public let TRAITS: [Trait] = [
9899
"pound": .renamed(from: "poundToken"),
99100
"macroName": .renamed(from: "macro"),
100101
"arguments": .renamed(from: "argumentList"),
102+
"genericArgumentClause": .renamed(from: "genericArguments"),
101103
]
102104
]
103105
),

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift

+16
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ let renamedChildrenCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copy
3434
}
3535
}
3636
}
37+
38+
for trait in TRAITS.filter({ !$0.childHistory.isEmpty }) {
39+
var deprecatedVars = SYNTAX_COMPATIBILITY_LAYERS.deprecatedVarsByTrait[trait.traitName] ?? []
40+
var deprecatedArgLists = SYNTAX_COMPATIBILITY_LAYERS.deprecatedInitSignaturesByTrait[trait.traitName] ?? []
41+
42+
try ExtensionDeclSyntax("extension \(trait.protocolName)") {
43+
for child in deprecatedVars {
44+
makeCompatibilityVar(for: child)
45+
if let addMethod = makeCompatibilityAddMethod(for: child) {
46+
addMethod
47+
}
48+
}
49+
50+
// Not currently generating compatibility inits for traits.
51+
}
52+
}
3753
}
3854

3955
func makeCompatibilityVar(for child: Child) -> DeclSyntax {

Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift

-42
Original file line numberDiff line numberDiff line change
@@ -350,48 +350,6 @@ extension FunctionEffectSpecifiersSyntax {
350350
}
351351
}
352352

353-
extension FreestandingMacroExpansionSyntax {
354-
@available(*, deprecated, renamed: "pound")
355-
public var poundToken: TokenSyntax {
356-
get {
357-
return pound
358-
}
359-
set {
360-
pound = newValue
361-
}
362-
}
363-
364-
@available(*, deprecated, renamed: "macroName")
365-
public var macro: TokenSyntax {
366-
get {
367-
return macroName
368-
}
369-
set {
370-
macroName = newValue
371-
}
372-
}
373-
374-
@available(*, deprecated, renamed: "genericArgumentClause")
375-
public var genericArguments: GenericArgumentClauseSyntax? {
376-
get {
377-
return genericArgumentClause
378-
}
379-
set {
380-
genericArgumentClause = newValue
381-
}
382-
}
383-
384-
@available(*, deprecated, renamed: "arguments")
385-
public var argumentList: LabeledExprListSyntax {
386-
get {
387-
return arguments
388-
}
389-
set {
390-
arguments = newValue
391-
}
392-
}
393-
}
394-
395353
extension GenericRequirementSyntax {
396354
@available(*, deprecated, renamed: "Requirement")
397355
public typealias Body = Requirement

Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift

+42
Original file line numberDiff line numberDiff line change
@@ -8559,3 +8559,45 @@ extension YieldedExpressionsClauseSyntax {
85598559
)
85608560
}
85618561
}
8562+
8563+
extension FreestandingMacroExpansionSyntax {
8564+
@available(*, deprecated, renamed: "pound")
8565+
public var poundToken: TokenSyntax {
8566+
get {
8567+
return pound
8568+
}
8569+
set {
8570+
pound = newValue
8571+
}
8572+
}
8573+
8574+
@available(*, deprecated, renamed: "macroName")
8575+
public var macro: TokenSyntax {
8576+
get {
8577+
return macroName
8578+
}
8579+
set {
8580+
macroName = newValue
8581+
}
8582+
}
8583+
8584+
@available(*, deprecated, renamed: "genericArgumentClause")
8585+
public var genericArguments: GenericArgumentClauseSyntax? {
8586+
get {
8587+
return genericArgumentClause
8588+
}
8589+
set {
8590+
genericArgumentClause = newValue
8591+
}
8592+
}
8593+
8594+
@available(*, deprecated, renamed: "arguments")
8595+
public var argumentList: LabeledExprListSyntax {
8596+
get {
8597+
return arguments
8598+
}
8599+
set {
8600+
arguments = newValue
8601+
}
8602+
}
8603+
}

0 commit comments

Comments
 (0)