@@ -15,6 +15,9 @@ public struct CompatibilityLayer {
15
15
/// Deprecated members that the compatibility layer needs for each node.
16
16
private var deprecatedMembersByNode : [ SyntaxNodeKind : DeprecatedMemberInfo ] = [ : ]
17
17
18
+ /// Deprecated members that the compatibility layer needs for each trait.
19
+ public var deprecatedMembersByTrait : [ String : DeprecatedMemberInfo ] = [ : ]
20
+
18
21
/// Cache for `replacementChildren(for:by:)`. Ensures that we don't create two different replacement children even
19
22
/// if we refactor the same child twice, so we can reliably equate and hash `Child` objects by object identity.
20
23
private var cachedReplacementChildren : [ Child : [ Child ] ] = [ : ]
@@ -24,13 +27,21 @@ public struct CompatibilityLayer {
24
27
return deprecatedMembersByNode [ node. kind] ?? DeprecatedMemberInfo ( )
25
28
}
26
29
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 ] ) {
28
36
// This instance will be stored in a global that's used from multiple threads simultaneously, so it won't be safe
29
37
// to mutate once the initializer returns. We therefore do all the work to populate its tables up front, rather
30
38
// than computing it lazily on demand.
31
39
for node in nodes {
32
40
computeMembers ( for: node)
33
41
}
42
+ for trait in traits {
43
+ computeMembers ( for: trait)
44
+ }
34
45
}
35
46
36
47
/// Returns the child or children that would have existed in place of this
@@ -79,22 +90,54 @@ public struct CompatibilityLayer {
79
90
return
80
91
}
81
92
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 {
82
125
// The results that will ultimately be saved into the DeprecatedMemberInfo.
83
126
var vars : [ Child ] = [ ]
84
127
var initSignatures : [ InitSignature ] = [ ]
85
128
86
129
// Temporary working state for the loop.
87
- var children = layoutNode . children
130
+ var children = initialChildren
88
131
var knownVars = Set ( children)
89
132
90
133
func firstIndexOfChild( named targetName: String ) -> Int {
91
134
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) ) ) " )
93
136
}
94
137
return i
95
138
}
96
139
97
- for changeSet in layoutNode . childHistory {
140
+ for changeSet in history {
98
141
var unexpectedChildrenWithNewNames : Set < Child > = [ ]
99
142
100
143
// First pass: Apply the changes explicitly specified in the change set.
@@ -104,12 +147,14 @@ public struct CompatibilityLayer {
104
147
let replacementChildren = replacementChildren ( for: children [ i] , by: refactoring)
105
148
children. replaceSubrange ( i... i, with: replacementChildren)
106
149
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
+ }
113
158
}
114
159
}
115
160
@@ -132,10 +177,13 @@ public struct CompatibilityLayer {
132
177
// 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.
133
178
vars += children. filter { knownVars. insert ( $0) . inserted }
134
179
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
+ }
136
184
}
137
185
138
- deprecatedMembersByNode [ node . syntaxNodeKind ] = DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
186
+ return DeprecatedMemberInfo ( vars: vars, inits: initSignatures)
139
187
}
140
188
}
141
189
0 commit comments