1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftDiagnostics
import SwiftSyntax
extension VersionTuple {
/// Parse a compiler build version of the form "5007.*.1.2.3*", which is
/// used by an older if configuration form `_compiler_version("...")`.
/// - Parameters:
/// - versionString: The version string for the compiler build version that
/// we are parsing.
/// - versionSyntax: The syntax node that contains the version string, used
/// only for diagnostic purposes.
static func parseCompilerBuildVersion(
_ versionString: String,
_ versionSyntax: ExprSyntax
) -> (version: VersionTuple?, diagnostics: [Diagnostic]) {
var extraDiagnostics: [Diagnostic] = []
let version: VersionTuple?
do {
version = try parseCompilerBuildVersion(versionString, versionSyntax, extraDiagnostics: &extraDiagnostics)
} catch {
version = nil
extraDiagnostics.append(contentsOf: error.asDiagnostics(at: versionSyntax))
}
return (version, extraDiagnostics)
}
/// Parse a compiler build version of the form "5007.*.1.2.3*", which is
/// used by an older if configuration form `_compiler_version("...")`.
/// - Parameters:
/// - versionString: The version string for the compiler build version that
/// we are parsing.
/// - versionSyntax: The syntax node that contains the version string, used
/// only for diagnostic purposes.
static func parseCompilerBuildVersion(
_ versionString: String,
_ versionSyntax: ExprSyntax,
extraDiagnostics: inout [Diagnostic]
) throws -> VersionTuple {
var components: [Int] = []
// Version value are separated by periods.
let componentStrings = versionString.split(separator: ".", omittingEmptySubsequences: false)
/// Record a component after checking its value.
func recordComponent(_ value: Int) throws {
let limit = components.isEmpty ? 9223371 : 999
if value < 0 || value > limit {
throw IfConfigDiagnostic.compilerVersionOutOfRange(value: value, upperLimit: limit, syntax: versionSyntax)
}
components.append(value)
}
// Parse the components into version values.
for (index, componentString) in componentStrings.enumerated() {
// Check ahead of time for empty version components
if componentString.isEmpty {
throw IfConfigDiagnostic.emptyVersionComponent(syntax: versionSyntax)
}
// The second component is always "*", and is never used for comparison.
if index == 1 {
if componentString != "*" {
extraDiagnostics.append(
IfConfigDiagnostic.compilerVersionSecondComponentNotWildcard(syntax: versionSyntax).asDiagnostic
)
}
try recordComponent(0)
continue
}
// Every other component must be an integer value.
guard let component = Int(componentString) else {
throw IfConfigDiagnostic.invalidVersionOperand(name: "_compiler_version", syntax: versionSyntax)
}
try recordComponent(component)
}
// Only allowed to specify up to 5 version components.
if components.count > 5 {
throw IfConfigDiagnostic.compilerVersionTooManyComponents(syntax: versionSyntax)
}
// In the beginning, '_compiler_version(string-literal)' was designed for a
// different version scheme where the major was fairly large and the minor
// was ignored; now we use one where the minor is significant and major and
// minor match the Swift language version. Specifically, majors 600-1300
// were used for Swift 1.0-5.5 (based on clang versions), but then we reset
// the numbering based on Swift versions, so 5.6 had major 5. We assume
// that majors below 600 use the new scheme and equal/above it use the old
// scheme.
//
// However, we want the string literal variant of '_compiler_version' to
// maintain source compatibility with old checks; that means checks for new
// versions have to be written so that old compilers will think they represent
// newer versions, while new compilers have to interpret old version number
// strings in a way that will compare correctly to the new versions compiled
// into them.
//
// To achieve this, modern compilers divide the major by 1000 and overwrite
// the wildcard component with the remainder, effectively shifting the last
// three digits of the major into the minor, before comparing it to the
// compiler version:
//
// _compiler_version("5007.*.1.2.3") -> 5.7.1.2.3
// _compiler_version("1300.*.1.2.3") -> 1.300.1.2.3 (smaller than 5.6)
// _compiler_version( "600.*.1.2.3") -> 0.600.1.2.3 (smaller than 5.6)
//
// So if you want to specify a 5.7.z.a.b version, we ask users to either
// write it as 5007.*.z.a.b, or to use the new 'compiler(>= version)'
// syntax instead, which does not perform this conversion.
if !components.isEmpty {
if components.count > 1 {
components[1] = components[0] % 1000
}
components[0] = components[0] / 1000
}
return VersionTuple(components: components)
}
}
|