@@ -158,7 +158,61 @@ public func configuredRegions(
158
158
let regions = sourceFilePtr. pointee. syntax. configuredRegions ( in: configuration)
159
159
160
160
var cRegions : [ BridgedIfConfigClauseRangeInfo ] = [ ]
161
+
162
+ // Keep track of the enclosing #ifs so that we can emit and "#endif" directive
163
+ // right before moving on to the next #if (and at the end).
164
+ var ifConfigStack : [ IfConfigDeclSyntax ] = [ ]
165
+
166
+ /// Emit the #endif location for the given #if declaration.
167
+ func flushSingleIfConfig( _ topIfConfigDecl: IfConfigDeclSyntax ) {
168
+ cRegions. append (
169
+ . init(
170
+ directiveLoc: sourceFilePtr. pointee. sourceLoc (
171
+ at: topIfConfigDecl. poundEndif. positionAfterSkippingLeadingTrivia
172
+ ) ,
173
+ bodyLoc: sourceFilePtr. pointee. sourceLoc (
174
+ at: topIfConfigDecl. poundEndif. endPosition
175
+ ) ,
176
+ endLoc: sourceFilePtr. pointee. sourceLoc (
177
+ at: topIfConfigDecl. poundEndif. endPosition
178
+ ) ,
179
+ kind: . IfConfigEnd
180
+ )
181
+ )
182
+ }
183
+
184
+ /// Push a new #if declaration into the stack so that we'll insert #endifs
185
+ /// in the right places.
186
+ func pushIfConfig( _ currentIfConfigDecl: IfConfigDeclSyntax ) {
187
+ // Go through the current stack of #if declarations.
188
+ while let topIfConfig = ifConfigStack. last {
189
+ // If the top of the stack is the same as this #if, we're done.
190
+ if topIfConfig == currentIfConfigDecl {
191
+ return
192
+ }
193
+
194
+ // If the top of the stack is not an ancestor of this #if, flush it
195
+ // and keep going.
196
+ if !topIfConfig. isAncestor ( of: currentIfConfigDecl) {
197
+ flushSingleIfConfig ( topIfConfig)
198
+ ifConfigStack. removeLast ( )
199
+ continue
200
+ }
201
+
202
+ break
203
+ }
204
+
205
+ // Add this #if to the stack.
206
+ ifConfigStack. append ( currentIfConfigDecl)
207
+ }
208
+
209
+ // Translate all of the configured regions.
161
210
for (ifConfig, state) in regions {
211
+ // Note that we're handling an #if now.
212
+ if let currentIfConfigDecl = ifConfig. parent? . parent? . as ( IfConfigDeclSyntax . self) {
213
+ pushIfConfig ( currentIfConfigDecl)
214
+ }
215
+
162
216
let kind : BridgedIfConfigClauseKind
163
217
switch state {
164
218
case . active: kind = . IfConfigActive
@@ -174,23 +228,50 @@ public func configuredRegions(
174
228
bodyLoc = ifConfig. endPosition
175
229
}
176
230
231
+ let endLoc : AbsolutePosition
232
+ if let nextToken = ifConfig. nextToken ( viewMode: . sourceAccurate) {
233
+ endLoc = nextToken. positionAfterSkippingLeadingTrivia
234
+ } else {
235
+ endLoc = ifConfig. endPosition
236
+ }
237
+
177
238
cRegions. append (
178
239
. init(
179
240
directiveLoc: sourceFilePtr. pointee. sourceLoc (
180
241
at: ifConfig. poundKeyword. positionAfterSkippingLeadingTrivia
181
242
) ,
182
243
bodyLoc: sourceFilePtr. pointee. sourceLoc ( at: bodyLoc) ,
183
- endLoc: sourceFilePtr. pointee. sourceLoc (
184
- at: ifConfig. endPosition
185
- ) ,
244
+ endLoc: sourceFilePtr. pointee. sourceLoc ( at: endLoc) ,
186
245
kind: kind
187
246
)
188
247
)
189
248
}
190
249
250
+ // Flush the remaining #ifs.
251
+ while let topIfConfig = ifConfigStack. popLast ( ) {
252
+ flushSingleIfConfig ( topIfConfig)
253
+ }
254
+
191
255
let cRegionsBuf : UnsafeMutableBufferPointer < BridgedIfConfigClauseRangeInfo > =
192
256
. allocate( capacity: cRegions. count)
193
257
_ = cRegionsBuf. initialize ( from: cRegions)
194
258
cRegionsOut. pointee = cRegionsBuf. baseAddress
195
259
return cRegionsBuf. count
196
260
}
261
+
262
+ extension SyntaxProtocol {
263
+ /// Determine whether this node is an ancestor of the given `other` node.
264
+ func isAncestor( of other: some SyntaxProtocol ) -> Bool {
265
+ var other = Syntax ( other)
266
+ let selfSyntax = Syntax ( self )
267
+ while let otherParent = other. parent {
268
+ if otherParent == selfSyntax {
269
+ return true
270
+ }
271
+
272
+ other = otherParent
273
+ }
274
+
275
+ return false
276
+ }
277
+ }
0 commit comments