Skip to content

Commit 37ebac2

Browse files
committed
Insert #endif directives into #if config ranges list produced by SwiftIfConfig
The SIL coverage map generation depends on the locations of the `#endif` directives, but the mapping from SwiftIfConfig's configured regions wasn't producing them. The information is implicitly available in the SwiftIfConfig configured regions, so reconstitute it as we translate regions.
1 parent 14af1cb commit 37ebac2

File tree

1 file changed

+84
-3
lines changed

1 file changed

+84
-3
lines changed

lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)