diff --git a/Sources/SwiftIfConfig/IfConfigEvaluation.swift b/Sources/SwiftIfConfig/IfConfigEvaluation.swift index b645a587b1f..ac4b95e350d 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 '||'. @@ -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/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/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 ee273865f28..5ea79172176 100644 --- a/Tests/SwiftIfConfigTest/EvaluateTests.swift +++ b/Tests/SwiftIfConfigTest/EvaluateTests.swift @@ -176,12 +176,25 @@ 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) 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", @@ -206,6 +219,17 @@ public class EvaluateTests: XCTestCase { ) ] ) + assertIfConfig( + #"_compiler_version("...")"#, + .unparsed, + diagnostics: [ + DiagnosticSpec( + message: "found empty version component", + line: 1, + column: 20 + ) + ] + ) } func testCanImport() throws { @@ -269,7 +293,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.