Skip to content

Commit 34b2244

Browse files
committed
[NFC-ish] Factor out InitSignature
Introduce and factor out a type that represents a list of children for which an initializer ought to exist. This changes some `@available(renamed:)` values, but nothing that should change diagnostics or behavior.
1 parent 4229401 commit 34b2244

File tree

9 files changed

+422
-384
lines changed

9 files changed

+422
-384
lines changed

CodeGeneration/Sources/SyntaxSupport/CompatibilityLayers.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public struct CompatibilityLayers /*: Sendable*/ {
1616
public var deprecatedVarsByNode: [SyntaxNodeKind: [Child]] = [:]
1717

1818
/// Initializer signatures that are needed in the compatibility layer, in the order they ought to appear in the generated file.
19-
public var deprecatedInitArgListsByNode: [SyntaxNodeKind: [[Child]]] = [:]
19+
public var deprecatedInitSignaturesByNode: [SyntaxNodeKind: [InitSignature]] = [:]
2020

2121
internal init(nodes: [Node]) {
2222
for node in nodes {
@@ -51,13 +51,13 @@ public struct CompatibilityLayers /*: Sendable*/ {
5151

5252
/// Compute and cache compatibility layer information for the given node, unless it is already present.
5353
private mutating func realizeLayers(for node: Node) {
54-
guard deprecatedVarsByNode[node.syntaxNodeKind] == nil && deprecatedInitArgListsByNode[node.syntaxNodeKind] == nil, let layoutNode = node.layoutNode else {
54+
guard deprecatedVarsByNode[node.syntaxNodeKind] == nil && deprecatedInitSignaturesByNode[node.syntaxNodeKind] == nil, let layoutNode = node.layoutNode else {
5555
return
5656
}
5757

5858
// The results that will ultimately be saved into the *ByNode dictionaries.
5959
var vars: [Child] = []
60-
var initArgLists: [[Child]] = []
60+
var initSignatures: [InitSignature] = []
6161

6262
// Temporary working state for the loop.
6363
var children = layoutNode.children
@@ -108,11 +108,11 @@ public struct CompatibilityLayers /*: Sendable*/ {
108108
// 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.
109109
vars += children.filter { knownVars.insert($0).inserted }
110110

111-
initArgLists.append(children)
111+
initSignatures.append(InitSignature(children: children))
112112
}
113113

114114
deprecatedVarsByNode[node.syntaxNodeKind] = vars
115-
deprecatedInitArgListsByNode[node.syntaxNodeKind] = initArgLists
115+
deprecatedInitSignaturesByNode[node.syntaxNodeKind] = initSignatures
116116
}
117117
}
118118

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
public struct InitSignature: Hashable {
14+
public var children: [Child]
15+
16+
public init(children: [Child]) {
17+
self.children = children
18+
}
19+
20+
public init(_ layoutNode: LayoutNode) {
21+
self.init(children: layoutNode.children)
22+
}
23+
24+
public init(_ trait: Trait) {
25+
self.init(children: trait.children)
26+
}
27+
}
28+

CodeGeneration/Sources/generate-swift-syntax/LayoutNode+Extensions.swift renamed to CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift

Lines changed: 188 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,24 @@ import SwiftSyntaxBuilder
1515
import SyntaxSupport
1616
import Utils
1717

18-
extension LayoutNode {
19-
func generateInitializerDeclHeader(alternateChildren: [Child]? = nil) -> SyntaxNodeString {
20-
let children = alternateChildren ?? self.children
18+
extension InitSignature {
19+
var compoundName: String {
20+
let renamedArguments = children.map { child in
21+
if child.isUnexpectedNodes {
22+
return "_:"
23+
} else {
24+
return "\(child.labelDeclName):"
25+
}
26+
}.joined(separator: "")
27+
28+
return "init(leadingTrivia:\(renamedArguments)trailingTrivia:)"
29+
}
30+
31+
func generateInitializerDeclHeader(isRequirement: Bool = false) -> SyntaxNodeString {
32+
let stub = isRequirement ? "init?" : "public init"
33+
2134
if children.isEmpty {
22-
return "public init()"
35+
return "\(raw: stub)()"
2336
}
2437

2538
func createFunctionParameterSyntax(for child: Child) -> FunctionParameterSyntax {
@@ -52,19 +65,23 @@ extension LayoutNode {
5265
)
5366
}
5467

68+
func transformParam(_ param: FunctionParameterSyntax) -> FunctionParameterSyntax {
69+
isRequirement ? param.with(\.defaultValue, nil) : param
70+
}
71+
5572
let params = FunctionParameterListSyntax {
56-
FunctionParameterSyntax("leadingTrivia: Trivia? = nil")
73+
transformParam(FunctionParameterSyntax("leadingTrivia: Trivia? = nil"))
5774

5875
for child in children {
59-
createFunctionParameterSyntax(for: child)
76+
transformParam(createFunctionParameterSyntax(for: child))
6077
}
6178

62-
FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
63-
.with(\.leadingTrivia, .newline)
79+
transformParam(FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
80+
.with(\.leadingTrivia, .newline))
6481
}
6582

6683
return """
67-
public init(
84+
\(raw: stub)(
6885
\(params)
6986
)
7087
"""
@@ -91,7 +108,7 @@ extension LayoutNode {
91108
}
92109

93110
/// Create a builder-based convenience initializer, if needed.
94-
func createConvenienceBuilderInitializer(alternateChildren: [Child]? = nil) throws -> InitializerDeclSyntax? {
111+
func createConvenienceBuilderInitializer() throws -> InitializerDeclSyntax? {
95112
// Only create the convenience initializer if at least one parameter
96113
// is different than in the default initializer generated above.
97114
var shouldCreateInitializer = false
@@ -103,8 +120,6 @@ extension LayoutNode {
103120
var builderParameters: [FunctionParameterSyntax] = []
104121
var delegatedInitArgs: [LabeledExprSyntax] = []
105122

106-
let children = alternateChildren ?? self.children
107-
108123
for child in children {
109124
/// The expression that is used to call the default initializer defined above.
110125
let produceExpr: ExprSyntax
@@ -192,3 +207,164 @@ fileprivate func convertFromSyntaxProtocolToSyntaxType(
192207
}
193208
return ExprSyntax("\(childName.declNameOrVarCallName)")
194209
}
210+
211+
extension InitSignature {
212+
/// Generates arguments to pass this initializer's parameters through to the
213+
/// non-deprecated initializer. This will generate nested initializer calls for
214+
/// any children with a compound `newerChildPath`.
215+
func makeArgumentsToInitializeNewestChildren() -> [LabeledExprSyntax] {
216+
var root: [InitParameterMapping] = []
217+
218+
for child in children {
219+
InitParameterMapping.addChild(child, to: &root)
220+
}
221+
222+
return root.map { $0.makeArgumentExpr() }
223+
}
224+
}
225+
226+
/// Represents the means by which a newest child, possibly at a nested position, is created from
227+
/// one or more historical children.
228+
///
229+
/// For example, consider a couple of nodes with some child history:
230+
/// ```
231+
/// Node(
232+
/// kind: .nestedNodeExtractedLater,
233+
/// children: [
234+
/// Child(name: "x", ...),
235+
/// Child(name: "y", ...),
236+
/// ]
237+
/// ),
238+
/// Node(
239+
/// kind: .longstandingNode,
240+
/// children: [
241+
/// Child(name: "a", ...),
242+
/// Child(name: "nested", kind: .node(.nestedNodeExtractedLater), ...),
243+
/// ],
244+
/// childHistory: [
245+
/// [
246+
/// "a": .renamed(from: "b"),
247+
/// "nested": .extracted
248+
/// ]
249+
/// ]
250+
/// )
251+
/// ```
252+
///
253+
/// These will end up being represented by `InitParameterMapping`s that look something like
254+
/// this (with string literals standing in for the object references):
255+
///
256+
/// ```swift
257+
/// [
258+
/// InitParameterMapping(
259+
/// newerChild: "child for current LongstandingNode.a",
260+
/// argument: .decl("child for historical LongstandingNode.b")
261+
/// ),
262+
/// InitParameterMapping(
263+
/// newerChild: "child for current LongstandingNode.nested",
264+
/// argument: .nestedInit(
265+
/// [
266+
/// InitParameterMapping(
267+
/// newerChild: "child for current NestedNodeExtractedLater.x",
268+
/// argument: .decl("child for historical LongstandingNode.x")
269+
/// ),
270+
/// InitParameterMapping(
271+
/// newerChild: "child for current NestedNodeExtractedLater.y",
272+
/// argument: .decl("child for historical LongstandingNode.y")
273+
/// )
274+
/// ]
275+
/// )
276+
/// )
277+
/// ]
278+
/// ```
279+
///
280+
/// Which matches the structure of the `self.init` arguments we must generate to call from the
281+
/// compatibility `LongstandingNodeSyntax.init(b:x:y:)` to the current
282+
/// `LongstandingNodeSyntax.init(a:nested:)`:
283+
///
284+
/// ```swift
285+
/// self.init(
286+
/// a: b,
287+
/// nested: NestedNodeExtractedLaterSyntax(
288+
/// x: x,
289+
/// y: y
290+
/// )
291+
/// )
292+
/// ```
293+
private struct InitParameterMapping {
294+
var newerChild: Child
295+
var argument: Argument
296+
297+
enum Argument {
298+
case decl(olderChild: Child)
299+
case nestedInit([InitParameterMapping])
300+
}
301+
302+
static func addChild(_ olderChild: Child, to mappings: inout [InitParameterMapping]) {
303+
guard !olderChild.newerChildPath.isEmpty else {
304+
// This child is not historical, so we can just pass it right through.
305+
mappings.append(
306+
InitParameterMapping(
307+
newerChild: olderChild,
308+
argument: .decl(olderChild: olderChild)
309+
)
310+
)
311+
return
312+
}
313+
314+
let newerChildPath = olderChild.makeNewestChildPath()
315+
addChild(olderChild, to: &mappings, at: newerChildPath[...])
316+
}
317+
318+
private static func addChild(_ olderChild: Child, to mappings: inout [InitParameterMapping], at newerChildPath: ArraySlice<Child>) {
319+
let targetNewerChild = newerChildPath.first!
320+
321+
if newerChildPath.count == 1 {
322+
// We've found the argument list this ought to be added to.
323+
let newMapping = InitParameterMapping(newerChild: targetNewerChild, argument: .decl(olderChild: olderChild))
324+
mappings.append(newMapping)
325+
return
326+
}
327+
328+
// We've found a parent of the argument list this ought to be added to.
329+
var (i, nestedArgMappings) = findOrCreateNestedInit(for: targetNewerChild, in: &mappings)
330+
addChild(olderChild, to: &nestedArgMappings, at: newerChildPath.dropFirst())
331+
mappings[i].argument = .nestedInit(nestedArgMappings)
332+
}
333+
334+
private static func findOrCreateNestedInit(for newerChild: Child, in mappings: inout [InitParameterMapping]) -> (index: Int, nestedArgMapping: [InitParameterMapping]) {
335+
// If there isn't an existing mapping, we'll append a new one.
336+
guard let i = mappings.firstIndex(where: { $0.newerChild == newerChild }) else {
337+
mappings.append(InitParameterMapping(newerChild: newerChild, argument: .nestedInit([])))
338+
return (mappings.endIndex - 1, [])
339+
}
340+
341+
// We found an existing mapping for this child and its nested children.
342+
guard case .nestedInit(let nestedArgs) = mappings[i].argument else {
343+
fatalError("Can't nest parameter inside parameter!")
344+
}
345+
return (i, nestedArgs)
346+
}
347+
}
348+
349+
extension InitParameterMapping {
350+
func makeArgumentExpr() -> LabeledExprSyntax {
351+
let argValue = switch argument {
352+
case .decl(olderChild: let olderChild):
353+
ExprSyntax(DeclReferenceExprSyntax(baseName: olderChild.baseCallName))
354+
355+
case .nestedInit(let initArgs):
356+
ExprSyntax(
357+
FunctionCallExprSyntax(callee: TypeExprSyntax(type: newerChild.syntaxNodeKind.syntaxType)) {
358+
for initArg in initArgs {
359+
initArg.makeArgumentExpr()
360+
}
361+
}
362+
)
363+
}
364+
365+
return LabeledExprSyntax(
366+
label: newerChild.isUnexpectedNodes ? nil : newerChild.name,
367+
expression: argValue
368+
)
369+
}
370+
}

0 commit comments

Comments
 (0)