@@ -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+
75100extension 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 {
0 commit comments