@@ -51,9 +51,7 @@ public struct ConfiguredRegions {
51
51
return currentState
52
52
}
53
53
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 {
57
55
currentState = state
58
56
}
59
57
}
@@ -72,6 +70,33 @@ extension ConfiguredRegions: RandomAccessCollection {
72
70
}
73
71
}
74
72
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
+
75
100
extension SyntaxProtocol {
76
101
/// Find all of the #if/#elseif/#else clauses within the given syntax node,
77
102
/// indicating their active state. This operation will recurse into all
@@ -118,6 +143,9 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
118
143
/// Whether we are currently within an active region.
119
144
var inActiveRegion = true
120
145
146
+ /// Whether we are currently within an #if at all.
147
+ var inAnyIfConfig = false
148
+
121
149
// All diagnostics encountered along the way.
122
150
var diagnostics : [ Diagnostic ] = [ ]
123
151
@@ -127,9 +155,17 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
127
155
}
128
156
129
157
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
+
130
165
// Walk through the clauses to find the active one.
131
166
var foundActive = false
132
167
var syntaxErrorsAllowed = false
168
+ let outerState : IfConfigRegionState = inActiveRegion ? . active : . inactive
133
169
for clause in node. clauses {
134
170
let isActive : Bool
135
171
if let condition = clause. condition {
@@ -192,7 +228,11 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
192
228
case ( false , false ) : currentState = . inactive
193
229
case ( false , true ) : currentState = . unparsed
194
230
}
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
+ }
196
236
197
237
// If this is a parsed region, recurse into it.
198
238
if currentState != . unparsed, let elements = clause. elements {
0 commit comments