@@ -158,7 +158,61 @@ public func configuredRegions(
158158 let regions = sourceFilePtr. pointee. syntax. configuredRegions ( in: configuration)
159159
160160 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.
161210 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+
162216 let kind : BridgedIfConfigClauseKind
163217 switch state {
164218 case . active: kind = . IfConfigActive
@@ -174,23 +228,50 @@ public func configuredRegions(
174228 bodyLoc = ifConfig. endPosition
175229 }
176230
231+ let endLoc : AbsolutePosition
232+ if let nextToken = ifConfig. nextToken ( viewMode: . sourceAccurate) {
233+ endLoc = nextToken. positionAfterSkippingLeadingTrivia
234+ } else {
235+ endLoc = ifConfig. endPosition
236+ }
237+
177238 cRegions. append (
178239 . init(
179240 directiveLoc: sourceFilePtr. pointee. sourceLoc (
180241 at: ifConfig. poundKeyword. positionAfterSkippingLeadingTrivia
181242 ) ,
182243 bodyLoc: sourceFilePtr. pointee. sourceLoc ( at: bodyLoc) ,
183- endLoc: sourceFilePtr. pointee. sourceLoc (
184- at: ifConfig. endPosition
185- ) ,
244+ endLoc: sourceFilePtr. pointee. sourceLoc ( at: endLoc) ,
186245 kind: kind
187246 )
188247 )
189248 }
190249
250+ // Flush the remaining #ifs.
251+ while let topIfConfig = ifConfigStack. popLast ( ) {
252+ flushSingleIfConfig ( topIfConfig)
253+ }
254+
191255 let cRegionsBuf : UnsafeMutableBufferPointer < BridgedIfConfigClauseRangeInfo > =
192256 . allocate( capacity: cRegions. count)
193257 _ = cRegionsBuf. initialize ( from: cRegions)
194258 cRegionsOut. pointee = cRegionsBuf. baseAddress
195259 return cRegionsBuf. count
196260}
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