@@ -15,11 +15,24 @@ import SwiftSyntaxBuilder
15
15
import SyntaxSupport
16
16
import Utils
17
17
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
+
21
34
if children. isEmpty {
22
- return " public init ()"
35
+ return " \( raw : stub ) () "
23
36
}
24
37
25
38
func createFunctionParameterSyntax( for child: Child ) -> FunctionParameterSyntax {
@@ -52,19 +65,23 @@ extension LayoutNode {
52
65
)
53
66
}
54
67
68
+ func transformParam( _ param: FunctionParameterSyntax ) -> FunctionParameterSyntax {
69
+ isRequirement ? param. with ( \. defaultValue, nil ) : param
70
+ }
71
+
55
72
let params = FunctionParameterListSyntax {
56
- FunctionParameterSyntax ( " leadingTrivia: Trivia? = nil " )
73
+ transformParam ( FunctionParameterSyntax ( " leadingTrivia: Trivia? = nil " ) )
57
74
58
75
for child in children {
59
- createFunctionParameterSyntax ( for: child)
76
+ transformParam ( createFunctionParameterSyntax ( for: child) )
60
77
}
61
78
62
- FunctionParameterSyntax ( " trailingTrivia: Trivia? = nil " )
63
- . with ( \. leadingTrivia, . newline)
79
+ transformParam ( FunctionParameterSyntax ( " trailingTrivia: Trivia? = nil " )
80
+ . with ( \. leadingTrivia, . newline) )
64
81
}
65
82
66
83
return """
67
- public init (
84
+ \( raw : stub ) (
68
85
\( params)
69
86
)
70
87
"""
@@ -91,7 +108,7 @@ extension LayoutNode {
91
108
}
92
109
93
110
/// Create a builder-based convenience initializer, if needed.
94
- func createConvenienceBuilderInitializer( alternateChildren : [ Child ] ? = nil ) throws -> InitializerDeclSyntax ? {
111
+ func createConvenienceBuilderInitializer( ) throws -> InitializerDeclSyntax ? {
95
112
// Only create the convenience initializer if at least one parameter
96
113
// is different than in the default initializer generated above.
97
114
var shouldCreateInitializer = false
@@ -103,8 +120,6 @@ extension LayoutNode {
103
120
var builderParameters : [ FunctionParameterSyntax ] = [ ]
104
121
var delegatedInitArgs : [ LabeledExprSyntax ] = [ ]
105
122
106
- let children = alternateChildren ?? self . children
107
-
108
123
for child in children {
109
124
/// The expression that is used to call the default initializer defined above.
110
125
let produceExpr : ExprSyntax
@@ -192,3 +207,164 @@ fileprivate func convertFromSyntaxProtocolToSyntaxType(
192
207
}
193
208
return ExprSyntax ( " \( childName. declNameOrVarCallName) " )
194
209
}
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