From 87f8b94c26c023875524163f9bcf3c59353c05ba Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 23 Jul 2024 22:16:36 -0700 Subject: [PATCH] Build SourceKit-LSP in the Swift 6 language mode rdar://126553799 --- Package.swift | 84 ++++++------------ Sources/Diagnose/IndexCommand.swift | 4 - .../StderrStreamConcurrencySafe.swift | 9 +- Sources/LSPLogging/NonDarwinLogging.swift | 9 +- .../LSPLogging/SetGlobalLogFileHandler.swift | 9 +- Sources/LSPTestSupport/Assertions.swift | 54 ++++++------ .../TestJSONRPCConnection.swift | 9 +- Sources/SKCore/BuildServerBuildSystem.swift | 8 +- ...cumentURI+CustomLogStringConvertible.swift | 4 - .../SemanticIndex/SemanticIndexManager.swift | 15 ++-- .../DynamicallyLoadedSourceKitD.swift | 4 +- Sources/SourceKitD/dlopen.swift | 48 ++++++----- .../Clang/ClangLanguageService.swift | 12 ++- .../IndexStoreDB+MainFilesProvider.swift | 4 - Sources/SourceKitLSP/SourceKitLSPServer.swift | 85 ++++++++++++++++--- Sources/SourceKitLSP/TestDiscovery.swift | 11 ++- Sources/SwiftExtensions/ThreadSafeBox.swift | 6 +- Sources/sourcekit-lsp/SourceKitLSP.swift | 20 ++--- Tests/LSPLoggingTests/LoggingTests.swift | 1 + .../ConnectionTests.swift | 9 +- .../BuildServerBuildSystemTests.swift | 8 +- Tests/SKSupportTests/DebouncerTests.swift | 5 +- Tests/SourceKitLSPTests/DefinitionTests.swift | 1 + .../ExpectedIndexTaskTracker.swift | 8 +- Tests/SourceKitLSPTests/LifecycleTests.swift | 1 + .../PullDiagnosticsTests.swift | 6 +- 26 files changed, 261 insertions(+), 173 deletions(-) diff --git a/Package.swift b/Package.swift index ff456bd3f..b6b4714d3 100644 --- a/Package.swift +++ b/Package.swift @@ -3,12 +3,6 @@ import Foundation import PackageDescription -let strictConcurrencySettings: [SwiftSetting] = [ - .enableUpcomingFeature("StrictConcurrency"), - .enableUpcomingFeature("RegionBasedIsolation"), - .enableUpcomingFeature("InferSendableFromCaptures"), -] - let package = Package( name: "SourceKitLSP", platforms: [.macOS(.v13)], @@ -40,7 +34,6 @@ let package = Package( .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings, linkerSettings: sourcekitLSPLinkSettings ), @@ -52,8 +45,7 @@ let package = Package( dependencies: [ "LanguageServerProtocol" ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), // MARK: CAtomics @@ -94,8 +86,7 @@ let package = Package( .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -108,8 +99,7 @@ let package = Package( "SKCore", "SKTestSupport", .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: InProcessClient @@ -122,8 +112,7 @@ let package = Package( "SKCore", "SourceKitLSP", ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), // MARK: LanguageServerProtocol @@ -131,8 +120,7 @@ let package = Package( .target( name: "LanguageServerProtocol", dependencies: [], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -140,8 +128,7 @@ let package = Package( dependencies: [ "LanguageServerProtocol", "LSPTestSupport", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: LanguageServerProtocolJSONRPC @@ -153,8 +140,7 @@ let package = Package( "LanguageServerProtocol", "LSPLogging", ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -162,8 +148,7 @@ let package = Package( dependencies: [ "LanguageServerProtocolJSONRPC", "LSPTestSupport", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: LSPLogging @@ -176,7 +161,7 @@ let package = Package( .product(name: "Crypto", package: "swift-crypto"), ], exclude: ["CMakeLists.txt"], - swiftSettings: lspLoggingSwiftSettings + strictConcurrencySettings + swiftSettings: lspLoggingSwiftSettings ), .testTarget( @@ -184,8 +169,7 @@ let package = Package( dependencies: [ "LSPLogging", "SKTestSupport", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: LSPTestSupport @@ -198,8 +182,7 @@ let package = Package( "LanguageServerProtocolJSONRPC", "SKSupport", "SwiftExtensions", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SemanticIndex @@ -213,8 +196,7 @@ let package = Package( "SwiftExtensions", .product(name: "IndexStoreDB", package: "indexstore-db"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -223,8 +205,7 @@ let package = Package( "LSPLogging", "SemanticIndex", "SKTestSupport", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SKCore @@ -244,8 +225,7 @@ let package = Package( .product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -253,8 +233,7 @@ let package = Package( dependencies: [ "SKCore", "SKTestSupport", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SKSupport @@ -270,8 +249,7 @@ let package = Package( "SwiftExtensions", .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -281,8 +259,7 @@ let package = Package( "SKSupport", "SKTestSupport", "SwiftExtensions", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SKSwiftPMWorkspace @@ -298,8 +275,7 @@ let package = Package( .product(name: "SwiftPM-auto", package: "swift-package-manager"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -313,8 +289,7 @@ let package = Package( "SourceKitLSP", .product(name: "SwiftPM-auto", package: "swift-package-manager"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SKTestSupport @@ -333,8 +308,7 @@ let package = Package( .product(name: "ISDBTestSupport", package: "indexstore-db"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - resources: [.copy("INPUTS")], - swiftSettings: strictConcurrencySettings + resources: [.copy("INPUTS")] ), // MARK: SourceKitD @@ -348,8 +322,7 @@ let package = Package( "SwiftExtensions", .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), ], - exclude: ["CMakeLists.txt", "sourcekitd_uids.swift.gyb"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt", "sourcekitd_uids.swift.gyb"] ), .testTarget( @@ -359,8 +332,7 @@ let package = Package( "SKCore", "SKTestSupport", "SwiftExtensions", - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SourceKitLSP @@ -390,8 +362,7 @@ let package = Package( .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SwiftPM-auto", package: "swift-package-manager"), ], - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), .testTarget( @@ -416,18 +387,17 @@ let package = Package( // be used by test cases that test macros (see `SwiftPMTestProject.macroPackageManifest`). .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - ], - swiftSettings: strictConcurrencySettings + ] ), // MARK: SwiftExtensions .target( name: "SwiftExtensions", - exclude: ["CMakeLists.txt"], - swiftSettings: strictConcurrencySettings + exclude: ["CMakeLists.txt"] ), - ] + ], + swiftLanguageVersions: [.v5, .version("6")] ) // MARK: - Parse build arguments diff --git a/Sources/Diagnose/IndexCommand.swift b/Sources/Diagnose/IndexCommand.swift index d3ac5c533..94a36173e 100644 --- a/Sources/Diagnose/IndexCommand.swift +++ b/Sources/Diagnose/IndexCommand.swift @@ -120,8 +120,4 @@ fileprivate extension SourceKitLSPServer { } } -#if compiler(>=6) -extension ExperimentalFeature: @retroactive ExpressibleByArgument {} -#else extension ExperimentalFeature: ExpressibleByArgument {} -#endif diff --git a/Sources/Diagnose/StderrStreamConcurrencySafe.swift b/Sources/Diagnose/StderrStreamConcurrencySafe.swift index 93bc48f14..280474890 100644 --- a/Sources/Diagnose/StderrStreamConcurrencySafe.swift +++ b/Sources/Diagnose/StderrStreamConcurrencySafe.swift @@ -10,11 +10,16 @@ // //===----------------------------------------------------------------------===// -import TSCLibc - import class TSCBasic.LocalFileOutputByteStream import class TSCBasic.ThreadSafeOutputByteStream +#if canImport(Darwin) +import TSCLibc +#else +// FIXME: (async-workaround) @preconcurrency needed because stderr is not sendable on Linux rdar://125578486 +@preconcurrency import TSCLibc +#endif + // A version of `stderrStream` from `TSCBasic` that is a `let` and can thus be used from Swift 6. let stderrStreamConcurrencySafe: ThreadSafeOutputByteStream = try! ThreadSafeOutputByteStream( LocalFileOutputByteStream( diff --git a/Sources/LSPLogging/NonDarwinLogging.swift b/Sources/LSPLogging/NonDarwinLogging.swift index 6f6c71859..5f652425a 100644 --- a/Sources/LSPLogging/NonDarwinLogging.swift +++ b/Sources/LSPLogging/NonDarwinLogging.swift @@ -10,9 +10,16 @@ // //===----------------------------------------------------------------------===// -import Foundation import SwiftExtensions +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because DateFormatter and stderr are not marked as Sendable on Linux +// rdar://125578486, rdar://132378589 +@preconcurrency import Foundation +#endif + // MARK: - Log settings package enum LogConfig { diff --git a/Sources/LSPLogging/SetGlobalLogFileHandler.swift b/Sources/LSPLogging/SetGlobalLogFileHandler.swift index f20eefbe5..4fbdda5c2 100644 --- a/Sources/LSPLogging/SetGlobalLogFileHandler.swift +++ b/Sources/LSPLogging/SetGlobalLogFileHandler.swift @@ -10,9 +10,16 @@ // //===----------------------------------------------------------------------===// -import Foundation import RegexBuilder +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because DateFormatter and stderr are not marked as Sendable on Linux +// rdar://125578486, rdar://132378589 +@preconcurrency import Foundation +#endif + #if os(Windows) import WinSDK #endif diff --git a/Sources/LSPTestSupport/Assertions.swift b/Sources/LSPTestSupport/Assertions.swift index ea18446c3..124140301 100644 --- a/Sources/LSPTestSupport/Assertions.swift +++ b/Sources/LSPTestSupport/Assertions.swift @@ -112,36 +112,34 @@ package func unwrap( return try XCTUnwrap(expression, file: file, line: line) } -extension XCTestCase { - private struct ExpectationNotFulfilledError: Error, CustomStringConvertible { - var expecatations: [XCTestExpectation] +private struct ExpectationNotFulfilledError: Error, CustomStringConvertible { + var expecatations: [XCTestExpectation] - var description: String { - return """ - One of the expectation was not fulfilled within timeout: \ - \(expecatations.map(\.description).joined(separator: ", ")) - """ - } + var description: String { + return """ + One of the expectation was not fulfilled within timeout: \ + \(expecatations.map(\.description).joined(separator: ", ")) + """ } +} - /// Wait for the given expectations to be fulfilled. If the expectations aren't - /// fulfilled within `timeout`, throw an error, aborting the test execution. - package func fulfillmentOfOrThrow( - _ expectations: [XCTestExpectation], - timeout: TimeInterval = defaultTimeout, - enforceOrder enforceOrderOfFulfillment: Bool = false - ) async throws { - // `XCTWaiter.fulfillment` was introduced in the macOS 13.3 SDK but marked as being available on macOS 10.15. - // At the same time that XCTWaiter.fulfillment was introduced `XCTWaiter.wait` was deprecated in async contexts. - // This means that we can't write code that compiles without warnings with both the macOS 13.3 and any previous SDK. - // Accepting the warning here when compiling with macOS 13.3 or later is the only thing that I know of that we can do here. - let started = await XCTWaiter.fulfillment( - of: expectations, - timeout: timeout, - enforceOrder: enforceOrderOfFulfillment - ) - if started != .completed { - throw ExpectationNotFulfilledError(expecatations: expectations) - } +/// Wait for the given expectations to be fulfilled. If the expectations aren't +/// fulfilled within `timeout`, throw an error, aborting the test execution. +package nonisolated func fulfillmentOfOrThrow( + _ expectations: [XCTestExpectation], + timeout: TimeInterval = defaultTimeout, + enforceOrder enforceOrderOfFulfillment: Bool = false +) async throws { + // `XCTWaiter.fulfillment` was introduced in the macOS 13.3 SDK but marked as being available on macOS 10.15. + // At the same time that XCTWaiter.fulfillment was introduced `XCTWaiter.wait` was deprecated in async contexts. + // This means that we can't write code that compiles without warnings with both the macOS 13.3 and any previous SDK. + // Accepting the warning here when compiling with macOS 13.3 or later is the only thing that I know of that we can do here. + let started = await XCTWaiter.fulfillment( + of: expectations, + timeout: timeout, + enforceOrder: enforceOrderOfFulfillment + ) + if started != .completed { + throw ExpectationNotFulfilledError(expecatations: expectations) } } diff --git a/Sources/LSPTestSupport/TestJSONRPCConnection.swift b/Sources/LSPTestSupport/TestJSONRPCConnection.swift index fd799c24c..fd157df20 100644 --- a/Sources/LSPTestSupport/TestJSONRPCConnection.swift +++ b/Sources/LSPTestSupport/TestJSONRPCConnection.swift @@ -17,7 +17,12 @@ import SKSupport import SwiftExtensions import XCTest +#if canImport(Darwin) import class Foundation.Pipe +#else +// FIMXE: (async-workaround) @preconcurrency needed because Pipe is not marked as Sendable on Linux rdar://132378792 +@preconcurrency import class Foundation.Pipe +#endif package final class TestJSONRPCConnection: Sendable { package let clientToServer: Pipe = Pipe() @@ -202,11 +207,7 @@ private let testMessageRegistry = MessageRegistry( notifications: [EchoNotification.self, ShowMessageNotification.self] ) -#if compiler(<5.11) extension String: ResponseType {} -#else -extension String: @retroactive ResponseType {} -#endif package struct EchoRequest: RequestType { package static let method: String = "test_server/echo" diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index c200fa817..e5b95eefc 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// import BuildServerProtocol -import Foundation import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC @@ -26,6 +25,13 @@ import var TSCBasic.localFileSystem import func TSCBasic.lookupExecutablePath import func TSCBasic.resolveSymlinks +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because Pipe is not marked as Sendable on Linux rdar://132378792 +@preconcurrency import Foundation +#endif + enum BuildServerTestError: Error { case executableNotFound(String) } diff --git a/Sources/SKSupport/DocumentURI+CustomLogStringConvertible.swift b/Sources/SKSupport/DocumentURI+CustomLogStringConvertible.swift index c89618553..b95a34c92 100644 --- a/Sources/SKSupport/DocumentURI+CustomLogStringConvertible.swift +++ b/Sources/SKSupport/DocumentURI+CustomLogStringConvertible.swift @@ -21,8 +21,4 @@ extension DocumentURI { return "" } } -#if compiler(<5.11) extension DocumentURI: CustomLogStringConvertible {} -#else -extension DocumentURI: @retroactive CustomLogStringConvertible {} -#endif diff --git a/Sources/SemanticIndex/SemanticIndexManager.swift b/Sources/SemanticIndex/SemanticIndexManager.swift index bbb0462ce..cfcbd721e 100644 --- a/Sources/SemanticIndex/SemanticIndexManager.swift +++ b/Sources/SemanticIndex/SemanticIndexManager.swift @@ -238,7 +238,10 @@ package final actor SemanticIndexManager { /// Returns immediately after scheduling that task. /// /// Indexing is being performed with a low priority. - private func scheduleBackgroundIndex(files: some Collection, indexFilesWithUpToDateUnit: Bool) async { + private func scheduleBackgroundIndex( + files: some Collection & Sendable, + indexFilesWithUpToDateUnit: Bool + ) async { _ = await self.scheduleIndexing(of: files, indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit, priority: .low) } @@ -270,7 +273,9 @@ package final actor SemanticIndexManager { // potentially not knowing about unit files, which causes the corresponding source files to be re-indexed. index.pollForUnitChangesAndWait() await testHooks.buildGraphGenerationDidFinish?() - var filesToIndex: any Collection = await self.buildSystemManager.sourceFiles().lazy.map(\.uri) + // FIXME: (async-workaround) Ideally this would be a type like any Collection & Sendable but that + // doesn't work due to rdar://132374933 + var filesToIndex: [DocumentURI] = await self.buildSystemManager.sourceFiles().lazy.map(\.uri) if !indexFilesWithUpToDateUnit { let index = index.checked(for: .modifiedFiles) filesToIndex = filesToIndex.filter { !index.hasUpToDateUnit(for: $0) } @@ -318,7 +323,7 @@ package final actor SemanticIndexManager { /// /// This tries to produce an up-to-date index for the given files as quickly as possible. To achieve this, it might /// suspend previous target-wide index tasks in favor of index tasks that index a fewer files. - package func waitForUpToDateIndex(for uris: some Collection) async { + package func waitForUpToDateIndex(for uris: some Collection & Sendable) async { logger.info( "Waiting for up-to-date index for \(uris.map { $0.fileURL?.lastPathComponent ?? $0.stringValue }.joined(separator: ", "))" ) @@ -372,7 +377,7 @@ package final actor SemanticIndexManager { /// If `files` contains a header file, this will return a `FileToIndex` that re-indexes a main file which includes the /// header file to update the header file's index. private func filesToIndex( - toCover files: some Collection + toCover files: some Collection & Sendable ) async -> [FileToIndex] { let sourceFiles = Set(await buildSystemManager.sourceFiles().map(\.uri)) let filesToReIndex = await files.asyncCompactMap { (uri) -> FileToIndex? in @@ -564,7 +569,7 @@ package final actor SemanticIndexManager { /// /// The returned task finishes when all files are indexed. private func scheduleIndexing( - of files: some Collection, + of files: some Collection & Sendable, indexFilesWithUpToDateUnit: Bool, priority: TaskPriority? ) async -> Task { diff --git a/Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift b/Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift index 059101038..c46d810fb 100644 --- a/Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift +++ b/Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift @@ -84,7 +84,9 @@ package actor DynamicallyLoadedSourceKitD: SourceKitD { self.api.set_notification_handler(nil) self.api.shutdown() // FIXME: is it safe to dlclose() sourcekitd? If so, do that here. For now, let the handle leak. - dylib.leak() + Task.detached(priority: .background) { [dylib] in + await dylib.leak() + } } /// Adds a new notification handler (referenced weakly). diff --git a/Sources/SourceKitD/dlopen.swift b/Sources/SourceKitD/dlopen.swift index a9b894f9d..fd566a57d 100644 --- a/Sources/SourceKitD/dlopen.swift +++ b/Sources/SourceKitD/dlopen.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SwiftExtensions + #if os(Windows) import CRT import WinSDK @@ -25,37 +27,45 @@ import Android package final class DLHandle: Sendable { #if os(Windows) - typealias Handle = HMODULE + struct Handle: @unchecked Sendable { + let handle: HMODULE + } #else - typealias Handle = UnsafeMutableRawPointer + struct Handle: @unchecked Sendable { + let handle: UnsafeMutableRawPointer + } #endif - var rawValue: Handle? = nil + let rawValue: ThreadSafeBox init(rawValue: Handle) { - self.rawValue = rawValue + self.rawValue = .init(initialValue: rawValue) } deinit { - precondition(rawValue == nil, "DLHandle must be closed or explicitly leaked before destroying") + precondition(rawValue.value == nil, "DLHandle must be closed or explicitly leaked before destroying") } + /// The handle must not be used anymore after calling `close`. package func close() throws { - if let handle = rawValue { - #if os(Windows) - guard FreeLibrary(handle) else { - throw DLError.close("Failed to FreeLibrary: \(GetLastError())") - } - #else - guard dlclose(handle) == 0 else { - throw DLError.close(dlerror() ?? "unknown error") + try rawValue.withLock { rawValue in + if let handle = rawValue { + #if os(Windows) + guard FreeLibrary(handle.handle) else { + throw DLError.close("Failed to FreeLibrary: \(GetLastError())") + } + #else + guard dlclose(handle.handle) == 0 else { + throw DLError.close(dlerror() ?? "unknown error") + } + #endif } - #endif + rawValue = nil } - rawValue = nil } + /// The handle must not be used anymore after calling `leak`. package func leak() { - rawValue = nil + rawValue.value = nil } } @@ -97,16 +107,16 @@ package func dlopen(_ path: String?, mode: DLOpenFlags) throws -> DLHandle { throw DLError.open(dlerror() ?? "unknown error") } #endif - return DLHandle(rawValue: handle) + return DLHandle(rawValue: DLHandle.Handle(handle: handle)) } package func dlsym(_ handle: DLHandle, symbol: String) -> T? { #if os(Windows) - guard let ptr = GetProcAddress(handle.rawValue!, symbol) else { + guard let ptr = GetProcAddress(handle.rawValue.value!.handle, symbol) else { return nil } #else - guard let ptr = dlsym(handle.rawValue!, symbol) else { + guard let ptr = dlsym(handle.rawValue.value!.handle, symbol) else { return nil } #endif diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift index 8647cdd5a..6d4042004 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -import Foundation import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC @@ -21,10 +20,21 @@ import SwiftSyntax import struct TSCBasic.AbsolutePath +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because Pipe is not marked as Sendable on Linux +@preconcurrency import Foundation +#endif + #if os(Windows) import WinSDK #endif +#if !canImport(Darwin) +extension Process: @unchecked Sendable {} +#endif + /// A thin wrapper over a connection to a clangd server providing build setting handling. /// /// In addition, it also intercepts notifications and replies from clangd in order to do things diff --git a/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift b/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift index f0e0ecccf..a6bef76a0 100644 --- a/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift +++ b/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift @@ -34,8 +34,4 @@ extension UncheckedIndex { } } -#if compiler(<5.11) extension UncheckedIndex: MainFilesProvider {} -#else -extension UncheckedIndex: @retroactive MainFilesProvider {} -#endif diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 13025e14d..6b12fa638 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -1662,7 +1662,7 @@ extension SourceKitLSPServer { /// /// - Parameter location: The symbol index location /// - Returns: The LSP location - private func indexToLSPLocation(_ location: SymbolLocation) -> Location? { + private nonisolated func indexToLSPLocation(_ location: SymbolLocation) -> Location? { guard !location.path.isEmpty else { return nil } return Location( uri: location.documentUri, @@ -2084,19 +2084,33 @@ extension SourceKitLSPServer { } } + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPLocation2(_ location: SymbolLocation) -> Location? { + return self.indexToLSPLocation(location) + } + + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPCallHierarchyItem2( + symbol: Symbol, + containerName: String?, + location: Location + ) -> CallHierarchyItem { + return self.indexToLSPCallHierarchyItem(symbol: symbol, containerName: containerName, location: location) + } + let calls = callersToCalls.compactMap { (caller: Symbol, calls: [SymbolOccurrence]) -> CallHierarchyIncomingCall? in // Resolve the caller's definition to find its location let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: caller.usr) let definitionSymbolLocation = definition?.location - let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation) + let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2) - let locations = calls.compactMap { indexToLSPLocation($0.location) }.sorted() + let locations = calls.compactMap { indexToLSPLocation2($0.location) }.sorted() guard !locations.isEmpty else { return nil } return CallHierarchyIncomingCall( - from: indexToLSPCallHierarchyItem( + from: indexToLSPCallHierarchyItem2( symbol: caller, containerName: definition?.containerName, location: definitionLocation ?? locations.first! @@ -2113,23 +2127,38 @@ extension SourceKitLSPServer { else { return [] } + + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPLocation2(_ location: SymbolLocation) -> Location? { + return self.indexToLSPLocation(location) + } + + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPCallHierarchyItem2( + symbol: Symbol, + containerName: String?, + location: Location + ) -> CallHierarchyItem { + return self.indexToLSPCallHierarchyItem(symbol: symbol, containerName: containerName, location: location) + } + let callableUsrs = [data.usr] + index.occurrences(relatedToUSR: data.usr, roles: .accessorOf).map(\.symbol.usr) let callOccurrences = callableUsrs.flatMap { index.occurrences(relatedToUSR: $0, roles: .containedBy) } let calls = callOccurrences.compactMap { occurrence -> CallHierarchyOutgoingCall? in guard occurrence.symbol.kind.isCallable else { return nil } - guard let location = indexToLSPLocation(occurrence.location) else { + guard let location = indexToLSPLocation2(occurrence.location) else { return nil } // Resolve the callee's definition to find its location let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr) let definitionSymbolLocation = definition?.location - let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation) + let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2) return CallHierarchyOutgoingCall( - to: indexToLSPCallHierarchyItem( + to: indexToLSPCallHierarchyItem2( symbol: occurrence.symbol, containerName: definition?.containerName, location: definitionLocation ?? location // Use occurrence location as fallback @@ -2285,19 +2314,34 @@ extension SourceKitLSPServer { return index.occurrences(relatedToUSR: related.symbol.usr, roles: .baseOf) } + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPLocation2(_ location: SymbolLocation) -> Location? { + return self.indexToLSPLocation(location) + } + + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPTypeHierarchyItem2( + symbol: Symbol, + moduleName: String?, + location: Location, + index: CheckedIndex + ) -> TypeHierarchyItem { + return self.indexToLSPTypeHierarchyItem(symbol: symbol, moduleName: moduleName, location: location, index: index) + } + // Convert occurrences to type hierarchy items let occurs = baseOccurs + retroactiveConformanceOccurs let types = occurs.compactMap { occurrence -> TypeHierarchyItem? in - guard let location = indexToLSPLocation(occurrence.location) else { + guard let location = indexToLSPLocation2(occurrence.location) else { return nil } // Resolve the supertype's definition to find its location let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: occurrence.symbol.usr) let definitionSymbolLocation = definition?.location - let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation) + let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2) - return indexToLSPTypeHierarchyItem( + return indexToLSPTypeHierarchyItem2( symbol: occurrence.symbol, moduleName: definitionSymbolLocation?.moduleName, location: definitionLocation ?? location, // Use occurrence location as fallback @@ -2317,6 +2361,21 @@ extension SourceKitLSPServer { // Resolve child types and extensions let occurs = index.occurrences(ofUSR: data.usr, roles: [.baseOf, .extendedBy]) + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPLocation2(_ location: SymbolLocation) -> Location? { + return self.indexToLSPLocation(location) + } + + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func indexToLSPTypeHierarchyItem2( + symbol: Symbol, + moduleName: String?, + location: Location, + index: CheckedIndex + ) -> TypeHierarchyItem { + return self.indexToLSPTypeHierarchyItem(symbol: symbol, moduleName: moduleName, location: location, index: index) + } + // Convert occurrences to type hierarchy items let types = occurs.compactMap { occurrence -> TypeHierarchyItem? in if occurrence.relations.count > 1 { @@ -2325,7 +2384,7 @@ extension SourceKitLSPServer { // to. logger.fault("Expected at most extendedBy or baseOf relation but got \(occurrence.relations.count)") } - guard let related = occurrence.relations.sorted().first, let location = indexToLSPLocation(occurrence.location) + guard let related = occurrence.relations.sorted().first, let location = indexToLSPLocation2(occurrence.location) else { return nil } @@ -2333,9 +2392,9 @@ extension SourceKitLSPServer { // Resolve the subtype's definition to find its location let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: related.symbol.usr) let definitionSymbolLocation = definition.map(\.location) - let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation) + let definitionLocation = definitionSymbolLocation.flatMap(indexToLSPLocation2) - return indexToLSPTypeHierarchyItem( + return indexToLSPTypeHierarchyItem2( symbol: related.symbol, moduleName: definitionSymbolLocation?.moduleName, location: definitionLocation ?? location, // Use occurrence location as fallback diff --git a/Sources/SourceKitLSP/TestDiscovery.swift b/Sources/SourceKitLSP/TestDiscovery.swift index 085123b0a..179cf6ec2 100644 --- a/Sources/SourceKitLSP/TestDiscovery.swift +++ b/Sources/SourceKitLSP/TestDiscovery.swift @@ -187,10 +187,19 @@ extension SourceKitLSPServer { // - All files that have in-memory modifications are syntactically scanned for tests here. let index = workspace.index(checkedFor: .inMemoryModifiedFiles(documentManager)) + // FIXME: (async-workaround) Needed to work around rdar://130112205 + func documentManagerHasInMemoryModifications(_ uri: DocumentURI) -> Bool { + return documentManager.fileHasInMemoryModifications(uri) + } + let filesWithInMemoryState = documentManager.documents.keys.filter { uri in // Use the index to check for in-memory modifications so we can re-use its cache. If no index exits, ask the // document manager directly. - return index?.fileHasInMemoryModifications(uri) ?? documentManager.fileHasInMemoryModifications(uri) + if let index { + return index.fileHasInMemoryModifications(uri) + } else { + return documentManagerHasInMemoryModifications(uri) + } } let testsFromFilesWithInMemoryState = await filesWithInMemoryState.concurrentMap { (uri) -> [AnnotatedTestItem] in diff --git a/Sources/SwiftExtensions/ThreadSafeBox.swift b/Sources/SwiftExtensions/ThreadSafeBox.swift index ac97a801d..bc0ec9be4 100644 --- a/Sources/SwiftExtensions/ThreadSafeBox.swift +++ b/Sources/SwiftExtensions/ThreadSafeBox.swift @@ -43,9 +43,9 @@ package class ThreadSafeBox: @unchecked Sendable { _value = initialValue } - package func withLock(_ body: (inout T) -> Result) -> Result { - return lock.withLock { - return body(&_value) + package func withLock(_ body: (inout T) throws -> Result) rethrows -> Result { + return try lock.withLock { + return try body(&_value) } } diff --git a/Sources/sourcekit-lsp/SourceKitLSP.swift b/Sources/sourcekit-lsp/SourceKitLSP.swift index 2302f955f..a499e0abb 100644 --- a/Sources/sourcekit-lsp/SourceKitLSP.swift +++ b/Sources/sourcekit-lsp/SourceKitLSP.swift @@ -14,7 +14,6 @@ import ArgumentParser import Csourcekitd // Not needed here, but fixes debugging... import Diagnose import Dispatch -import Foundation import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC @@ -26,6 +25,13 @@ import struct TSCBasic.AbsolutePath import struct TSCBasic.RelativePath import var TSCBasic.localFileSystem +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because FileHandle is not marked as Sendable on Linux rdar://132378985 +@preconcurrency import Foundation +#endif + extension AbsolutePath { public init?(argument: String) { let path: AbsolutePath? @@ -81,23 +87,11 @@ extension PathPrefixMapping { ) } } -#if compiler(<5.11) extension PathPrefixMapping: ExpressibleByArgument {} -#else -extension PathPrefixMapping: @retroactive ExpressibleByArgument {} -#endif -#if compiler(<5.11) extension SKCore.BuildConfiguration: ExpressibleByArgument {} -#else -extension SKCore.BuildConfiguration: @retroactive ExpressibleByArgument {} -#endif -#if compiler(<5.11) extension SKCore.WorkspaceType: ExpressibleByArgument {} -#else -extension SKCore.WorkspaceType: @retroactive ExpressibleByArgument {} -#endif @main struct SourceKitLSP: AsyncParsableCommand { diff --git a/Tests/LSPLoggingTests/LoggingTests.swift b/Tests/LSPLoggingTests/LoggingTests.swift index 5ce3cc663..3e4aa16aa 100644 --- a/Tests/LSPLoggingTests/LoggingTests.swift +++ b/Tests/LSPLoggingTests/LoggingTests.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// @_spi(Testing) import LSPLogging +import LSPTestSupport import SKTestSupport import XCTest diff --git a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift index daceb8b9f..6d5ae6f71 100644 --- a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift +++ b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift @@ -15,6 +15,13 @@ import LanguageServerProtocol @_spi(Testing) import LanguageServerProtocolJSONRPC import XCTest +#if canImport(Darwin) +import class Foundation.Pipe +#else +// FIMXE: (async-workaround) @preconcurrency needed because Pipe is not marked as Sendable on Linux rdar://132378792 +@preconcurrency import class Foundation.Pipe +#endif + #if os(Windows) import WinSDK #endif @@ -297,7 +304,7 @@ class ConnectionTests: XCTestCase { """ connection.clientToServerConnection.send(message: messageContents) - try await self.fulfillmentOfOrThrow([expectation]) + try await fulfillmentOfOrThrow([expectation]) } } diff --git a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift index 681186912..b67d5c773 100644 --- a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift +++ b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// import BuildServerProtocol -import Foundation import ISDBTestSupport import LSPTestSupport import LanguageServerProtocol @@ -20,6 +19,13 @@ import SKTestSupport import TSCBasic import XCTest +#if canImport(Darwin) +import Foundation +#else +// FIMXE: (async-workaround) @preconcurrency needed because Pipe is not marked as Sendable on Linux rdar://132378792 +@preconcurrency import Foundation +#endif + /// The path to the INPUTS directory of shared test projects. private let skTestSupportInputsDirectory: URL = { #if os(macOS) diff --git a/Tests/SKSupportTests/DebouncerTests.swift b/Tests/SKSupportTests/DebouncerTests.swift index cb8c9f841..1d33b8ba1 100644 --- a/Tests/SKSupportTests/DebouncerTests.swift +++ b/Tests/SKSupportTests/DebouncerTests.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import LSPTestSupport import SKSupport import XCTest @@ -22,7 +23,7 @@ final class DebouncerTests: XCTestCase { } await debouncer.scheduleCall() await debouncer.scheduleCall() - try await self.fulfillmentOfOrThrow([expectation]) + try await fulfillmentOfOrThrow([expectation]) // Sleep for 0.2s to make sure the debouncer actually debounces and doesn't fulfill the expectation twice. try await Task.sleep(for: .seconds(0.2)) } @@ -36,6 +37,6 @@ final class DebouncerTests: XCTestCase { } await debouncer.scheduleCall(1) await debouncer.scheduleCall(2) - try await self.fulfillmentOfOrThrow([expectation]) + try await fulfillmentOfOrThrow([expectation]) } } diff --git a/Tests/SourceKitLSPTests/DefinitionTests.swift b/Tests/SourceKitLSPTests/DefinitionTests.swift index 265e5f04a..4b5dc75d0 100644 --- a/Tests/SourceKitLSPTests/DefinitionTests.swift +++ b/Tests/SourceKitLSPTests/DefinitionTests.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// @_spi(Testing) import LSPLogging +import LSPTestSupport import LanguageServerProtocol import SKTestSupport import XCTest diff --git a/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift b/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift index 8a8d549e4..f0f2ae128 100644 --- a/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift +++ b/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift @@ -22,17 +22,17 @@ struct ExpectedPreparation { /// A closure that will be executed when a preparation task starts. /// This allows the artificial delay of a preparation task to force two preparation task to race. - let didStart: (() -> Void)? + let didStart: (@Sendable () -> Void)? /// A closure that will be executed when a preparation task finishes. /// This allows the artificial delay of a preparation task to force two preparation task to race. - let didFinish: (() -> Void)? + let didFinish: (@Sendable () -> Void)? internal init( targetID: String, runDestinationID: String, - didStart: (() -> Void)? = nil, - didFinish: (() -> Void)? = nil + didStart: (@Sendable () -> Void)? = nil, + didFinish: (@Sendable () -> Void)? = nil ) { self.targetID = targetID self.runDestinationID = runDestinationID diff --git a/Tests/SourceKitLSPTests/LifecycleTests.swift b/Tests/SourceKitLSPTests/LifecycleTests.swift index 0ca5b4524..2a87c8f19 100644 --- a/Tests/SourceKitLSPTests/LifecycleTests.swift +++ b/Tests/SourceKitLSPTests/LifecycleTests.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import LSPTestSupport import LanguageServerProtocol import SKTestSupport import XCTest diff --git a/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift b/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift index 87ec20234..8c01600ac 100644 --- a/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift +++ b/Tests/SourceKitLSPTests/PullDiagnosticsTests.swift @@ -181,7 +181,7 @@ final class PullDiagnosticsTests: XCTestCase { DidChangeWatchedFilesNotification(changes: [FileEvent(uri: aUri, type: .changed)]) ) - try await self.fulfillmentOfOrThrow([diagnosticsRefreshRequestReceived]) + try await fulfillmentOfOrThrow([diagnosticsRefreshRequestReceived]) let afterChangingFileA = try await project.testClient.send( DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(bUri)) @@ -250,7 +250,7 @@ final class PullDiagnosticsTests: XCTestCase { ) ) - try await self.fulfillmentOfOrThrow([diagnosticsRefreshRequestReceived]) + try await fulfillmentOfOrThrow([diagnosticsRefreshRequestReceived]) let afterChangingFileA = try await project.testClient.send( DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(bUri)) @@ -319,7 +319,7 @@ final class PullDiagnosticsTests: XCTestCase { let diagnosticRequestCancelled = self.expectation(description: "diagnostic request cancelled") var testHooks = TestHooks() testHooks.indexTestHooks.preparationTaskDidStart = { _ in - await self.fulfillment(of: [diagnosticRequestCancelled], timeout: defaultTimeout) + _ = await XCTWaiter.fulfillment(of: [diagnosticRequestCancelled], timeout: defaultTimeout) // Poll until the `CancelRequestNotification` has been propagated to the request handling. for _ in 0..