@@ -150,30 +150,43 @@ 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 }
176168
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+ }
189+
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.
179192 public var tokenKind : Token ? {
@@ -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,63 @@ 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 =
304+ switch ( earlier, later) {
305+ case ( nil , let later? ) :
306+ " unexpectedBefore \( later. name. withFirstCharacterUppercased) "
307+ case ( let earlier? , nil ) :
308+ " unexpectedAfter \( earlier. name. withFirstCharacterUppercased) "
309+ case ( let earlier? , let later? ) :
310+ " unexpectedBetween \( earlier. name. withFirstCharacterUppercased) And \( later. name. withFirstCharacterUppercased) "
311+ case ( nil , nil ) :
312+ fatalError ( " unexpected node has no siblings? " )
313+ }
314+
315+ self . init (
316+ name: name,
317+ deprecatedName: nil , // deprecation of unexpected nodes is handled in CompatibilityLayer
318+ kind: . collection( kind: . unexpectedNodes, collectionElementName: name. withFirstCharacterUppercased) ,
319+ experimentalFeature: earlier? . experimentalFeature ?? later? . experimentalFeature,
320+ nameForDiagnostics: nil ,
321+ documentation: nil ,
322+ isOptional: true ,
323+ newerChild: newerChild
324+ )
325+ }
326+ }
327+
328+ extension Child : Hashable {
329+ public static func == ( lhs: Child , rhs: Child ) -> Bool {
330+ lhs === rhs
331+ }
332+
333+ public func hash( into hasher: inout Hasher ) {
334+ hasher. combine ( ObjectIdentifier ( self ) )
335+ }
275336}
0 commit comments