@@ -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,22 +90,54 @@ 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 {
92- fatalError ( " couldn't find ' \( targetName) ' in current children of \( node . syntaxNodeKind . rawValue ) : \( String ( reflecting: children. map ( \. name) ) ) " )
135+ fatalError ( " couldn't find ' \( targetName) ' in current children of \( typeName ) : \( String ( reflecting: children. map ( \. name) ) ) " )
93136 }
94137 return i
95138 }
96139
97- for changeSet in layoutNode . childHistory {
140+ for changeSet in history {
98141 var unexpectedChildrenWithNewNames : Set < Child > = [ ]
99142
100143 // First pass: Apply the changes explicitly specified in the change set.
@@ -104,12 +147,14 @@ public struct CompatibilityLayer {
104147 let replacementChildren = replacementChildren ( for: children [ i] , by: refactoring)
105148 children. replaceSubrange ( i... i, with: replacementChildren)
106149
107- // Mark adjacent unexpected node children whose names have changed too.
108- if currentName != replacementChildren. first? . name {
109- unexpectedChildrenWithNewNames. insert ( children [ i - 1 ] )
110- }
111- if currentName != replacementChildren. last? . name {
112- unexpectedChildrenWithNewNames. insert ( children [ i + replacementChildren. count] )
150+ if !areRequirements {
151+ // Mark adjacent unexpected node children whose names have changed too.
152+ if currentName != replacementChildren. first? . name {
153+ unexpectedChildrenWithNewNames. insert ( children [ i - 1 ] )
154+ }
155+ if currentName != replacementChildren. last? . name {
156+ unexpectedChildrenWithNewNames. insert ( children [ i + replacementChildren. count] )
157+ }
113158 }
114159 }
115160
@@ -132,10 +177,13 @@ public struct CompatibilityLayer {
132177 // 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.
133178 vars += children. filter { knownVars. insert ( $0) . inserted }
134179
135- initSignatures. append ( InitSignature ( children: children) )
180+ // We don't create compatibility layers for protocol requirement inits.
181+ if !areRequirements {
182+ initSignatures. append ( InitSignature ( children: children) )
183+ }
136184 }
137185
138- deprecatedMembersByNode [ node . syntaxNodeKind ] = DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
186+ return DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
139187 }
140188}
141189
0 commit comments