|
11 | 11 | //===----------------------------------------------------------------------===//
|
12 | 12 |
|
13 | 13 | import SwiftDiagnostics
|
| 14 | +import SwiftOperators |
14 | 15 | import SwiftSyntax
|
15 | 16 |
|
16 | 17 | /// Evaluate the condition of an `#if`.
|
@@ -433,20 +434,77 @@ func evaluateIfConfig(
|
433 | 434 | }
|
434 | 435 |
|
435 | 436 | extension IfConfigClauseSyntax {
|
436 |
| - /// Determine whether this condition is "syntaxErrorsAllowed". |
437 |
| - func syntaxErrorsAllowed( |
438 |
| - configuration: some BuildConfiguration |
| 437 | + /// Fold the operators within an #if condition, turning sequence expressions |
| 438 | + /// involving the various allowed operators (&&, ||, !) into well-structured |
| 439 | + /// binary operators. |
| 440 | + public static func foldOperators( |
| 441 | + _ condition: some ExprSyntaxProtocol |
| 442 | + ) -> (folded: ExprSyntax, diagnostics: [Diagnostic]) { |
| 443 | + var foldingDiagnostics: [Diagnostic] = [] |
| 444 | + let foldedCondition = OperatorTable.logicalOperators.foldAll(condition) { error in |
| 445 | + foldingDiagnostics.append(contentsOf: error.asDiagnostics(at: condition)) |
| 446 | + }.cast(ExprSyntax.self) |
| 447 | + return (folded: foldedCondition, diagnostics: foldingDiagnostics) |
| 448 | + } |
| 449 | + |
| 450 | + /// Determine whether the given expression, when used as the condition in |
| 451 | + /// an inactive `#if` clause, implies that syntax errors are permitted within |
| 452 | + /// that region. |
| 453 | + public static func syntaxErrorsAllowed( |
| 454 | + _ condition: some ExprSyntaxProtocol |
439 | 455 | ) -> (syntaxErrorsAllowed: Bool, diagnostics: [Diagnostic]) {
|
440 |
| - guard let condition else { |
441 |
| - return (syntaxErrorsAllowed: false, diagnostics: []) |
442 |
| - } |
| 456 | + let (foldedCondition, foldingDiagnostics) = IfConfigClauseSyntax.foldOperators(condition) |
443 | 457 |
|
444 |
| - // Evaluate this condition against the build configuration. |
445 |
| - let (_, syntaxErrorsAllowed, diagnostics) = evaluateIfConfig( |
446 |
| - condition: condition, |
447 |
| - configuration: configuration |
| 458 | + return ( |
| 459 | + !foldingDiagnostics.isEmpty || foldedCondition.allowsSyntaxErrorsFolded, |
| 460 | + foldingDiagnostics |
448 | 461 | )
|
| 462 | + } |
| 463 | +} |
| 464 | + |
| 465 | +extension ExprSyntaxProtocol { |
| 466 | + /// Determine whether this expression, when used as a condition within a #if |
| 467 | + /// that evaluates false, implies that the code contained in that `#if` |
| 468 | + /// |
| 469 | + /// Check whether of allowsSyntaxErrors(_:) that assumes that inputs have |
| 470 | + /// already been operator-folded. |
| 471 | + var allowsSyntaxErrorsFolded: Bool { |
| 472 | + // Logical '!'. |
| 473 | + if let prefixOp = self.as(PrefixOperatorExprSyntax.self), |
| 474 | + prefixOp.operator.text == "!" |
| 475 | + { |
| 476 | + return prefixOp.expression.allowsSyntaxErrorsFolded |
| 477 | + } |
| 478 | + |
| 479 | + // Logical '&&' and '||'. |
| 480 | + if let binOp = self.as(InfixOperatorExprSyntax.self), |
| 481 | + let op = binOp.operator.as(BinaryOperatorExprSyntax.self) |
| 482 | + { |
| 483 | + switch op.operator.text { |
| 484 | + case "&&": |
| 485 | + return binOp.leftOperand.allowsSyntaxErrorsFolded || binOp.rightOperand.allowsSyntaxErrorsFolded |
| 486 | + case "||": |
| 487 | + return binOp.leftOperand.allowsSyntaxErrorsFolded && binOp.rightOperand.allowsSyntaxErrorsFolded |
| 488 | + default: |
| 489 | + return false |
| 490 | + } |
| 491 | + } |
| 492 | + |
| 493 | + // Look through parentheses. |
| 494 | + if let tuple = self.as(TupleExprSyntax.self), tuple.isParentheses, |
| 495 | + let element = tuple.elements.first |
| 496 | + { |
| 497 | + return element.expression.allowsSyntaxErrorsFolded |
| 498 | + } |
| 499 | + |
| 500 | + // Call syntax is for operations. |
| 501 | + if let call = self.as(FunctionCallExprSyntax.self), |
| 502 | + let fnName = call.calledExpression.simpleIdentifierExpr, |
| 503 | + let fn = IfConfigFunctions(rawValue: fnName) |
| 504 | + { |
| 505 | + return fn.syntaxErrorsAllowed |
| 506 | + } |
449 | 507 |
|
450 |
| - return (syntaxErrorsAllowed, diagnostics) |
| 508 | + return false |
451 | 509 | }
|
452 | 510 | }
|
0 commit comments