@@ -150,29 +150,44 @@ public class Child: NodeChoiceConvertible {
150150 /// For any other kind of child nodes, accessing this property crashes.
151151 public var syntaxChoicesType : TypeSyntax {
152152 precondition ( kind. isNodeChoices, " Cannot get `syntaxChoicesType` for node that doesn’t have nodeChoices " )
153- return " \( raw: name . withFirstCharacterUppercased) "
153+ return " \( raw: newestName . withFirstCharacterUppercased) "
154154 }
155155
156156 /// If this child only has tokens, the type that the generated `TokenSpecSet` should get.
157157 ///
158158 /// For any other kind of child nodes, accessing this property crashes.
159159 public var tokenSpecSetType : TypeSyntax {
160160 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 "
170162 }
171163
172164 /// Determines if this child has a deprecated name
173165 public var hasDeprecatedName : Bool {
174166 return deprecatedName != nil
175167 }
168+
169+ /// If this child is actually part of another child's history, links back
170+ /// to that child. Nil if this is the newest version of the child.
171+ public let newerChild : Child ?
172+
173+ /// True if this child was created by a `Refactoring`. Such children are part
174+ /// of a compatibility layer and are therefore deprecated.
175+ public var isHistorical : Bool {
176+ newerChild != nil
177+ }
178+
179+ /// Replaces the nodes in `newerChildPath` with their own `newerChildPath`s,
180+ /// if any, to form a child path enitrely of non-historical nodes.
181+ public func makeNewestChild( ) -> Child {
182+ guard let newerChild else {
183+ return self
184+ }
185+ return newerChild. makeNewestChild ( )
186+ }
187+
188+ private var newestName : String {
189+ return makeNewestChild ( ) . name
190+ }
176191
177192 /// If the child ends with "token" in the kind, it's considered a token node.
178193 /// Grab the existing reference to that token from the global list.
@@ -244,19 +259,15 @@ public class Child: NodeChoiceConvertible {
244259 return AttributeListSyntax ( " @_spi(ExperimentalLanguageFeatures) " ) . with ( \. trailingTrivia, . newline)
245260 }
246261
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.
252262 init (
253263 name: String ,
254264 deprecatedName: String ? = nil ,
255265 kind: ChildKind ,
256266 experimentalFeature: ExperimentalFeature ? = nil ,
257267 nameForDiagnostics: String ? = nil ,
258268 documentation: String ? = nil ,
259- isOptional: Bool = false
269+ isOptional: Bool = false ,
270+ newerChild: Child ? = nil
260271 ) {
261272 precondition ( name. first? . isLowercase ?? true , " The first letter of a child’s name should be lowercase " )
262273 precondition (
@@ -265,11 +276,62 @@ public class Child: NodeChoiceConvertible {
265276 )
266277 self . name = name
267278 self . deprecatedName = deprecatedName
279+ self . newerChild = newerChild
268280 self . kind = kind
269281 self . experimentalFeature = experimentalFeature
270282 self . nameForDiagnostics = nameForDiagnostics
271283 self . documentationSummary = SwiftSyntax . Trivia. docCommentTrivia ( from: documentation)
272284 self . documentationAbstract = String ( documentation? . split ( whereSeparator: \. isNewline) . first ?? " " )
273285 self . isOptional = isOptional
274286 }
287+
288+ /// Create a node that is a copy of the last node in `newerChildPath`, but
289+ /// with modifications.
290+ init ( renamingTo replacementName: String ? = nil , newerChild other: Child ) {
291+ self . name = replacementName ?? other. name
292+ self . deprecatedName = nil
293+ self . newerChild = other
294+ self . kind = other. kind
295+ self . experimentalFeature = other. experimentalFeature
296+ self . nameForDiagnostics = other. nameForDiagnostics
297+ self . documentationSummary = other. documentationSummary
298+ self . documentationAbstract = other. documentationAbstract
299+ self . isOptional = other. isOptional
300+ }
301+
302+ /// Create a child for the unexpected nodes between two children (either or
303+ /// both of which may be `nil`).
304+ convenience init ( forUnexpectedBetween earlier: Child ? , and later: Child ? , newerChild: Child ? = nil ) {
305+ let name = switch ( earlier, later) {
306+ case ( nil , let later? ) :
307+ " unexpectedBefore \( later. name. withFirstCharacterUppercased) "
308+ case ( let earlier? , nil ) :
309+ " unexpectedAfter \( earlier. name. withFirstCharacterUppercased) "
310+ case ( let earlier? , let later? ) :
311+ " unexpectedBetween \( earlier. name. withFirstCharacterUppercased) And \( later. name. withFirstCharacterUppercased) "
312+ case ( nil , nil ) :
313+ " unexpected "
314+ }
315+
316+ self . init (
317+ name: name,
318+ deprecatedName: nil , // deprecation of unexpected nodes is handled in CompatibilityLayers
319+ kind: . collection( kind: . unexpectedNodes, collectionElementName: name. withFirstCharacterUppercased) ,
320+ experimentalFeature: earlier? . experimentalFeature ?? later? . experimentalFeature,
321+ nameForDiagnostics: nil ,
322+ documentation: nil ,
323+ isOptional: true ,
324+ newerChild: newerChild
325+ )
326+ }
327+ }
328+
329+ extension Child : Hashable {
330+ public static func == ( lhs: Child , rhs: Child ) -> Bool {
331+ lhs === rhs
332+ }
333+
334+ public func hash( into hasher: inout Hasher ) {
335+ hasher. combine ( ObjectIdentifier ( self ) )
336+ }
275337}
0 commit comments