@@ -56,58 +56,96 @@ fileprivate class ConfiguredRegionVisitor<Configuration: BuildConfiguration>: Sy
56
56
/// Whether we are currently within an active region.
57
57
var inActiveRegion = true
58
58
59
+ // All diagnostics encountered along the way.
60
+ var diagnostics : [ Diagnostic ] = [ ]
61
+
59
62
init ( configuration: Configuration ) {
60
63
self . configuration = configuration
61
64
super. init ( viewMode: . sourceAccurate)
62
65
}
63
66
64
67
override func visit( _ node: IfConfigDeclSyntax ) -> SyntaxVisitorContinueKind {
65
- // If we're in an active region, find the active clause. Otherwise,
66
- // there isn't one.
67
- let activeClause = inActiveRegion ? node. activeClause ( in: configuration) . clause : nil
68
+ // Walk through the clauses to find the active one.
68
69
var foundActive = false
69
70
var syntaxErrorsAllowed = false
70
71
for clause in node. clauses {
71
- // If we haven't found the active clause yet, syntax errors are allowed
72
- // depending on this clause.
73
- if !foundActive {
74
- syntaxErrorsAllowed =
75
- clause. condition. map {
76
- IfConfigClauseSyntax . syntaxErrorsAllowed ( $0) . syntaxErrorsAllowed
77
- } ?? false
78
- }
72
+ let isActive : Bool
73
+ if let condition = clause. condition {
74
+ if !foundActive {
75
+ // Fold operators so we can evaluate this #if condition.
76
+ let ( foldedCondition, foldDiagnostics) = IfConfigClauseSyntax . foldOperators ( condition)
77
+ diagnostics. append ( contentsOf: foldDiagnostics)
79
78
80
- // If this is the active clause, record it and then recurse into the
81
- // elements.
82
- if clause == activeClause {
83
- assert ( inActiveRegion)
79
+ // In an active region, evaluate the condition to determine whether
80
+ // this clause is active. Otherwise, this clause is inactive.
81
+ // inactive.
82
+ if inActiveRegion {
83
+ let ( thisIsActive, _, evalDiagnostics) = evaluateIfConfig (
84
+ condition: foldedCondition,
85
+ configuration: configuration
86
+ )
87
+ diagnostics. append ( contentsOf: evalDiagnostics)
84
88
85
- regions. append ( ( clause, . active) )
89
+ // Determine if there was an error that prevented us from
90
+ // evaluating the condition. If so, we'll allow syntax errors
91
+ // from here on out.
92
+ let hadError =
93
+ foldDiagnostics. contains { diag in
94
+ diag. diagMessage. severity == . error
95
+ }
96
+ || evalDiagnostics. contains { diag in
97
+ diag. diagMessage. severity == . error
98
+ }
86
99
87
- if let elements = clause. elements {
88
- walk ( elements)
89
- }
100
+ if hadError {
101
+ isActive = false
102
+ syntaxErrorsAllowed = true
103
+ } else {
104
+ isActive = thisIsActive
90
105
91
- foundActive = true
92
- continue
106
+ // Determine whether syntax errors are allowed.
107
+ syntaxErrorsAllowed = foldedCondition. allowsSyntaxErrorsFolded
108
+ }
109
+ } else {
110
+ isActive = false
111
+
112
+ // Determine whether syntax errors are allowed, even though we
113
+ // skipped evaluation of the actual condition.
114
+ syntaxErrorsAllowed = foldedCondition. allowsSyntaxErrorsFolded
115
+ }
116
+ } else {
117
+ // We already found an active condition, so this is inactive.
118
+ isActive = false
119
+ }
120
+ } else {
121
+ // This is an #else. It's active if we haven't found an active clause
122
+ // yet and are in an active region.
123
+ isActive = !foundActive && inActiveRegion
93
124
}
94
125
95
- // If this is within an active region, or this is an unparsed region,
96
- // record it.
97
- if inActiveRegion || syntaxErrorsAllowed {
98
- regions. append ( ( clause, syntaxErrorsAllowed ? . unparsed : . inactive) )
126
+ // Determine and record the current state.
127
+ let currentState : IfConfigRegionState
128
+ switch ( isActive, syntaxErrorsAllowed) {
129
+ case ( true , _) : currentState = . active
130
+ case ( false , false ) : currentState = . inactive
131
+ case ( false , true ) : currentState = . unparsed
99
132
}
133
+ regions. append ( ( clause, currentState) )
100
134
101
- // Recurse into inactive (but not unparsed) regions to find any
102
- // unparsed regions below.
103
- if !syntaxErrorsAllowed, let elements = clause. elements {
135
+ // If this is a parsed region, recurse into it.
136
+ if currentState != . unparsed, let elements = clause. elements {
104
137
let priorInActiveRegion = inActiveRegion
105
- inActiveRegion = false
138
+ inActiveRegion = isActive
106
139
defer {
107
140
inActiveRegion = priorInActiveRegion
108
141
}
109
142
walk ( elements)
110
143
}
144
+
145
+ // Note when we found an active clause.
146
+ if isActive {
147
+ foundActive = true
148
+ }
111
149
}
112
150
113
151
return . skipChildren
0 commit comments