Skip to content

Commit 98aeb44

Browse files
committed
Small improvements for ConfiguredRegions
Add a debugDescription so it's easier to see what the regions are, and allow us to test for exact results. Tweak the set of configured regions more precisely so they match what the compiler is expecting. The only difference here is that we create a separate region for an active block within another active block.
1 parent 43ce0d6 commit 98aeb44

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

Sources/SwiftIfConfig/ConfiguredRegions.swift

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

54-
let ifRegionStart =
55-
ifClause.condition?.endPosition ?? ifClause.elements?._syntaxNode.position ?? ifClause.poundKeyword.endPosition
56-
if node.position >= ifRegionStart && node.position <= ifClause.endPosition {
54+
if node.position >= ifClause.regionStart && node.position <= ifClause.endPosition {
5755
currentState = state
5856
}
5957
}
@@ -72,6 +70,33 @@ extension ConfiguredRegions: RandomAccessCollection {
7270
}
7371
}
7472

73+
extension ConfiguredRegions: CustomDebugStringConvertible {
74+
/// Provides source ranges for each of the configured regions.
75+
public var debugDescription: String {
76+
guard let firstRegion = first else {
77+
return "[]"
78+
}
79+
80+
let root = firstRegion.0.root
81+
let converter = SourceLocationConverter(fileName: "", tree: root)
82+
let regionDescriptions = regions.map { (ifClause, state) in
83+
let startPosition = converter.location(for: ifClause.position)
84+
let endPosition = converter.location(for: ifClause.endPosition)
85+
return "[\(startPosition.line):\(startPosition.column) - \(endPosition.line):\(endPosition.column)] = \(state)"
86+
}
87+
88+
return "[\(regionDescriptions.joined(separator: ", ")))]"
89+
}
90+
}
91+
92+
extension IfConfigClauseSyntax {
93+
/// The effective start of the region after which code is subject to its
94+
/// condition.
95+
fileprivate var regionStart: AbsolutePosition {
96+
condition?.endPosition ?? elements?._syntaxNode.position ?? poundKeyword.endPosition
97+
}
98+
}
99+
75100
extension SyntaxProtocol {
76101
/// Find all of the #if/#elseif/#else clauses within the given syntax node,
77102
/// indicating their active state. This operation will recurse into all
@@ -118,6 +143,9 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
118143
/// Whether we are currently within an active region.
119144
var inActiveRegion = true
120145

146+
/// Whether we are currently within an #if at all.
147+
var inAnyIfConfig = false
148+
121149
// All diagnostics encountered along the way.
122150
var diagnostics: [Diagnostic] = []
123151

@@ -127,9 +155,17 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
127155
}
128156

129157
override func visit(_ node: IfConfigDeclSyntax) -> SyntaxVisitorContinueKind {
158+
// We are in an #if.
159+
let priorInAnyIfConfig = inAnyIfConfig
160+
inAnyIfConfig = true
161+
defer {
162+
inAnyIfConfig = priorInAnyIfConfig
163+
}
164+
130165
// Walk through the clauses to find the active one.
131166
var foundActive = false
132167
var syntaxErrorsAllowed = false
168+
let outerState: IfConfigRegionState = inActiveRegion ? .active : .inactive
133169
for clause in node.clauses {
134170
let isActive: Bool
135171
if let condition = clause.condition {
@@ -192,7 +228,11 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
192228
case (false, false): currentState = .inactive
193229
case (false, true): currentState = .unparsed
194230
}
195-
regions.append((clause, currentState))
231+
232+
// If there is a state change, record it.
233+
if !priorInAnyIfConfig || currentState != .inactive || currentState != outerState {
234+
regions.append((clause, currentState))
235+
}
196236

197237
// If this is a parsed region, recurse into it.
198238
if currentState != .unparsed, let elements = clause.elements {

Tests/SwiftIfConfigTest/ActiveRegionTests.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ public class ActiveRegionTests: XCTestCase {
7979
"4️⃣": .unparsed,
8080
"5️⃣": .unparsed,
8181
"6️⃣": .active,
82-
]
82+
],
83+
configuredRegionDescription:
84+
"[[1:6 - 3:5] = active, [3:5 - 10:7] = inactive, [5:5 - 7:5] = unparsed, [7:5 - 9:5] = unparsed)]"
8385
)
8486
}
8587

@@ -127,6 +129,7 @@ fileprivate func assertActiveCode(
127129
_ markedSource: String,
128130
configuration: some BuildConfiguration = TestingBuildConfiguration(),
129131
states: [String: IfConfigRegionState],
132+
configuredRegionDescription: String? = nil,
130133
file: StaticString = #filePath,
131134
line: UInt = #line
132135
) throws {
@@ -152,13 +155,23 @@ fileprivate func assertActiveCode(
152155
let (actualState, _) = token.isActive(in: configuration)
153156
XCTAssertEqual(actualState, expectedState, "isActive(in:) at marker \(marker)", file: file, line: line)
154157

155-
let actualViaRegions = token.isActive(inConfiguredRegions: configuredRegions)
158+
let actualViaRegions = configuredRegions.isActive(token)
156159
XCTAssertEqual(
157160
actualViaRegions,
158161
expectedState,
159162
"isActive(inConfiguredRegions:) at marker \(marker)",
160163
file: file,
161164
line: line
162165
)
166+
167+
if let configuredRegionDescription {
168+
XCTAssertEqual(
169+
configuredRegions.debugDescription,
170+
configuredRegionDescription,
171+
"configured region descsription",
172+
file: file,
173+
line: line
174+
)
175+
}
163176
}
164177
}

Tests/SwiftIfConfigTest/TestingBuildConfiguration.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ struct TestingBuildConfiguration: BuildConfiguration {
5454
}
5555

5656
func canImport(
57-
importPath: [String],
57+
importPath: [(TokenSyntax, String)],
5858
version: CanImportVersion
5959
) throws -> Bool {
60-
guard let moduleName = importPath.first else {
60+
guard let moduleName = importPath.first?.1 else {
6161
return false
6262
}
6363

0 commit comments

Comments
 (0)