@@ -15,11 +15,24 @@ import SwiftSyntaxBuilder
1515import SyntaxSupport
1616import 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