From 73cfd080867e7d4e965b45dd53ec7a1ea898a93b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Aug 2024 21:57:10 -0700 Subject: [PATCH 1/4] Expand IfConfigRegionState.evaluate() result to include syntaxErrorsAllowed The compiler needs this information, and other clients probably will, too. --- Sources/SwiftIfConfig/IfConfigRegionState.swift | 12 ++++++------ Tests/SwiftIfConfigTest/EvaluateTests.swift | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftIfConfig/IfConfigRegionState.swift b/Sources/SwiftIfConfig/IfConfigRegionState.swift index 1c887f62fd5..50a1ab0369b 100644 --- a/Sources/SwiftIfConfig/IfConfigRegionState.swift +++ b/Sources/SwiftIfConfig/IfConfigRegionState.swift @@ -25,12 +25,12 @@ public enum IfConfigRegionState { case active /// Evaluate the given `#if` condition using the given build configuration - /// to determine its state and identify any problems encountered along the - /// way. + /// to determine its state, whether syntax errors in inactive conditions are + /// permitted, and to identify any problems encountered along the way. public static func evaluating( _ condition: some ExprSyntaxProtocol, in configuration: some BuildConfiguration - ) -> (state: IfConfigRegionState, diagnostics: [Diagnostic]) { + ) -> (state: IfConfigRegionState, syntaxErrorsAllowed: Bool, diagnostics: [Diagnostic]) { // Apply operator folding for !/&&/||. var foldingDiagnostics: [Diagnostic] = [] let foldedCondition = OperatorTable.logicalOperators.foldAll(condition) { error in @@ -44,9 +44,9 @@ public enum IfConfigRegionState { let diagnostics = foldingDiagnostics + evalDiagnostics switch (active, syntaxErrorsAllowed) { - case (true, _): return (.active, diagnostics) - case (false, false): return (.inactive, diagnostics) - case (false, true): return (.unparsed, diagnostics) + case (true, _): return (.active, syntaxErrorsAllowed, diagnostics) + case (false, false): return (.inactive, syntaxErrorsAllowed, diagnostics) + case (false, true): return (.unparsed, syntaxErrorsAllowed, diagnostics) } } } diff --git a/Tests/SwiftIfConfigTest/EvaluateTests.swift b/Tests/SwiftIfConfigTest/EvaluateTests.swift index ee273865f28..d8908c6c131 100644 --- a/Tests/SwiftIfConfigTest/EvaluateTests.swift +++ b/Tests/SwiftIfConfigTest/EvaluateTests.swift @@ -269,7 +269,7 @@ fileprivate func assertIfConfig( // Evaluate the condition to check the state. let actualDiagnostics: [Diagnostic] let actualState: IfConfigRegionState - (actualState, actualDiagnostics) = IfConfigRegionState.evaluating(condition, in: configuration) + (actualState, _, actualDiagnostics) = IfConfigRegionState.evaluating(condition, in: configuration) XCTAssertEqual(actualState, expectedState, file: file, line: line) // Check the diagnostics. From 2d6b282e777502c19f45b5d85c0fe08f84ccb56f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Aug 2024 22:19:34 -0700 Subject: [PATCH 2/4] [Version parsing] Fix precondition failure with no characters in between .'s Uncovered during compiler testing. --- .../SwiftIfConfig/VersionTuple+Parsing.swift | 2 +- Sources/SwiftIfConfig/VersionTuple.swift | 2 +- Tests/SwiftIfConfigTest/EvaluateTests.swift | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftIfConfig/VersionTuple+Parsing.swift b/Sources/SwiftIfConfig/VersionTuple+Parsing.swift index c8dc16eb8ec..c5d301c2991 100644 --- a/Sources/SwiftIfConfig/VersionTuple+Parsing.swift +++ b/Sources/SwiftIfConfig/VersionTuple+Parsing.swift @@ -27,7 +27,7 @@ extension VersionTuple { components = [] // Version value are separated by periods. - let componentStrings = versionString.split(separator: ".") + let componentStrings = versionString.split(separator: ".", omittingEmptySubsequences: false) /// Record a component after checking its value. func recordComponent(_ value: Int) throws { diff --git a/Sources/SwiftIfConfig/VersionTuple.swift b/Sources/SwiftIfConfig/VersionTuple.swift index 53a5eb1d034..d31d544b59b 100644 --- a/Sources/SwiftIfConfig/VersionTuple.swift +++ b/Sources/SwiftIfConfig/VersionTuple.swift @@ -33,7 +33,7 @@ public struct VersionTuple: Sendable { public init?(parsing string: String) { self.components = [] - for componentText in string.split(separator: ".") { + for componentText in string.split(separator: ".", omittingEmptySubsequences: false) { guard let component = Int(componentText) else { return nil } diff --git a/Tests/SwiftIfConfigTest/EvaluateTests.swift b/Tests/SwiftIfConfigTest/EvaluateTests.swift index d8908c6c131..f3a52546192 100644 --- a/Tests/SwiftIfConfigTest/EvaluateTests.swift +++ b/Tests/SwiftIfConfigTest/EvaluateTests.swift @@ -176,6 +176,17 @@ public class EvaluateTests: XCTestCase { assertIfConfig("swift(>=5.5)", .active) assertIfConfig("swift(<6)", .active) assertIfConfig("swift(>=6)", .unparsed) + assertIfConfig( + "swift(>=...)", + .unparsed, + diagnostics: [ + DiagnosticSpec( + message: "'swift' version check has invalid version ''", + line: 1, + column: 9 + ) + ] + ) assertIfConfig("compiler(>=5.8)", .active) assertIfConfig("compiler(>=5.9)", .active) assertIfConfig("compiler(>=5.10)", .unparsed) @@ -206,6 +217,17 @@ public class EvaluateTests: XCTestCase { ) ] ) + assertIfConfig( + #"_compiler_version("...")"#, + .unparsed, + diagnostics: [ + DiagnosticSpec( + message: "found empty version component", + line: 1, + column: 20 + ) + ] + ) } func testCanImport() throws { From ec6b09ff386b8c1f5f8cc654ed9279a6ec3c4351 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Aug 2024 22:23:49 -0700 Subject: [PATCH 3/4] Fix capitalization issue in variable name --- Sources/SwiftIfConfig/IfConfigEvaluation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftIfConfig/IfConfigEvaluation.swift b/Sources/SwiftIfConfig/IfConfigEvaluation.swift index b645a587b1f..c361bfcb0cf 100644 --- a/Sources/SwiftIfConfig/IfConfigEvaluation.swift +++ b/Sources/SwiftIfConfig/IfConfigEvaluation.swift @@ -109,12 +109,12 @@ func evaluateIfConfig( if let prefixOp = condition.as(PrefixOperatorExprSyntax.self), prefixOp.operator.text == "!" { - let (innerActive, innersyntaxErrorsAllowed, innerDiagnostics) = evaluateIfConfig( + let (innerActive, innerSyntaxErrorsAllowed, innerDiagnostics) = evaluateIfConfig( condition: prefixOp.expression, configuration: configuration ) - return (active: !innerActive, syntaxErrorsAllowed: innersyntaxErrorsAllowed, diagnostics: innerDiagnostics) + return (active: !innerActive, syntaxErrorsAllowed: innerSyntaxErrorsAllowed, diagnostics: innerDiagnostics) } // Logical '&&' and '||'. From 19445c001bab1615e30c9ed1895100c9cac169e4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Aug 2024 22:31:32 -0700 Subject: [PATCH 4/4] Enable the >=version syntax for `#if _compiler_version` In addition to the string-literal form, this compilation conditional handles the same version check syntax as `#if swift` and `#if compiler`. --- Sources/SwiftIfConfig/IfConfigEvaluation.swift | 8 +------- Tests/SwiftIfConfigTest/EvaluateTests.swift | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftIfConfig/IfConfigEvaluation.swift b/Sources/SwiftIfConfig/IfConfigEvaluation.swift index c361bfcb0cf..ac4b95e350d 100644 --- a/Sources/SwiftIfConfig/IfConfigEvaluation.swift +++ b/Sources/SwiftIfConfig/IfConfigEvaluation.swift @@ -334,13 +334,7 @@ func evaluateIfConfig( let segment = stringLiteral.segments.first, case .stringSegment(let stringSegment) = segment else { - return recordError( - .requiresUnlabeledArgument( - name: "_compiler_version", - role: "version", - syntax: ExprSyntax(call) - ) - ) + return doVersionComparisonCheck(configuration.compilerVersion) } let versionString = stringSegment.content.text diff --git a/Tests/SwiftIfConfigTest/EvaluateTests.swift b/Tests/SwiftIfConfigTest/EvaluateTests.swift index f3a52546192..5ea79172176 100644 --- a/Tests/SwiftIfConfigTest/EvaluateTests.swift +++ b/Tests/SwiftIfConfigTest/EvaluateTests.swift @@ -193,6 +193,8 @@ public class EvaluateTests: XCTestCase { assertIfConfig(#"_compiler_version("5009.*.1")"#, .active) assertIfConfig(#"_compiler_version("5009.*.3.2.3")"#, .unparsed) assertIfConfig(#"_compiler_version("5010.*.0")"#, .unparsed) + assertIfConfig("_compiler_version(>=5.8)", .active) + assertIfConfig("_compiler_version(>=12.0)", .unparsed) assertIfConfig("compiler(>=5.10) && 3.14159", .unparsed) assertIfConfig( "compiler(>=5.10) || 3.14159",