Skip to content

Commit 5a19857

Browse files
committed
Reimplement SyntaxProtocol.isActive(in:) in terms of ConfiguredRegions.
The compiler's semantics for determining unparsed vs. inactive regions require looking all the way up the `#if` tree to the root, and the bottom-up implementation of `isActive(in:)` was failing to match the semantics of configured regions. Use `ConfiguredRegions` directly, with a note that the performance of `SyntaxProtocol.isActive(in:)` is not good. I'm still considering whether this API should go away entirely.
1 parent 049ed84 commit 5a19857

File tree

2 files changed

+10
-36
lines changed

2 files changed

+10
-36
lines changed

Sources/SwiftIfConfig/ConfiguredRegions.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public struct ConfiguredRegions {
5151
return currentState
5252
}
5353

54-
if node.position <= ifClause.endPosition {
54+
let ifRegionStart =
55+
ifClause.condition?.endPosition ?? ifClause.elements?._syntaxNode.position ?? ifClause.poundKeyword.endPosition
56+
if node.position >= ifRegionStart && node.position <= ifClause.endPosition {
5557
currentState = state
5658
}
5759
}

Sources/SwiftIfConfig/SyntaxProtocol+IfConfig.swift

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,44 +36,16 @@ extension SyntaxProtocol {
3636
/// If the compiler version is smaller than 12.0, then `isActive` on any of the tokens within
3737
/// that `#elseif` block would return "unparsed", because that syntax should not (conceptually)
3838
/// be parsed.
39+
///
40+
/// Note that this function requires processing all #ifs from the root node
41+
/// of the syntax tree down to the current node. If performing more than a
42+
/// small number of `isActive(_:)` queries, please form a `ConfiguredRegions`
43+
/// instance and use `ConfiguredRegions.isActive(_:)` instead.
3944
public func isActive(
4045
in configuration: some BuildConfiguration
4146
) -> (state: IfConfigRegionState, diagnostics: [Diagnostic]) {
42-
var currentNode: Syntax = Syntax(self)
43-
var currentState: IfConfigRegionState = .active
44-
var diagnostics: [Diagnostic] = []
45-
46-
while let parent = currentNode.parent {
47-
// If the parent is an `#if` configuration, check whether our current
48-
// clause is active. If not, we're in an inactive region. We also
49-
// need to determine whether an inactive region should be parsed or not.
50-
if let ifConfigClause = currentNode.as(IfConfigClauseSyntax.self),
51-
let ifConfigDecl = ifConfigClause.parent?.parent?.as(IfConfigDeclSyntax.self)
52-
{
53-
let (activeClause, localDiagnostics) = ifConfigDecl.activeClause(in: configuration)
54-
diagnostics.append(contentsOf: localDiagnostics)
55-
56-
if activeClause != ifConfigClause {
57-
// This was not the active clause, so we know that we're in an
58-
// inactive block. If syntax errors aren't allowable, this is an
59-
// unparsed region.
60-
let syntaxErrorsAllowed =
61-
ifConfigClause.condition.map {
62-
IfConfigClauseSyntax.syntaxErrorsAllowed($0).syntaxErrorsAllowed
63-
} ?? false
64-
65-
if syntaxErrorsAllowed {
66-
return (.unparsed, diagnostics)
67-
}
68-
69-
currentState = .inactive
70-
}
71-
}
72-
73-
currentNode = parent
74-
}
75-
76-
return (currentState, diagnostics)
47+
let configuredRegions = root.configuredRegions(in: configuration)
48+
return (configuredRegions.isActive(self), configuredRegions.diagnostics)
7749
}
7850

7951
/// Determine whether the given syntax node is active given a set of

0 commit comments

Comments
 (0)