22//
33// This source file is part of the Swift.org open source project
44//
5- // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
5+ // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66// Licensed under Apache License v2.0 with Runtime Library Exception
77//
88// See https://swift.org/LICENSE.txt for license information
@@ -13,231 +13,6 @@ import SwiftDiagnostics
1313import SwiftOperators
1414import SwiftSyntax
1515
16- enum IfConfigError : Error , CustomStringConvertible {
17- case unknownExpression( ExprSyntax )
18- case unhandledFunction( name: String , syntax: ExprSyntax )
19- case requiresUnlabeledArgument( name: String , role: String , syntax: ExprSyntax )
20- case unsupportedVersionOperator( name: String , operator: TokenSyntax )
21- case invalidVersionOperand( name: String , syntax: ExprSyntax )
22- case emptyVersionComponent( syntax: ExprSyntax )
23- case compilerVersionOutOfRange( value: Int , upperLimit: Int , syntax: ExprSyntax )
24- case compilerVersionSecondComponentNotWildcard( syntax: ExprSyntax )
25- case compilerVersionTooManyComponents( syntax: ExprSyntax )
26- case canImportMissingModule( syntax: ExprSyntax )
27- case canImportLabel( syntax: ExprSyntax )
28- case canImportTwoParameters( syntax: ExprSyntax )
29- case ignoredTrailingComponents( version: VersionTuple , syntax: ExprSyntax )
30- case integerLiteralCondition( syntax: ExprSyntax , replacement: Bool )
31-
32- var description : String {
33- switch self {
34- case . unknownExpression:
35- return " invalid conditional compilation expression "
36-
37- case . unhandledFunction( name: let name, syntax: _) :
38- return " build configuration cannot handle ' \( name) ' "
39-
40- case . requiresUnlabeledArgument( name: let name, role: let role, syntax: _) :
41- return " \( name) requires a single unlabeled argument for the \( role) "
42-
43- case . unsupportedVersionOperator( name: let name, operator: let op) :
44- return " ' \( name) ' version check does not support operator ' \( op. trimmedDescription) ' "
45-
46- case . invalidVersionOperand( name: let name, syntax: let version) :
47- return " ' \( name) ' version check has invalid version ' \( version. trimmedDescription) ' "
48-
49- case . emptyVersionComponent( syntax: _) :
50- return " found empty version component "
51-
52- case . compilerVersionOutOfRange( value: _, upperLimit: let upperLimit, syntax: _) :
53- // FIXME: This matches the C++ implementation, but it would be more useful to
54- // provide the actual value as-written and avoid the mathy [0, N] syntax.
55- return " compiler version component out of range: must be in [0, \( upperLimit) ] "
56-
57- case . compilerVersionSecondComponentNotWildcard( syntax: _) :
58- return " the second version component is not used for comparison in legacy compiler versions "
59-
60- case . compilerVersionTooManyComponents( syntax: _) :
61- return " compiler version must not have more than five components "
62-
63- case . canImportMissingModule( syntax: _) :
64- return " canImport requires a module name "
65-
66- case . canImportLabel( syntax: _) :
67- return " 2nd parameter of canImport should be labeled as _version or _underlyingVersion "
68-
69- case . canImportTwoParameters( syntax: _) :
70- return " canImport can take only two parameters "
71-
72- case . ignoredTrailingComponents( version: let version, syntax: _) :
73- return " trailing components of version ' \( version. description) ' are ignored "
74-
75- case . integerLiteralCondition( syntax: let syntax, replacement: let replacement) :
76- return " ' \( syntax. trimmedDescription) ' is not a valid conditional compilation expression, use ' \( replacement) ' "
77- }
78- }
79-
80- /// Retrieve the syntax node associated with this error.
81- var syntax : Syntax {
82- switch self {
83- case . unknownExpression( let syntax) ,
84- . unhandledFunction( name: _, syntax: let syntax) ,
85- . requiresUnlabeledArgument( name: _, role: _, syntax: let syntax) ,
86- . invalidVersionOperand( name: _, syntax: let syntax) ,
87- . emptyVersionComponent( syntax: let syntax) ,
88- . compilerVersionOutOfRange( value: _, upperLimit: _, syntax: let syntax) ,
89- . compilerVersionTooManyComponents( syntax: let syntax) ,
90- . compilerVersionSecondComponentNotWildcard( syntax: let syntax) ,
91- . canImportMissingModule( syntax: let syntax) ,
92- . canImportLabel( syntax: let syntax) ,
93- . canImportTwoParameters( syntax: let syntax) ,
94- . ignoredTrailingComponents( version: _, syntax: let syntax) ,
95- . integerLiteralCondition( syntax: let syntax, replacement: _) :
96- return Syntax ( syntax)
97-
98- case . unsupportedVersionOperator( name: _, operator: let op) :
99- return Syntax ( op)
100- }
101- }
102- }
103-
104- extension IfConfigError : DiagnosticMessage {
105- var message : String { description }
106-
107- var diagnosticID : MessageID {
108- . init( domain: " SwiftIfConfig " , id: " IfConfigError " )
109- }
110-
111- var severity : SwiftDiagnostics . DiagnosticSeverity {
112- switch self {
113- case . ignoredTrailingComponents: return . warning
114- default : return . error
115- }
116- }
117-
118- private struct SimpleFixItMessage : FixItMessage {
119- var message : String
120-
121- var fixItID : MessageID {
122- . init( domain: " SwiftIfConfig " , id: " IfConfigFixIt " )
123- }
124- }
125-
126- var asDiagnostic : Diagnostic {
127- // For the integer literal condition we have a Fix-It.
128- if case . integerLiteralCondition( let syntax, let replacement) = self {
129- return Diagnostic (
130- node: syntax,
131- message: self ,
132- fixIt: . replace(
133- message: SimpleFixItMessage (
134- message: " replace with Boolean literal ' \( replacement) ' "
135- ) ,
136- oldNode: syntax,
137- newNode: BooleanLiteralExprSyntax (
138- literal: . keyword( replacement ? . true : . false )
139- )
140- )
141- )
142- }
143-
144- return Diagnostic ( node: syntax, message: self )
145- }
146- }
147-
148- extension VersionTuple {
149- /// Parse a compiler build version of the form "5007.*.1.2.3*", which is
150- /// used by an older if configuration form `_compiler_version("...")`.
151- /// - Parameters:
152- /// - versionString: The version string for the compiler build version that
153- /// we are parsing.
154- /// - versionSyntax: The syntax node that contains the version string, used
155- /// only for diagnostic purposes.
156- fileprivate init (
157- parsingCompilerBuildVersion versionString: String ,
158- _ versionSyntax: ExprSyntax
159- ) throws {
160- components = [ ]
161-
162- // Version value are separated by periods.
163- let componentStrings = versionString. split ( separator: " . " )
164-
165- /// Record a component after checking its value.
166- func recordComponent( _ value: Int ) throws {
167- let limit = components. isEmpty ? 9223371 : 999
168- if value < 0 || value > limit {
169- throw IfConfigError . compilerVersionOutOfRange ( value: value, upperLimit: limit, syntax: versionSyntax)
170- }
171-
172- components. append ( value)
173- }
174-
175- // Parse the components into version values.
176- for (index, componentString) in componentStrings. enumerated ( ) {
177- // Check ahead of time for empty version components
178- if componentString. isEmpty {
179- throw IfConfigError . emptyVersionComponent ( syntax: versionSyntax)
180- }
181-
182- // The second component is always "*", and is never used for comparison.
183- if index == 1 {
184- if componentString != " * " {
185- throw IfConfigError . compilerVersionSecondComponentNotWildcard ( syntax: versionSyntax)
186- }
187- try recordComponent ( 0 )
188- continue
189- }
190-
191- // Every other component must be an integer value.
192- guard let component = Int ( componentString) else {
193- throw IfConfigError . invalidVersionOperand ( name: " _compiler_version " , syntax: versionSyntax)
194- }
195-
196- try recordComponent ( component)
197- }
198-
199- // Only allowed to specify up to 5 version components.
200- if components. count > 5 {
201- throw IfConfigError . compilerVersionTooManyComponents ( syntax: versionSyntax)
202- }
203-
204- // In the beginning, '_compiler_version(string-literal)' was designed for a
205- // different version scheme where the major was fairly large and the minor
206- // was ignored; now we use one where the minor is significant and major and
207- // minor match the Swift language version. Specifically, majors 600-1300
208- // were used for Swift 1.0-5.5 (based on clang versions), but then we reset
209- // the numbering based on Swift versions, so 5.6 had major 5. We assume
210- // that majors below 600 use the new scheme and equal/above it use the old
211- // scheme.
212- //
213- // However, we want the string literal variant of '_compiler_version' to
214- // maintain source compatibility with old checks; that means checks for new
215- // versions have to be written so that old compilers will think they represent
216- // newer versions, while new compilers have to interpret old version number
217- // strings in a way that will compare correctly to the new versions compiled
218- // into them.
219- //
220- // To achieve this, modern compilers divide the major by 1000 and overwrite
221- // the wildcard component with the remainder, effectively shifting the last
222- // three digits of the major into the minor, before comparing it to the
223- // compiler version:
224- //
225- // _compiler_version("5007.*.1.2.3") -> 5.7.1.2.3
226- // _compiler_version("1300.*.1.2.3") -> 1.300.1.2.3 (smaller than 5.6)
227- // _compiler_version( "600.*.1.2.3") -> 0.600.1.2.3 (smaller than 5.6)
228- //
229- // So if you want to specify a 5.7.z.a.b version, we ask users to either
230- // write it as 5007.*.z.a.b, or to use the new 'compiler(>= version)'
231- // syntax instead, which does not perform this conversion.
232- if !components. isEmpty {
233- if components. count > 1 {
234- components [ 1 ] = components [ 0 ] % 1000
235- }
236- components [ 0 ] = components [ 0 ] / 1000
237- }
238- }
239- }
240-
24116/// Evaluate the condition of an `#if`.
24217/// - Parameters:
24318/// - condition: The condition to evaluate, which we assume has already been
@@ -247,7 +22,8 @@ extension VersionTuple {
24722/// - diagnosticHandler: Receives any diagnostics that are produced by the
24823/// evaluation, whether from errors in the source code or produced by the
24924/// build configuration itself.
250- /// - Throws: Throws if an error occurs occur during evaluation. The error will
25+ /// - Throws: Throws if an error occurs occur during evaluation that prevents
26+ /// this function from forming a valid result. The error will
25127/// also be provided to the diagnostic handler before doing so.
25228/// - Returns: A pair of Boolean values. The first describes whether the
25329/// condition holds with the given build configuration. The second whether
0 commit comments