@@ -30,8 +30,7 @@ import SwiftSyntax
30
30
/// diagnose syntax errors in blocks where the check fails.
31
31
func evaluateIfConfig(
32
32
condition: ExprSyntax ,
33
- configuration: some BuildConfiguration ,
34
- outermostCondition: Bool = true
33
+ configuration: some BuildConfiguration
35
34
) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
36
35
var extraDiagnostics : [ Diagnostic ] = [ ]
37
36
@@ -50,7 +49,7 @@ func evaluateIfConfig(
50
49
/// Record an if-config evaluation error before returning it. Use this for
51
50
/// every 'throw' site in this evaluation.
52
51
func recordError(
53
- _ error: IfConfigError
52
+ _ error: IfConfigDiagnostic
54
53
) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
55
54
return recordError ( error, at: error. syntax)
56
55
}
@@ -88,7 +87,7 @@ func evaluateIfConfig(
88
87
active: result,
89
88
syntaxErrorsAllowed: false ,
90
89
diagnostics: [
91
- IfConfigError . integerLiteralCondition (
90
+ IfConfigDiagnostic . integerLiteralCondition (
92
91
syntax: condition,
93
92
replacement: result
94
93
) . asDiagnostic
@@ -98,7 +97,7 @@ func evaluateIfConfig(
98
97
99
98
// Declaration references are for custom compilation flags.
100
99
if let identExpr = condition. as ( DeclReferenceExprSyntax . self) ,
101
- let ident = identExpr. simpleIdentifier
100
+ let ident = identExpr. simpleIdentifier? . name
102
101
{
103
102
// Evaluate the custom condition. If the build configuration cannot answer this query, fail.
104
103
return checkConfiguration ( at: identExpr) {
@@ -115,8 +114,7 @@ func evaluateIfConfig(
115
114
116
115
let ( innerActive, innerSyntaxErrorsAllowed, innerDiagnostics) = evaluateIfConfig (
117
116
condition: prefixOp. expression,
118
- configuration: configuration,
119
- outermostCondition: outermostCondition
117
+ configuration: configuration
120
118
)
121
119
122
120
return ( active: !innerActive, syntaxErrorsAllowed: innerSyntaxErrorsAllowed, diagnostics: innerDiagnostics)
@@ -133,7 +131,7 @@ func evaluateIfConfig(
133
131
}
134
132
135
133
// Check whether this was likely to be a check for targetEnvironment(simulator).
136
- if outermostCondition ,
134
+ if binOp . isOutermostIfCondition ,
137
135
let targetEnvironmentDiag = diagnoseLikelySimulatorEnvironmentTest ( binOp)
138
136
{
139
137
extraDiagnostics. append ( targetEnvironmentDiag)
@@ -142,13 +140,12 @@ func evaluateIfConfig(
142
140
// Evaluate the left-hand side.
143
141
let ( lhsActive, lhsSyntaxErrorsAllowed, lhsDiagnostics) = evaluateIfConfig (
144
142
condition: binOp. leftOperand,
145
- configuration: configuration,
146
- outermostCondition: false
143
+ configuration: configuration
147
144
)
148
145
149
- // Short-circuit evaluation if we know the answer . We still recurse into
150
- // the right-hand side, but with a dummy configuration that won't have
151
- // side effects, so we only get validation-related errors .
146
+ // Determine whether we already know the result . We might short-circuit the
147
+ // evaluation, depending on whether we need to produce validation
148
+ // diagnostics for the right-hand side .
152
149
let shortCircuitResult : Bool ?
153
150
switch ( lhsActive, op. operator. text) {
154
151
case ( true , " || " ) : shortCircuitResult = true
@@ -157,8 +154,8 @@ func evaluateIfConfig(
157
154
}
158
155
159
156
// If we are supposed to short-circuit and the left-hand side of this
160
- // operator with inactive && , stop now: we shouldn't evaluate the right-
161
- // hand side at all.
157
+ // operator permits syntax errors when it fails , stop now: we shouldn't
158
+ // process the right- hand side at all.
162
159
if let isActive = shortCircuitResult, lhsSyntaxErrorsAllowed {
163
160
return (
164
161
active: isActive,
@@ -167,21 +164,21 @@ func evaluateIfConfig(
167
164
)
168
165
}
169
166
170
- // Evaluate the right-hand side.
167
+ // Process the right-hand side. If we already know the answer, then
168
+ // avoid performing any build configuration queries that might cause
169
+ // side effects.
171
170
let rhsActive : Bool
172
171
let rhsSyntaxErrorsAllowed : Bool
173
172
let rhsDiagnostics : [ Diagnostic ]
174
173
if shortCircuitResult != nil {
175
174
( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
176
175
condition: binOp. rightOperand,
177
- configuration: CanImportSuppressingBuildConfiguration ( other: configuration) ,
178
- outermostCondition: false
176
+ configuration: CanImportSuppressingBuildConfiguration ( other: configuration)
179
177
)
180
178
} else {
181
179
( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
182
180
condition: binOp. rightOperand,
183
- configuration: configuration,
184
- outermostCondition: false
181
+ configuration: configuration
185
182
)
186
183
}
187
184
@@ -211,14 +208,13 @@ func evaluateIfConfig(
211
208
{
212
209
return evaluateIfConfig (
213
210
condition: element. expression,
214
- configuration: configuration,
215
- outermostCondition: outermostCondition
211
+ configuration: configuration
216
212
)
217
213
}
218
214
219
215
// Call syntax is for operations.
220
216
if let call = condition. as ( FunctionCallExprSyntax . self) ,
221
- let fnName = call. calledExpression. simpleIdentifierExpr,
217
+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
222
218
let fn = IfConfigFunctions ( rawValue: fnName)
223
219
{
224
220
/// Perform a check for an operation that takes a single identifier argument.
@@ -228,7 +224,7 @@ func evaluateIfConfig(
228
224
) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
229
225
// Ensure that we have a single argument that is a simple identifier.
230
226
guard let argExpr = call. arguments. singleUnlabeledExpression,
231
- var arg = argExpr. simpleIdentifierExpr
227
+ var arg = argExpr. simpleIdentifierExpr? . name
232
228
else {
233
229
return recordError (
234
230
. requiresUnlabeledArgument( name: fnName, role: role, syntax: ExprSyntax ( call) )
@@ -238,7 +234,7 @@ func evaluateIfConfig(
238
234
// The historical "macabi" environment has been renamed to "macCatalyst".
239
235
if role == " environment " && arg == " macabi " {
240
236
extraDiagnostics. append (
241
- IfConfigError . macabiIsMacCatalyst ( syntax: argExpr)
237
+ IfConfigDiagnostic . macabiIsMacCatalyst ( syntax: argExpr)
242
238
. asDiagnostic
243
239
)
244
240
@@ -320,7 +316,7 @@ func evaluateIfConfig(
320
316
case . _endian:
321
317
// Ensure that we have a single argument that is a simple identifier.
322
318
guard let argExpr = call. arguments. singleUnlabeledExpression,
323
- let arg = argExpr. simpleIdentifierExpr
319
+ let arg = argExpr. simpleIdentifierExpr? . name
324
320
else {
325
321
return recordError (
326
322
. requiresUnlabeledArgument(
@@ -339,7 +335,7 @@ func evaluateIfConfig(
339
335
} else {
340
336
// Complain about unknown endianness
341
337
extraDiagnostics. append (
342
- IfConfigError . endiannessDoesNotMatch ( syntax: argExpr, argument: arg)
338
+ IfConfigDiagnostic . endiannessDoesNotMatch ( syntax: argExpr, argument: arg)
343
339
. asDiagnostic
344
340
)
345
341
@@ -356,7 +352,7 @@ func evaluateIfConfig(
356
352
// Ensure that we have a single argument that is a simple identifier, which
357
353
// is an underscore followed by an integer.
358
354
guard let argExpr = call. arguments. singleUnlabeledExpression,
359
- let arg = argExpr. simpleIdentifierExpr,
355
+ let arg = argExpr. simpleIdentifierExpr? . name ,
360
356
let argFirst = arg. first,
361
357
argFirst == " _ " ,
362
358
let expectedBitWidth = Int ( arg. dropFirst ( ) )
@@ -470,7 +466,7 @@ func evaluateIfConfig(
470
466
471
467
// Warn that we did this.
472
468
extraDiagnostics. append (
473
- IfConfigError . ignoredTrailingComponents (
469
+ IfConfigDiagnostic . ignoredTrailingComponents (
474
470
version: versionTuple,
475
471
syntax: secondArg. expression
476
472
) . asDiagnostic
@@ -502,26 +498,51 @@ func evaluateIfConfig(
502
498
return recordError ( . unknownExpression( condition) )
503
499
}
504
500
501
+ extension SyntaxProtocol {
502
+ /// Determine whether this expression node is an "outermost" #if condition,
503
+ /// meaning that it is not nested within some kind of expression like && or
504
+ /// ||.
505
+ fileprivate var isOutermostIfCondition : Bool {
506
+ // If there is no parent, it's the outermost condition.
507
+ guard let parent = self . parent else {
508
+ return true
509
+ }
510
+
511
+ // If we hit the #if condition clause, it's the outermost condition.
512
+ if parent. is ( IfConfigClauseSyntax . self) {
513
+ return true
514
+ }
515
+
516
+ // We found an infix operator, so this is not an outermost #if condition.
517
+ if parent. is ( InfixOperatorExprSyntax . self) {
518
+ return false
519
+ }
520
+
521
+ // Keep looking up the syntax tree.
522
+ return parent. isOutermostIfCondition
523
+ }
524
+ }
525
+
505
526
/// Given an expression with the expected form A.B.C, extract the import path
506
527
/// ["A", "B", "C"] from it. Throws an error if the expression doesn't match
507
528
/// this form.
508
529
private func extractImportPath( _ expression: some ExprSyntaxProtocol ) throws -> [ String ] {
509
530
// Member access.
510
531
if let memberAccess = expression. as ( MemberAccessExprSyntax . self) ,
511
532
let base = memberAccess. base,
512
- let memberName = memberAccess. declName. simpleIdentifier
533
+ let memberName = memberAccess. declName. simpleIdentifier? . name
513
534
{
514
535
return try extractImportPath ( base) + [ memberName]
515
536
}
516
537
517
538
// Declaration reference.
518
539
if let declRef = expression. as ( DeclReferenceExprSyntax . self) ,
519
- let name = declRef. simpleIdentifier
540
+ let name = declRef. simpleIdentifier? . name
520
541
{
521
542
return [ name]
522
543
}
523
544
524
- throw IfConfigError . expectedModuleName ( syntax: ExprSyntax ( expression) )
545
+ throw IfConfigDiagnostic . expectedModuleName ( syntax: ExprSyntax ( expression) )
525
546
}
526
547
527
548
/// Determine whether the given condition only involves disjunctions that
@@ -553,11 +574,11 @@ private func isConditionDisjunction(
553
574
// If we have a call to this function, check whether the argument is one of
554
575
// the acceptable values.
555
576
if let call = condition. as ( FunctionCallExprSyntax . self) ,
556
- let fnName = call. calledExpression. simpleIdentifierExpr,
577
+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
557
578
let callFn = IfConfigFunctions ( rawValue: fnName) ,
558
579
callFn == function,
559
580
let argExpr = call. arguments. singleUnlabeledExpression,
560
- let arg = argExpr. simpleIdentifierExpr
581
+ let arg = argExpr. simpleIdentifierExpr? . name
561
582
{
562
583
return values. contains ( arg)
563
584
}
@@ -603,14 +624,14 @@ private func diagnoseLikelySimulatorEnvironmentTest(
603
624
return nil
604
625
}
605
626
606
- return IfConfigError . likelySimulatorPlatform ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
627
+ return IfConfigDiagnostic . likelySimulatorPlatform ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
607
628
}
608
629
609
630
extension IfConfigClauseSyntax {
610
631
/// Fold the operators within an #if condition, turning sequence expressions
611
632
/// involving the various allowed operators (&&, ||, !) into well-structured
612
633
/// binary operators.
613
- public static func foldOperators(
634
+ static func foldOperators(
614
635
_ condition: some ExprSyntaxProtocol
615
636
) -> ( folded: ExprSyntax , diagnostics: [ Diagnostic ] ) {
616
637
var foldingDiagnostics : [ Diagnostic ] = [ ]
@@ -622,7 +643,7 @@ extension IfConfigClauseSyntax {
622
643
{
623
644
624
645
foldingDiagnostics. append (
625
- IfConfigError . badInfixOperator ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
646
+ IfConfigDiagnostic . badInfixOperator ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
626
647
)
627
648
return
628
649
}
@@ -635,7 +656,7 @@ extension IfConfigClauseSyntax {
635
656
/// Determine whether the given expression, when used as the condition in
636
657
/// an inactive `#if` clause, implies that syntax errors are permitted within
637
658
/// that region.
638
- public static func syntaxErrorsAllowed(
659
+ static func syntaxErrorsAllowed(
639
660
_ condition: some ExprSyntaxProtocol
640
661
) -> ( syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
641
662
let ( foldedCondition, foldingDiagnostics) = IfConfigClauseSyntax . foldOperators ( condition)
@@ -684,7 +705,7 @@ extension ExprSyntaxProtocol {
684
705
685
706
// Call syntax is for operations.
686
707
if let call = self . as ( FunctionCallExprSyntax . self) ,
687
- let fnName = call. calledExpression. simpleIdentifierExpr,
708
+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
688
709
let fn = IfConfigFunctions ( rawValue: fnName)
689
710
{
690
711
return fn. syntaxErrorsAllowed
0 commit comments