@@ -150,29 +150,42 @@ 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 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+ }
176189
177190 /// If the child ends with "token" in the kind, it's considered a token node.
178191 /// Grab the existing reference to that token from the global list.
@@ -244,19 +257,15 @@ public class Child: NodeChoiceConvertible {
244257 return AttributeListSyntax ( " @_spi(ExperimentalLanguageFeatures) " ) . with ( \. trailingTrivia, . newline)
245258 }
246259
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.
252260 init (
253261 name: String ,
254262 deprecatedName: String ? = nil ,
255263 kind: ChildKind ,
256264 experimentalFeature: ExperimentalFeature ? = nil ,
257265 nameForDiagnostics: String ? = nil ,
258266 documentation: String ? = nil ,
259- isOptional: Bool = false
267+ isOptional: Bool = false ,
268+ newerChild: Child ? = nil
260269 ) {
261270 precondition ( name. first? . isLowercase ?? true , " The first letter of a child’s name should be lowercase " )
262271 precondition (
@@ -265,11 +274,62 @@ public class Child: NodeChoiceConvertible {
265274 )
266275 self . name = name
267276 self . deprecatedName = deprecatedName
277+ self . newestChild = Self . makeNewestChild ( from: newerChild)
268278 self . kind = kind
269279 self . experimentalFeature = experimentalFeature
270280 self . nameForDiagnostics = nameForDiagnostics
271281 self . documentationSummary = SwiftSyntax . Trivia. docCommentTrivia ( from: documentation)
272282 self . documentationAbstract = String ( documentation? . split ( whereSeparator: \. isNewline) . first ?? " " )
273283 self . isOptional = isOptional
274284 }
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+ }
275335}
0 commit comments