Skip to content

Commit e458bdf

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 ec5e933 commit e458bdf

File tree

6 files changed

+121
-56
lines changed

6 files changed

+121
-56
lines changed

Diff for: CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift

+60-12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public struct CompatibilityLayer {
1515
/// Deprecated members that the compatibility layer needs for each node.
1616
private var deprecatedMembersByNode: [SyntaxNodeKind: DeprecatedMemberInfo] = [:]
1717

18+
/// Deprecated members that the compatibility layer needs for each trait.
19+
public var deprecatedMembersByTrait: [String: DeprecatedMemberInfo] = [:]
20+
1821
/// Cache for `replacementChildren(for:by:)`. Ensures that we don't create two different replacement children even
1922
/// if we refactor the same child twice, so we can reliably equate and hash `Child` objects by object identity.
2023
private var cachedReplacementChildren: [Child: [Child]] = [:]
@@ -24,13 +27,21 @@ public struct CompatibilityLayer {
2427
return deprecatedMembersByNode[node.kind] ?? DeprecatedMemberInfo()
2528
}
2629

27-
internal init(nodes: [Node]) {
30+
/// Returns the deprecated members that the compatibility layer needs for `trait`.
31+
public func deprecatedMembers(for trait: Trait) -> DeprecatedMemberInfo {
32+
return deprecatedMembersByTrait[trait.traitName] ?? DeprecatedMemberInfo()
33+
}
34+
35+
internal init(nodes: [Node], traits: [Trait]) {
2836
// This instance will be stored in a global that's used from multiple threads simultaneously, so it won't be safe
2937
// to mutate once the initializer returns. We therefore do all the work to populate its tables up front, rather
3038
// than computing it lazily on demand.
3139
for node in nodes {
3240
computeMembers(for: node)
3341
}
42+
for trait in traits {
43+
computeMembers(for: trait)
44+
}
3445
}
3546

3647
/// Returns the child or children that would have existed in place of this
@@ -79,22 +90,54 @@ public struct CompatibilityLayer {
7990
return
8091
}
8192

93+
let result = computeMembersFor(
94+
typeName: layoutNode.kind.rawValue,
95+
initialChildren: layoutNode.children,
96+
history: layoutNode.childHistory,
97+
areRequirements: false
98+
)
99+
100+
deprecatedMembersByNode[node.syntaxNodeKind] = result
101+
}
102+
103+
private mutating func computeMembers(for trait: Trait) {
104+
guard deprecatedMembersByTrait[trait.traitName] == nil else {
105+
return
106+
}
107+
108+
let result = computeMembersFor(
109+
typeName: trait.traitName,
110+
initialChildren: trait.children,
111+
history: trait.childHistory,
112+
areRequirements: true
113+
)
114+
115+
deprecatedMembersByTrait[trait.traitName] = result
116+
}
117+
118+
/// Compute and cache compatibility layer information for the given children.
119+
private mutating func computeMembersFor(
120+
typeName: String,
121+
initialChildren: [Child],
122+
history: Child.History,
123+
areRequirements: Bool
124+
) -> DeprecatedMemberInfo {
82125
// The results that will ultimately be saved into the DeprecatedMemberInfo.
83126
var vars: [Child] = []
84127
var initSignatures: [InitSignature] = []
85128

86129
// Temporary working state for the loop.
87-
var children = layoutNode.children
130+
var children = initialChildren
88131
var knownVars = Set(children)
89132

90133
func firstIndexOfChild(named targetName: String) -> Int {
91134
guard let i = children.firstIndex(where: { $0.name == targetName }) else {
92-
fatalError("couldn't find '\(targetName)' in current children of \(node.syntaxNodeKind.rawValue): \(String(reflecting: children.map(\.name)))")
135+
fatalError("couldn't find '\(targetName)' in current children of \(typeName): \(String(reflecting: children.map(\.name)))")
93136
}
94137
return i
95138
}
96139

97-
for changeSet in layoutNode.childHistory {
140+
for changeSet in history {
98141
var unexpectedChildrenWithNewNames: Set<Child> = []
99142

100143
// First pass: Apply the changes explicitly specified in the change set.
@@ -104,12 +147,14 @@ public struct CompatibilityLayer {
104147
let replacementChildren = replacementChildren(for: children[i], by: refactoring)
105148
children.replaceSubrange(i...i, with: replacementChildren)
106149

107-
// Mark adjacent unexpected node children whose names have changed too.
108-
if currentName != replacementChildren.first?.name {
109-
unexpectedChildrenWithNewNames.insert(children[i - 1])
110-
}
111-
if currentName != replacementChildren.last?.name {
112-
unexpectedChildrenWithNewNames.insert(children[i + replacementChildren.count])
150+
if !areRequirements {
151+
// Mark adjacent unexpected node children whose names have changed too.
152+
if currentName != replacementChildren.first?.name {
153+
unexpectedChildrenWithNewNames.insert(children[i - 1])
154+
}
155+
if currentName != replacementChildren.last?.name {
156+
unexpectedChildrenWithNewNames.insert(children[i + replacementChildren.count])
157+
}
113158
}
114159
}
115160

@@ -132,10 +177,13 @@ public struct CompatibilityLayer {
132177
// 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.
133178
vars += children.filter { knownVars.insert($0).inserted }
134179

135-
initSignatures.append(InitSignature(children: children))
180+
// We don't create compatibility layers for protocol requirement inits.
181+
if !areRequirements {
182+
initSignatures.append(InitSignature(children: children))
183+
}
136184
}
137185

138-
deprecatedMembersByNode[node.syntaxNodeKind] = DeprecatedMemberInfo(vars: vars, inits: initSignatures)
186+
return DeprecatedMemberInfo(vars: vars, inits: initSignatures)
139187
}
140188
}
141189

Diff for: 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_LAYER = CompatibilityLayer(nodes: SYNTAX_NODES)
39+
public let SYNTAX_COMPATIBILITY_LAYER = CompatibilityLayer(nodes: SYNTAX_NODES, traits: TRAITS)

Diff for: 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
),

Diff for: CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift

+15
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ let renamedChildrenCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copy
3333
}
3434
}
3535
}
36+
37+
for trait in TRAITS.filter({ !$0.childHistory.isEmpty }) {
38+
var deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: trait)
39+
40+
try ExtensionDeclSyntax("extension \(trait.protocolName)") {
41+
for child in deprecatedMembers.vars {
42+
makeCompatibilityVar(for: child)
43+
if let addMethod = makeCompatibilityAddMethod(for: child) {
44+
addMethod
45+
}
46+
}
47+
48+
// Not currently generating compatibility inits for traits.
49+
}
50+
}
3651
}
3752

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

Diff for: 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

Diff for: 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)