-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Use the SwiftIfConfig library for evaluating #if conditions #75688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ | |
|
||
import ASTBridging | ||
import SwiftDiagnostics | ||
import SwiftIfConfig | ||
@_spi(Compiler) import SwiftIfConfig | ||
import SwiftParser | ||
import SwiftSyntax | ||
|
||
|
@@ -109,6 +109,13 @@ struct CompilerBuildConfiguration: BuildConfiguration { | |
|
||
func isActiveTargetRuntime(name: String) throws -> Bool { | ||
var name = name | ||
|
||
// Complain if the provided runtime isn't one of the known values. | ||
switch name { | ||
case "_Native", "_ObjC", "_multithreaded": break | ||
default: throw IfConfigError.unexpectedRuntimeCondition | ||
} | ||
|
||
return name.withBridgedString { nameRef in | ||
ctx.langOptsIsActiveTargetRuntime(nameRef) | ||
} | ||
|
@@ -161,6 +168,18 @@ struct CompilerBuildConfiguration: BuildConfiguration { | |
} | ||
} | ||
|
||
enum IfConfigError: Error, CustomStringConvertible { | ||
case unexpectedRuntimeCondition | ||
|
||
var description: String { | ||
switch self { | ||
case .unexpectedRuntimeCondition: | ||
return "unexpected argument for the '_runtime' condition; expected '_Native' or '_ObjC'" | ||
} | ||
} | ||
} | ||
|
||
|
||
/// Extract the #if clause range information for the given source file. | ||
@_cdecl("swift_ASTGen_configuredRegions") | ||
public func configuredRegions( | ||
|
@@ -301,3 +320,53 @@ public func freeConfiguredRegions( | |
) { | ||
UnsafeMutableBufferPointer(start: regions, count: numRegions).deallocate() | ||
} | ||
|
||
/// Evaluate the #if condition at ifClauseLocationPtr. | ||
@_cdecl("swift_ASTGen_evaluatePoundIfCondition") | ||
public func evaluatePoundIfCondition( | ||
astContext: BridgedASTContext, | ||
diagEnginePtr: UnsafeMutableRawPointer, | ||
sourceFileBuffer: BridgedStringRef, | ||
ifConditionText: BridgedStringRef, | ||
shouldEvaluate: Bool | ||
) -> Int { | ||
// Retrieve the #if condition that we're evaluating here. | ||
// FIXME: Use 'ExportedSourceFile' when C++ parser is replaced. | ||
let textBuffer = UnsafeBufferPointer<UInt8>(start: ifConditionText.data, count: ifConditionText.count) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t we have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm following the path @rintaro used for checking the definitions of macros, which avoids running the swift-syntax parser on the whole file (for performance reasons). This also avoids the somewhat brittle "go find this exact swift-syntax node at this source location" lookup we need to do with that approach, which can fail when the C++ parser and SwiftParser don't recover in exactly the same way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I was still thinking we had the syntax tree for the entire file anyway and forgot that we no longer parse it completely. |
||
var parser = Parser(textBuffer) | ||
let conditionExpr = ExprSyntax.parse(from: &parser) | ||
|
||
let isActive: Bool | ||
let syntaxErrorsAllowed: Bool | ||
let diagnostics: [Diagnostic] | ||
if shouldEvaluate { | ||
// Evaluate the condition against the compiler's build configuration. | ||
let configuration = CompilerBuildConfiguration( | ||
ctx: astContext, | ||
sourceBuffer: textBuffer | ||
) | ||
|
||
let state: IfConfigRegionState | ||
(state, syntaxErrorsAllowed, diagnostics) = IfConfigRegionState.evaluating(conditionExpr, in: configuration) | ||
isActive = (state == .active) | ||
} else { | ||
// Don't evaluate the condition, because we know it's inactive. Determine | ||
// whether syntax errors are permitted within this region according to the | ||
// condition. | ||
isActive = false | ||
(syntaxErrorsAllowed, diagnostics) = IfConfigClauseSyntax.syntaxErrorsAllowed(conditionExpr) | ||
} | ||
|
||
// Render the diagnostics. | ||
for diagnostic in diagnostics { | ||
emitDiagnostic( | ||
diagnosticEngine: BridgedDiagnosticEngine(raw: diagEnginePtr), | ||
sourceFileBuffer: UnsafeBufferPointer(start: sourceFileBuffer.data, count: sourceFileBuffer.count), | ||
sourceFileBufferOffset: ifConditionText.data! - sourceFileBuffer.data!, | ||
diagnostic: diagnostic, | ||
diagnosticSeverity: diagnostic.diagMessage.severity | ||
) | ||
} | ||
|
||
return (isActive ? 0x1 : 0) | (syntaxErrorsAllowed ? 0x2 : 0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn’t
_multithreaded
also allowed, based on the check above?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, yes. I was originally following the actually-incorrect diagnostic text in the compiler itself to try to minimize the diffs in this PR. We can fix this now.