@@ -15,6 +15,9 @@ public struct CompatibilityLayer {
1515 /// Deprecated members that the compatibility layer needs for each node.
1616 private var deprecatedMembersByNode : [ SyntaxNodeKind : DeprecatedMemberInfo ] = [ : ]
1717
18+ /// Deprecated members that the compatibility layer needs for each trait.
19+ public var deprecatedMembersByTrait : [ String : DeprecatedMemberInfo ] = [ : ]
20+
1821 /// Cache for `replacementChildren(for:by:)`. Ensures that we don't create two different replacement children even
1922 /// if we refactor the same child twice, so we can reliably equate and hash `Child` objects by object identity.
2023 private var cachedReplacementChildren : [ Child : [ Child ] ] = [ : ]
@@ -24,13 +27,21 @@ public struct CompatibilityLayer {
2427 return deprecatedMembersByNode [ node. kind] ?? DeprecatedMemberInfo ( )
2528 }
2629
27- internal init ( nodes: [ Node ] ) {
30+ /// Returns the deprecated members that the compatibility layer needs for `trait`.
31+ public func deprecatedMembers( for trait: Trait ) -> DeprecatedMemberInfo {
32+ return deprecatedMembersByTrait [ trait. traitName] ?? DeprecatedMemberInfo ( )
33+ }
34+
35+ internal init ( nodes: [ Node ] , traits: [ Trait ] ) {
2836 // This instance will be stored in a global that's used from multiple threads simultaneously, so it won't be safe
2937 // to mutate once the initializer returns. We therefore do all the work to populate its tables up front, rather
3038 // than computing it lazily on demand.
3139 for node in nodes {
3240 computeMembers ( for: node)
3341 }
42+ for trait in traits {
43+ computeMembers ( for: trait)
44+ }
3445 }
3546
3647 /// Returns the child or children that would have existed in place of this
@@ -79,24 +90,56 @@ public struct CompatibilityLayer {
7990 return
8091 }
8192
93+ let result = computeMembersFor (
94+ typeName: layoutNode. kind. rawValue,
95+ initialChildren: layoutNode. children,
96+ history: layoutNode. childHistory,
97+ areRequirements: false
98+ )
99+
100+ deprecatedMembersByNode [ node. syntaxNodeKind] = result
101+ }
102+
103+ private mutating func computeMembers( for trait: Trait ) {
104+ guard deprecatedMembersByTrait [ trait. traitName] == nil else {
105+ return
106+ }
107+
108+ let result = computeMembersFor (
109+ typeName: trait. traitName,
110+ initialChildren: trait. children,
111+ history: trait. childHistory,
112+ areRequirements: true
113+ )
114+
115+ deprecatedMembersByTrait [ trait. traitName] = result
116+ }
117+
118+ /// Compute and cache compatibility layer information for the given children.
119+ private mutating func computeMembersFor(
120+ typeName: String ,
121+ initialChildren: [ Child ] ,
122+ history: Child . History ,
123+ areRequirements: Bool
124+ ) -> DeprecatedMemberInfo {
82125 // The results that will ultimately be saved into the DeprecatedMemberInfo.
83126 var vars : [ Child ] = [ ]
84127 var initSignatures : [ InitSignature ] = [ ]
85128
86129 // Temporary working state for the loop.
87- var children = layoutNode . children
130+ var children = initialChildren
88131 var knownVars = Set ( children)
89132
90133 func firstIndexOfChild( named targetName: String ) -> Int {
91134 guard let i = children. firstIndex ( where: { $0. name == targetName } ) else {
92135 fatalError (
93- " couldn't find ' \( targetName) ' in current children of \( node . syntaxNodeKind . rawValue ) : \( String ( reflecting: children. map ( \. name) ) ) "
136+ " couldn't find ' \( targetName) ' in current children of \( typeName ) : \( String ( reflecting: children. map ( \. name) ) ) "
94137 )
95138 }
96139 return i
97140 }
98141
99- for changeSet in layoutNode . childHistory {
142+ for changeSet in history {
100143 var unexpectedChildrenWithNewNames : Set < Child > = [ ]
101144
102145 // First pass: Apply the changes explicitly specified in the change set.
@@ -106,12 +149,14 @@ public struct CompatibilityLayer {
106149 let replacementChildren = replacementChildren ( for: children [ i] , by: refactoring)
107150 children. replaceSubrange ( i... i, with: replacementChildren)
108151
109- // Mark adjacent unexpected node children whose names have changed too.
110- if currentName != replacementChildren. first? . name {
111- unexpectedChildrenWithNewNames. insert ( children [ i - 1 ] )
112- }
113- if currentName != replacementChildren. last? . name {
114- unexpectedChildrenWithNewNames. insert ( children [ i + replacementChildren. count] )
152+ if !areRequirements {
153+ // Mark adjacent unexpected node children whose names have changed too.
154+ if currentName != replacementChildren. first? . name {
155+ unexpectedChildrenWithNewNames. insert ( children [ i - 1 ] )
156+ }
157+ if currentName != replacementChildren. last? . name {
158+ unexpectedChildrenWithNewNames. insert ( children [ i + replacementChildren. count] )
159+ }
115160 }
116161 }
117162
@@ -134,10 +179,13 @@ public struct CompatibilityLayer {
134179 // Third pass: Append newly-created children to vars. We do this now so that changes from the first two passes are properly interleaved, preserving source order.
135180 vars += children. filter { knownVars. insert ( $0) . inserted }
136181
137- initSignatures. append ( InitSignature ( children: children) )
182+ // We don't create compatibility layers for protocol requirement inits.
183+ if !areRequirements {
184+ initSignatures. append ( InitSignature ( children: children) )
185+ }
138186 }
139187
140- deprecatedMembersByNode [ node . syntaxNodeKind ] = DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
188+ return DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
141189 }
142190}
143191
0 commit comments