@@ -30,8 +30,7 @@ import SwiftSyntax
3030/// diagnose syntax errors in blocks where the check fails.
3131func evaluateIfConfig(
3232 condition: ExprSyntax ,
33- configuration: some BuildConfiguration ,
34- outermostCondition: Bool = true
33+ configuration: some BuildConfiguration
3534) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
3635 var extraDiagnostics : [ Diagnostic ] = [ ]
3736
@@ -50,7 +49,7 @@ func evaluateIfConfig(
5049 /// Record an if-config evaluation error before returning it. Use this for
5150 /// every 'throw' site in this evaluation.
5251 func recordError(
53- _ error: IfConfigError
52+ _ error: IfConfigDiagnostic
5453 ) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
5554 return recordError ( error, at: error. syntax)
5655 }
@@ -88,7 +87,7 @@ func evaluateIfConfig(
8887 active: result,
8988 syntaxErrorsAllowed: false ,
9089 diagnostics: [
91- IfConfigError . integerLiteralCondition (
90+ IfConfigDiagnostic . integerLiteralCondition (
9291 syntax: condition,
9392 replacement: result
9493 ) . asDiagnostic
@@ -98,7 +97,7 @@ func evaluateIfConfig(
9897
9998 // Declaration references are for custom compilation flags.
10099 if let identExpr = condition. as ( DeclReferenceExprSyntax . self) ,
101- let ident = identExpr. simpleIdentifier
100+ let ident = identExpr. simpleIdentifier? . name
102101 {
103102 // Evaluate the custom condition. If the build configuration cannot answer this query, fail.
104103 return checkConfiguration ( at: identExpr) {
@@ -115,8 +114,7 @@ func evaluateIfConfig(
115114
116115 let ( innerActive, innerSyntaxErrorsAllowed, innerDiagnostics) = evaluateIfConfig (
117116 condition: prefixOp. expression,
118- configuration: configuration,
119- outermostCondition: outermostCondition
117+ configuration: configuration
120118 )
121119
122120 return ( active: !innerActive, syntaxErrorsAllowed: innerSyntaxErrorsAllowed, diagnostics: innerDiagnostics)
@@ -133,7 +131,7 @@ func evaluateIfConfig(
133131 }
134132
135133 // Check whether this was likely to be a check for targetEnvironment(simulator).
136- if outermostCondition ,
134+ if binOp . isOutermostIfCondition ,
137135 let targetEnvironmentDiag = diagnoseLikelySimulatorEnvironmentTest ( binOp)
138136 {
139137 extraDiagnostics. append ( targetEnvironmentDiag)
@@ -142,13 +140,12 @@ func evaluateIfConfig(
142140 // Evaluate the left-hand side.
143141 let ( lhsActive, lhsSyntaxErrorsAllowed, lhsDiagnostics) = evaluateIfConfig (
144142 condition: binOp. leftOperand,
145- configuration: configuration,
146- outermostCondition: false
143+ configuration: configuration
147144 )
148145
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 .
152149 let shortCircuitResult : Bool ?
153150 switch ( lhsActive, op. operator. text) {
154151 case ( true , " || " ) : shortCircuitResult = true
@@ -157,8 +154,8 @@ func evaluateIfConfig(
157154 }
158155
159156 // 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.
162159 if let isActive = shortCircuitResult, lhsSyntaxErrorsAllowed {
163160 return (
164161 active: isActive,
@@ -167,21 +164,21 @@ func evaluateIfConfig(
167164 )
168165 }
169166
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.
171170 let rhsActive : Bool
172171 let rhsSyntaxErrorsAllowed : Bool
173172 let rhsDiagnostics : [ Diagnostic ]
174173 if shortCircuitResult != nil {
175174 ( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
176175 condition: binOp. rightOperand,
177- configuration: CanImportSuppressingBuildConfiguration ( other: configuration) ,
178- outermostCondition: false
176+ configuration: CanImportSuppressingBuildConfiguration ( other: configuration)
179177 )
180178 } else {
181179 ( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
182180 condition: binOp. rightOperand,
183- configuration: configuration,
184- outermostCondition: false
181+ configuration: configuration
185182 )
186183 }
187184
@@ -211,14 +208,13 @@ func evaluateIfConfig(
211208 {
212209 return evaluateIfConfig (
213210 condition: element. expression,
214- configuration: configuration,
215- outermostCondition: outermostCondition
211+ configuration: configuration
216212 )
217213 }
218214
219215 // Call syntax is for operations.
220216 if let call = condition. as ( FunctionCallExprSyntax . self) ,
221- let fnName = call. calledExpression. simpleIdentifierExpr,
217+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
222218 let fn = IfConfigFunctions ( rawValue: fnName)
223219 {
224220 /// Perform a check for an operation that takes a single identifier argument.
@@ -228,7 +224,7 @@ func evaluateIfConfig(
228224 ) -> ( active: Bool , syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
229225 // Ensure that we have a single argument that is a simple identifier.
230226 guard let argExpr = call. arguments. singleUnlabeledExpression,
231- var arg = argExpr. simpleIdentifierExpr
227+ var arg = argExpr. simpleIdentifierExpr? . name
232228 else {
233229 return recordError (
234230 . requiresUnlabeledArgument( name: fnName, role: role, syntax: ExprSyntax ( call) )
@@ -238,7 +234,7 @@ func evaluateIfConfig(
238234 // The historical "macabi" environment has been renamed to "macCatalyst".
239235 if role == " environment " && arg == " macabi " {
240236 extraDiagnostics. append (
241- IfConfigError . macabiIsMacCatalyst ( syntax: argExpr)
237+ IfConfigDiagnostic . macabiIsMacCatalyst ( syntax: argExpr)
242238 . asDiagnostic
243239 )
244240
@@ -320,7 +316,7 @@ func evaluateIfConfig(
320316 case . _endian:
321317 // Ensure that we have a single argument that is a simple identifier.
322318 guard let argExpr = call. arguments. singleUnlabeledExpression,
323- let arg = argExpr. simpleIdentifierExpr
319+ let arg = argExpr. simpleIdentifierExpr? . name
324320 else {
325321 return recordError (
326322 . requiresUnlabeledArgument(
@@ -339,7 +335,7 @@ func evaluateIfConfig(
339335 } else {
340336 // Complain about unknown endianness
341337 extraDiagnostics. append (
342- IfConfigError . endiannessDoesNotMatch ( syntax: argExpr, argument: arg)
338+ IfConfigDiagnostic . endiannessDoesNotMatch ( syntax: argExpr, argument: arg)
343339 . asDiagnostic
344340 )
345341
@@ -356,7 +352,7 @@ func evaluateIfConfig(
356352 // Ensure that we have a single argument that is a simple identifier, which
357353 // is an underscore followed by an integer.
358354 guard let argExpr = call. arguments. singleUnlabeledExpression,
359- let arg = argExpr. simpleIdentifierExpr,
355+ let arg = argExpr. simpleIdentifierExpr? . name ,
360356 let argFirst = arg. first,
361357 argFirst == " _ " ,
362358 let expectedBitWidth = Int ( arg. dropFirst ( ) )
@@ -470,7 +466,7 @@ func evaluateIfConfig(
470466
471467 // Warn that we did this.
472468 extraDiagnostics. append (
473- IfConfigError . ignoredTrailingComponents (
469+ IfConfigDiagnostic . ignoredTrailingComponents (
474470 version: versionTuple,
475471 syntax: secondArg. expression
476472 ) . asDiagnostic
@@ -502,26 +498,51 @@ func evaluateIfConfig(
502498 return recordError ( . unknownExpression( condition) )
503499}
504500
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+
505526/// Given an expression with the expected form A.B.C, extract the import path
506527/// ["A", "B", "C"] from it. Throws an error if the expression doesn't match
507528/// this form.
508529private func extractImportPath( _ expression: some ExprSyntaxProtocol ) throws -> [ String ] {
509530 // Member access.
510531 if let memberAccess = expression. as ( MemberAccessExprSyntax . self) ,
511532 let base = memberAccess. base,
512- let memberName = memberAccess. declName. simpleIdentifier
533+ let memberName = memberAccess. declName. simpleIdentifier? . name
513534 {
514535 return try extractImportPath ( base) + [ memberName]
515536 }
516537
517538 // Declaration reference.
518539 if let declRef = expression. as ( DeclReferenceExprSyntax . self) ,
519- let name = declRef. simpleIdentifier
540+ let name = declRef. simpleIdentifier? . name
520541 {
521542 return [ name]
522543 }
523544
524- throw IfConfigError . expectedModuleName ( syntax: ExprSyntax ( expression) )
545+ throw IfConfigDiagnostic . expectedModuleName ( syntax: ExprSyntax ( expression) )
525546}
526547
527548/// Determine whether the given condition only involves disjunctions that
@@ -553,11 +574,11 @@ private func isConditionDisjunction(
553574 // If we have a call to this function, check whether the argument is one of
554575 // the acceptable values.
555576 if let call = condition. as ( FunctionCallExprSyntax . self) ,
556- let fnName = call. calledExpression. simpleIdentifierExpr,
577+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
557578 let callFn = IfConfigFunctions ( rawValue: fnName) ,
558579 callFn == function,
559580 let argExpr = call. arguments. singleUnlabeledExpression,
560- let arg = argExpr. simpleIdentifierExpr
581+ let arg = argExpr. simpleIdentifierExpr? . name
561582 {
562583 return values. contains ( arg)
563584 }
@@ -603,14 +624,14 @@ private func diagnoseLikelySimulatorEnvironmentTest(
603624 return nil
604625 }
605626
606- return IfConfigError . likelySimulatorPlatform ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
627+ return IfConfigDiagnostic . likelySimulatorPlatform ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
607628}
608629
609630extension IfConfigClauseSyntax {
610631 /// Fold the operators within an #if condition, turning sequence expressions
611632 /// involving the various allowed operators (&&, ||, !) into well-structured
612633 /// binary operators.
613- public static func foldOperators(
634+ static func foldOperators(
614635 _ condition: some ExprSyntaxProtocol
615636 ) -> ( folded: ExprSyntax , diagnostics: [ Diagnostic ] ) {
616637 var foldingDiagnostics : [ Diagnostic ] = [ ]
@@ -622,7 +643,7 @@ extension IfConfigClauseSyntax {
622643 {
623644
624645 foldingDiagnostics. append (
625- IfConfigError . badInfixOperator ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
646+ IfConfigDiagnostic . badInfixOperator ( syntax: ExprSyntax ( binOp) ) . asDiagnostic
626647 )
627648 return
628649 }
@@ -635,7 +656,7 @@ extension IfConfigClauseSyntax {
635656 /// Determine whether the given expression, when used as the condition in
636657 /// an inactive `#if` clause, implies that syntax errors are permitted within
637658 /// that region.
638- public static func syntaxErrorsAllowed(
659+ static func syntaxErrorsAllowed(
639660 _ condition: some ExprSyntaxProtocol
640661 ) -> ( syntaxErrorsAllowed: Bool , diagnostics: [ Diagnostic ] ) {
641662 let ( foldedCondition, foldingDiagnostics) = IfConfigClauseSyntax . foldOperators ( condition)
@@ -684,7 +705,7 @@ extension ExprSyntaxProtocol {
684705
685706 // Call syntax is for operations.
686707 if let call = self . as ( FunctionCallExprSyntax . self) ,
687- let fnName = call. calledExpression. simpleIdentifierExpr,
708+ let fnName = call. calledExpression. simpleIdentifierExpr? . name ,
688709 let fn = IfConfigFunctions ( rawValue: fnName)
689710 {
690711 return fn. syntaxErrorsAllowed
0 commit comments