@@ -150,29 +150,42 @@ public class Child: NodeChoiceConvertible {
150
150
/// For any other kind of child nodes, accessing this property crashes.
151
151
public var syntaxChoicesType : TypeSyntax {
152
152
precondition ( kind. isNodeChoices, " Cannot get `syntaxChoicesType` for node that doesn’t have nodeChoices " )
153
- return " \( raw: name . withFirstCharacterUppercased) "
153
+ return " \( raw: newestName . withFirstCharacterUppercased) "
154
154
}
155
155
156
156
/// If this child only has tokens, the type that the generated `TokenSpecSet` should get.
157
157
///
158
158
/// For any other kind of child nodes, accessing this property crashes.
159
159
public var tokenSpecSetType : TypeSyntax {
160
160
precondition ( kind. isToken, " Cannot get `tokenSpecSetType` for node that isn’t a token " )
161
- return " \( raw: name. withFirstCharacterUppercased) Options "
162
- }
163
-
164
- /// The deprecated name of this child that's suitable to be used for variable or enum case names.
165
- public var deprecatedVarName : TokenSyntax ? {
166
- guard let deprecatedName = deprecatedName else {
167
- return nil
168
- }
169
- return . identifier( lowercaseFirstWord ( name: deprecatedName) )
161
+ return " \( raw: newestName. withFirstCharacterUppercased) Options "
170
162
}
171
163
172
164
/// Determines if this child has a deprecated name
173
165
public var hasDeprecatedName : Bool {
174
166
return deprecatedName != nil
175
167
}
168
+
169
+ /// If this child is actually part of another child's history, links back
170
+ /// to the newest (that is, most current/non-deprecated) version of the
171
+ /// child. Nil if this is the newest version of the child.
172
+ public let newestChild : Child ?
173
+
174
+ /// True if this child was created by a `Child.Refactoring`. Such children
175
+ /// are part of the compatibility layer and are therefore deprecated.
176
+ public var isHistorical : Bool {
177
+ newestChild != nil
178
+ }
179
+
180
+ /// Replaces the nodes in `newerChildPath` with their own `newerChildPath`s,
181
+ /// if any, to form a child path enitrely of non-historical nodes.
182
+ static private func makeNewestChild( from newerChild: Child ? ) -> Child ? {
183
+ return newerChild? . newestChild ?? newerChild
184
+ }
185
+
186
+ private var newestName : String {
187
+ return newestChild? . name ?? name
188
+ }
176
189
177
190
/// If the child ends with "token" in the kind, it's considered a token node.
178
191
/// Grab the existing reference to that token from the global list.
@@ -244,19 +257,15 @@ public class Child: NodeChoiceConvertible {
244
257
return AttributeListSyntax ( " @_spi(ExperimentalLanguageFeatures) " ) . with ( \. trailingTrivia, . newline)
245
258
}
246
259
247
- /// If a classification is passed, it specifies the color identifiers in
248
- /// that subtree should inherit for syntax coloring. Must be a member of
249
- /// ``SyntaxClassification``.
250
- /// If `forceClassification` is also set to true, all child nodes (not only
251
- /// identifiers) inherit the syntax classification.
252
260
init (
253
261
name: String ,
254
262
deprecatedName: String ? = nil ,
255
263
kind: ChildKind ,
256
264
experimentalFeature: ExperimentalFeature ? = nil ,
257
265
nameForDiagnostics: String ? = nil ,
258
266
documentation: String ? = nil ,
259
- isOptional: Bool = false
267
+ isOptional: Bool = false ,
268
+ newerChild: Child ? = nil
260
269
) {
261
270
precondition ( name. first? . isLowercase ?? true , " The first letter of a child’s name should be lowercase " )
262
271
precondition (
@@ -265,11 +274,62 @@ public class Child: NodeChoiceConvertible {
265
274
)
266
275
self . name = name
267
276
self . deprecatedName = deprecatedName
277
+ self . newestChild = Self . makeNewestChild ( from: newerChild)
268
278
self . kind = kind
269
279
self . experimentalFeature = experimentalFeature
270
280
self . nameForDiagnostics = nameForDiagnostics
271
281
self . documentationSummary = SwiftSyntax . Trivia. docCommentTrivia ( from: documentation)
272
282
self . documentationAbstract = String ( documentation? . split ( whereSeparator: \. isNewline) . first ?? " " )
273
283
self . isOptional = isOptional
274
284
}
285
+
286
+ /// Create a node that is a copy of the last node in `newerChildPath`, but
287
+ /// with modifications.
288
+ init ( renamingTo replacementName: String ? = nil , newerChild other: Child ) {
289
+ self . name = replacementName ?? other. name
290
+ self . deprecatedName = nil
291
+ self . newestChild = Self . makeNewestChild ( from: other)
292
+ self . kind = other. kind
293
+ self . experimentalFeature = other. experimentalFeature
294
+ self . nameForDiagnostics = other. nameForDiagnostics
295
+ self . documentationSummary = other. documentationSummary
296
+ self . documentationAbstract = other. documentationAbstract
297
+ self . isOptional = other. isOptional
298
+ }
299
+
300
+ /// Create a child for the unexpected nodes between two children (either or
301
+ /// both of which may be `nil`).
302
+ convenience init ( forUnexpectedBetween earlier: Child ? , and later: Child ? , newerChild: Child ? = nil ) {
303
+ let name = switch ( earlier, later) {
304
+ case ( nil , let later? ) :
305
+ " unexpectedBefore \( later. name. withFirstCharacterUppercased) "
306
+ case ( let earlier? , nil ) :
307
+ " unexpectedAfter \( earlier. name. withFirstCharacterUppercased) "
308
+ case ( let earlier? , let later? ) :
309
+ " unexpectedBetween \( earlier. name. withFirstCharacterUppercased) And \( later. name. withFirstCharacterUppercased) "
310
+ case ( nil , nil ) :
311
+ fatalError ( " unexpected node has no siblings? " )
312
+ }
313
+
314
+ self . init (
315
+ name: name,
316
+ deprecatedName: nil , // deprecation of unexpected nodes is handled in CompatibilityLayer
317
+ kind: . collection( kind: . unexpectedNodes, collectionElementName: name. withFirstCharacterUppercased) ,
318
+ experimentalFeature: earlier? . experimentalFeature ?? later? . experimentalFeature,
319
+ nameForDiagnostics: nil ,
320
+ documentation: nil ,
321
+ isOptional: true ,
322
+ newerChild: newerChild
323
+ )
324
+ }
325
+ }
326
+
327
+ extension Child : Hashable {
328
+ public static func == ( lhs: Child , rhs: Child ) -> Bool {
329
+ lhs === rhs
330
+ }
331
+
332
+ public func hash( into hasher: inout Hasher ) {
333
+ hasher. combine ( ObjectIdentifier ( self ) )
334
+ }
275
335
}
0 commit comments