From 32af558a6e5d93115d9a9e60decb5d5e689ca80f Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 22 Sep 2023 10:32:44 -0700 Subject: [PATCH] Migrate `BuildServerBuildSystem` to an actor and make methods in `BuildSystemDelegate` async This concludes the migration of the build systems to async. --- Sources/SKCore/BuildServerBuildSystem.swift | 56 +++++++++---------- Sources/SKCore/BuildSystemDelegate.swift | 8 +-- Sources/SKCore/BuildSystemManager.swift | 32 +++++------ .../CompilationDatabaseBuildSystem.swift | 12 ++-- Sources/SKCore/FallbackBuildSystem.swift | 4 +- .../SKSwiftPMWorkspace/SwiftPMWorkspace.swift | 20 +++---- Sources/SourceKitLSP/Workspace.swift | 2 +- .../BuildServerBuildSystemTests.swift | 34 +++++------ .../SKCoreTests/BuildSystemManagerTests.swift | 10 ++-- .../SourceKitLSPTests/BuildSystemTests.swift | 14 ++--- 10 files changed, 91 insertions(+), 101 deletions(-) diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index d7f3c6e3..4f7e3a66 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -43,10 +43,7 @@ func executable(_ name: String) -> String { /// /// Provides build settings from a build server launched based on a /// `buildServer.json` configuration file provided in the repo root. -public final class BuildServerBuildSystem: MessageHandler { - /// The handler's request queue. `delegate` will always be called on this queue. - public let queue: DispatchQueue = DispatchQueue(label: "language-server-queue", qos: .userInitiated) - +public actor BuildServerBuildSystem: MessageHandler { let projectRoot: AbsolutePath let buildFolder: AbsolutePath? let serverConfig: BuildServerConfig @@ -73,7 +70,7 @@ public final class BuildServerBuildSystem: MessageHandler { /// The build settings that have been received from the build server. private var buildSettings: [DocumentURI: FileBuildSettings] = [:] - public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) throws { + public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) async throws { let configPath = projectRoot.appending(component: "buildServer.json") let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem) #if os(Windows) @@ -95,12 +92,11 @@ public final class BuildServerBuildSystem: MessageHandler { /// Creates a build system using the Build Server Protocol config. /// /// - Returns: nil if `projectRoot` has no config or there is an error parsing it. - public convenience init?(projectRoot: AbsolutePath?, buildSetup: BuildSetup) - { + public init?(projectRoot: AbsolutePath?, buildSetup: BuildSetup) async { if projectRoot == nil { return nil } do { - try self.init(projectRoot: projectRoot!, buildFolder: buildSetup.path) + try await self.init(projectRoot: projectRoot!, buildFolder: buildSetup.path) } catch _ as FileSystemError { // config file was missing, no build server for this workspace return nil @@ -171,13 +167,11 @@ public final class BuildServerBuildSystem: MessageHandler { /// the build server has sent us a notification. /// /// We need to notify the delegate about any updated build settings. - public func handle(_ params: some NotificationType, from clientID: ObjectIdentifier) { - queue.async { - if let params = params as? BuildTargetsChangedNotification { - self.handleBuildTargetsChanged(Notification(params, clientID: clientID)) - } else if let params = params as? FileOptionsChangedNotification { - self.handleFileOptionsChanged(Notification(params, clientID: clientID)) - } + public func handle(_ params: some NotificationType, from clientID: ObjectIdentifier) async { + if let params = params as? BuildTargetsChangedNotification { + await self.handleBuildTargetsChanged(Notification(params, clientID: clientID)) + } else if let params = params as? FileOptionsChangedNotification { + await self.handleFileOptionsChanged(Notification(params, clientID: clientID)) } } @@ -190,30 +184,28 @@ public final class BuildServerBuildSystem: MessageHandler { from clientID: ObjectIdentifier, reply: @escaping (LSPResult) -> Void ) { - queue.async { - reply(.failure(ResponseError.methodNotFound(R.method))) - } + reply(.failure(ResponseError.methodNotFound(R.method))) } - func handleBuildTargetsChanged(_ notification: LanguageServerProtocol.Notification) { - self.delegate?.buildTargetsChanged(notification.params.changes) + func handleBuildTargetsChanged(_ notification: LanguageServerProtocol.Notification) async { + await self.delegate?.buildTargetsChanged(notification.params.changes) } - func handleFileOptionsChanged(_ notification: LanguageServerProtocol.Notification) { + func handleFileOptionsChanged(_ notification: LanguageServerProtocol.Notification) async { let result = notification.params.updatedOptions let settings = FileBuildSettings( compilerArguments: result.options, workingDirectory: result.workingDirectory) - self.buildSettingsChanged(for: notification.params.uri, settings: settings) + await self.buildSettingsChanged(for: notification.params.uri, settings: settings) } /// Record the new build settings for the given document and inform the delegate /// about the changed build settings. - private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) { + private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) async { buildSettings[document] = settings if let settings { - self.delegate?.fileBuildSettingsChanged([document: .modified(settings)]) + await self.delegate?.fileBuildSettingsChanged([document: .modified(settings)]) } else { - self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable]) + await self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable]) } } } @@ -239,12 +231,14 @@ extension BuildServerBuildSystem: BuildSystem { public func registerForChangeNotifications(for uri: DocumentURI, language: Language) { let request = RegisterForChanges(uri: uri, action: .register) _ = self.buildServer?.send(request, queue: requestQueue, reply: { result in - if let error = result.failure { - log("error registering \(uri): \(error)", level: .error) - - // BuildServer registration failed, so tell our delegate that no build - // settings are available. - self.buildSettingsChanged(for: uri, settings: nil) + Task { + if let error = result.failure { + log("error registering \(uri): \(error)", level: .error) + + // BuildServer registration failed, so tell our delegate that no build + // settings are available. + await self.buildSettingsChanged(for: uri, settings: nil) + } } }) } diff --git a/Sources/SKCore/BuildSystemDelegate.swift b/Sources/SKCore/BuildSystemDelegate.swift index 670dabf1..948a861a 100644 --- a/Sources/SKCore/BuildSystemDelegate.swift +++ b/Sources/SKCore/BuildSystemDelegate.swift @@ -18,14 +18,14 @@ public protocol BuildSystemDelegate: AnyObject { /// /// The callee should request new sources and outputs for the build targets of /// interest. - func buildTargetsChanged(_ changes: [BuildTargetEvent]) + func buildTargetsChanged(_ changes: [BuildTargetEvent]) async /// Notify the delegate that the given files' build settings have changed. /// /// The delegate should cache the new build settings for any of the given /// files that they are interested in. func fileBuildSettingsChanged( - _ changedFiles: [DocumentURI: FileBuildSettingsChange]) + _ changedFiles: [DocumentURI: FileBuildSettingsChange]) async /// Notify the delegate that the dependencies of the given files have changed /// and that ASTs may need to be refreshed. If the given set is empty, assume @@ -33,10 +33,10 @@ public protocol BuildSystemDelegate: AnyObject { /// /// The callee should refresh ASTs unless it is able to determine that a /// refresh is not necessary. - func filesDependenciesUpdated(_ changedFiles: Set) + func filesDependenciesUpdated(_ changedFiles: Set) async /// Notify the delegate that the file handling capability of this build system /// for some file has changed. The delegate should discard any cached file /// handling capability. - func fileHandlingCapabilityChanged() + func fileHandlingCapabilityChanged() async } diff --git a/Sources/SKCore/BuildSystemManager.swift b/Sources/SKCore/BuildSystemManager.swift index af668613..953f6247 100644 --- a/Sources/SKCore/BuildSystemManager.swift +++ b/Sources/SKCore/BuildSystemManager.swift @@ -200,7 +200,7 @@ extension BuildSystemManager { if let mainChange = newStatus.buildSettingsChange, let delegate = self._delegate { let change = self.convert(change: mainChange, ofMainFile: mainFile, to: uri) - delegate.fileBuildSettingsChanged([uri: change]) + await delegate.fileBuildSettingsChanged([uri: change]) } } @@ -242,7 +242,7 @@ extension BuildSystemManager { if let fallback = self.fallbackBuildSystem { Task { try await Task.sleep(nanoseconds: UInt64(self.fallbackSettingsTimeout.nanoseconds()!)) - self.handleFallbackTimer(for: mainFile, language: language, fallback) + await self.handleFallbackTimer(for: mainFile, language: language, fallback) } } @@ -280,7 +280,7 @@ extension BuildSystemManager { /// Update and notify our delegate for the given main file changes if they are /// convertible into `FileBuildSettingsChange`. - func updateAndNotifyStatuses(changes: [DocumentURI: MainFileStatus]) { + func updateAndNotifyStatuses(changes: [DocumentURI: MainFileStatus]) async { var changedWatchedFiles = [DocumentURI: FileBuildSettingsChange]() for (mainFile, status) in changes { let watches = self.watchedFiles.filter { $1.mainFile == mainFile } @@ -307,7 +307,7 @@ extension BuildSystemManager { } if !changedWatchedFiles.isEmpty, let delegate = self._delegate { - delegate.fileBuildSettingsChanged(changedWatchedFiles) + await delegate.fileBuildSettingsChanged(changedWatchedFiles) } } @@ -319,14 +319,14 @@ extension BuildSystemManager { for mainFile: DocumentURI, language: Language, _ fallback: FallbackBuildSystem - ) { + ) async { // There won't be a current status if it's unreferenced by any watched file. // Similarly, if the status isn't `waiting` then there's nothing to do. guard let status = self.mainFileStatuses[mainFile], status == .waiting else { return } if let settings = fallback.buildSettings(for: mainFile, language: language) { - self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)]) + await self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)]) } else { // Keep the status as waiting. } @@ -367,7 +367,7 @@ extension BuildSystemManager: BuildSystemDelegate { } } - public func fileBuildSettingsChangedImpl(_ changes: [DocumentURI: FileBuildSettingsChange]) { + public func fileBuildSettingsChangedImpl(_ changes: [DocumentURI: FileBuildSettingsChange]) async { let statusChanges: [DocumentURI: MainFileStatus] = changes.reduce(into: [:]) { (result, entry) in let mainFile = entry.key @@ -395,7 +395,7 @@ extension BuildSystemManager: BuildSystemDelegate { } result[mainFile] = newStatus } - self.updateAndNotifyStatuses(changes: statusChanges) + await self.updateAndNotifyStatuses(changes: statusChanges) } // FIXME: (async) Make this method isolated once `BuildSystemDelegate` has ben asyncified @@ -405,11 +405,11 @@ extension BuildSystemManager: BuildSystemDelegate { } } - public func filesDependenciesUpdatedImpl(_ changedFiles: Set) { + public func filesDependenciesUpdatedImpl(_ changedFiles: Set) async { // Empty changes --> assume everything has changed. guard !changedFiles.isEmpty else { if let delegate = self._delegate { - delegate.filesDependenciesUpdated(changedFiles) + await delegate.filesDependenciesUpdated(changedFiles) } return } @@ -418,7 +418,7 @@ extension BuildSystemManager: BuildSystemDelegate { let changedWatchedFiles = self.watchedFiles.filter { changedFiles.contains($1.mainFile) } let newChangedFiles = Set(changedWatchedFiles.map { $0.key }) if let delegate = self._delegate, !newChangedFiles.isEmpty { - delegate.filesDependenciesUpdated(newChangedFiles) + await delegate.filesDependenciesUpdated(newChangedFiles) } } @@ -429,9 +429,9 @@ extension BuildSystemManager: BuildSystemDelegate { } } - public func buildTargetsChangedImpl(_ changes: [BuildTargetEvent]) { + public func buildTargetsChangedImpl(_ changes: [BuildTargetEvent]) async { if let delegate = self._delegate { - delegate.buildTargetsChanged(changes) + await delegate.buildTargetsChanged(changes) } } @@ -442,9 +442,9 @@ extension BuildSystemManager: BuildSystemDelegate { } } - public func fileHandlingCapabilityChangedImpl() { + public func fileHandlingCapabilityChangedImpl() async { if let delegate = self._delegate { - delegate.fileHandlingCapabilityChanged() + await delegate.fileHandlingCapabilityChanged() } } } @@ -484,7 +484,7 @@ extension BuildSystemManager: MainFilesDelegate { } if let delegate = self._delegate, !buildSettingsChanges.isEmpty { - delegate.fileBuildSettingsChanged(buildSettingsChanges) + await delegate.fileBuildSettingsChanged(buildSettingsChanges) } } } diff --git a/Sources/SKCore/CompilationDatabaseBuildSystem.swift b/Sources/SKCore/CompilationDatabaseBuildSystem.swift index e1edb134..3425d474 100644 --- a/Sources/SKCore/CompilationDatabaseBuildSystem.swift +++ b/Sources/SKCore/CompilationDatabaseBuildSystem.swift @@ -91,13 +91,13 @@ extension CompilationDatabaseBuildSystem: BuildSystem { return self.settings(for: document) } - public func registerForChangeNotifications(for uri: DocumentURI, language: Language) { + public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async { self.watchedFiles[uri] = language guard let delegate = self.delegate else { return } let settings = self.settings(for: uri) - delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) + await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) } /// We don't support change watching. @@ -142,7 +142,7 @@ extension CompilationDatabaseBuildSystem: BuildSystem { /// The compilation database has been changed on disk. /// Reload it and notify the delegate about build setting changes. - private func reloadCompilationDatabase() { + private func reloadCompilationDatabase() async { guard let projectRoot = self.projectRoot else { return } self.compdb = tryLoadCompilationDatabase(directory: projectRoot, self.fileSystem) @@ -156,13 +156,13 @@ extension CompilationDatabaseBuildSystem: BuildSystem { changedFiles[uri] = .removedOrUnavailable } } - delegate.fileBuildSettingsChanged(changedFiles) + await delegate.fileBuildSettingsChanged(changedFiles) } } - public func filesDidChange(_ events: [FileEvent]) { + public func filesDidChange(_ events: [FileEvent]) async { if events.contains(where: { self.fileEventShouldTriggerCompilationDatabaseReload(event: $0) }) { - self.reloadCompilationDatabase() + await self.reloadCompilationDatabase() } } diff --git a/Sources/SKCore/FallbackBuildSystem.swift b/Sources/SKCore/FallbackBuildSystem.swift index 10aff43d..c03bfb44 100644 --- a/Sources/SKCore/FallbackBuildSystem.swift +++ b/Sources/SKCore/FallbackBuildSystem.swift @@ -63,8 +63,8 @@ public final class FallbackBuildSystem: BuildSystem { guard let delegate = self.delegate else { return } let settings = self.buildSettings(for: uri, language: language) - DispatchQueue.global().async { - delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) + Task { + await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) } } diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index a5ac3947..df39d785 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -152,7 +152,7 @@ public actor SwiftPMWorkspace { self.packageGraph = try PackageGraph(rootPackages: [], dependencies: [], binaryArtifacts: [:]) self.reloadPackageStatusCallback = reloadPackageStatusCallback - try reloadPackage() + try await reloadPackage() } /// Creates a build system using the Swift Package Manager, if this workspace is a package. @@ -188,7 +188,7 @@ extension SwiftPMWorkspace { /// (Re-)load the package settings by parsing the manifest and resolving all the targets and /// dependencies. - func reloadPackage() throws { + func reloadPackage() async throws { reloadPackageStatusCallback(.start) defer { reloadPackageStatusCallback(.end) @@ -252,8 +252,8 @@ extension SwiftPMWorkspace { } } } - delegate.fileBuildSettingsChanged(changedFiles) - delegate.fileHandlingCapabilityChanged() + await delegate.fileBuildSettingsChanged(changedFiles) + await delegate.fileHandlingCapabilityChanged() } } @@ -305,7 +305,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { return nil } - public func registerForChangeNotifications(for uri: DocumentURI, language: Language) { + public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async { assert(self.watchedFiles[uri] == nil, "Registered twice for change notifications of the same URI") guard let delegate = self.delegate else { return } self.watchedFiles[uri] = language @@ -317,9 +317,9 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { log("error computing settings: \(error)") } if let settings = settings { - delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) + await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) } else { - delegate.fileBuildSettingsChanged([uri: .removedOrUnavailable]) + await delegate.fileBuildSettingsChanged([uri: .removedOrUnavailable]) } } @@ -366,11 +366,11 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { } } - public func filesDidChange(_ events: [FileEvent]) { + public func filesDidChange(_ events: [FileEvent]) async { if events.contains(where: { self.fileEventShouldTriggerPackageReload(event: $0) }) { - orLog { + await orLog { // TODO: It should not be necessary to reload the entire package just to get build settings for one file. - try self.reloadPackage() + try await self.reloadPackage() } } } diff --git a/Sources/SourceKitLSP/Workspace.swift b/Sources/SourceKitLSP/Workspace.swift index e89d575a..646a1587 100644 --- a/Sources/SourceKitLSP/Workspace.swift +++ b/Sources/SourceKitLSP/Workspace.swift @@ -97,7 +97,7 @@ public final class Workspace { ) async throws { var buildSystem: BuildSystem? = nil if let rootUrl = rootUri.fileURL, let rootPath = try? AbsolutePath(validating: rootUrl.path) { - if let buildServer = BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) { + if let buildServer = await BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) { buildSystem = buildServer } else if let swiftpm = await SwiftPMWorkspace( url: rootUrl, diff --git a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift index a4324425..7e2bcacd 100644 --- a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift +++ b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift @@ -27,21 +27,21 @@ final class BuildServerBuildSystemTests: XCTestCase { } let buildFolder = try! AbsolutePath(validating: NSTemporaryDirectory()) - func testServerInitialize() throws { - let buildSystem = try BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) + func testServerInitialize() async throws { + let buildSystem = try await BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) - XCTAssertEqual( - buildSystem.indexDatabasePath, + assertEqual( + await buildSystem.indexDatabasePath, try AbsolutePath(validating: "some/index/db/path", relativeTo: root) ) - XCTAssertEqual( - buildSystem.indexStorePath, + assertEqual( + await buildSystem.indexStorePath, try AbsolutePath(validating: "some/index/store/path", relativeTo: root) ) } - func testFileRegistration() throws { - let buildSystem = try BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) + func testFileRegistration() async throws { + let buildSystem = try await BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) let fileUrl = URL(fileURLWithPath: "/some/file/path") let expectation = XCTestExpectation(description: "\(fileUrl) settings updated") @@ -50,14 +50,14 @@ final class BuildServerBuildSystemTests: XCTestCase { // BuildSystemManager has a weak reference to delegate. Keep it alive. _fixLifetime(buildSystemDelegate) } - buildSystem.delegate = buildSystemDelegate - buildSystem.registerForChangeNotifications(for: DocumentURI(fileUrl), language: .swift) + await buildSystem.setDelegate(buildSystemDelegate) + await buildSystem.registerForChangeNotifications(for: DocumentURI(fileUrl), language: .swift) XCTAssertEqual(XCTWaiter.wait(for: [expectation], timeout: defaultTimeout), .completed) } - func testBuildTargetsChanged() throws { - let buildSystem = try BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) + func testBuildTargetsChanged() async throws { + let buildSystem = try await BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) let fileUrl = URL(fileURLWithPath: "/some/file/path") let expectation = XCTestExpectation(description: "target changed") @@ -71,14 +71,10 @@ final class BuildServerBuildSystemTests: XCTestCase { // BuildSystemManager has a weak reference to delegate. Keep it alive. _fixLifetime(buildSystemDelegate) } - buildSystem.delegate = buildSystemDelegate - buildSystem.registerForChangeNotifications(for: DocumentURI(fileUrl), language: .swift) + await buildSystem.setDelegate(buildSystemDelegate) + await buildSystem.registerForChangeNotifications(for: DocumentURI(fileUrl), language: .swift) - let result = XCTWaiter.wait(for: [expectation], timeout: defaultTimeout) - guard result == .completed else { - XCTFail("error \(result) waiting for targets changed notification") - return - } + try await fulfillmentOfOrThrow([expectation]) } } diff --git a/Tests/SKCoreTests/BuildSystemManagerTests.swift b/Tests/SKCoreTests/BuildSystemManagerTests.swift index bff6306b..e94504e1 100644 --- a/Tests/SKCoreTests/BuildSystemManagerTests.swift +++ b/Tests/SKCoreTests/BuildSystemManagerTests.swift @@ -418,9 +418,9 @@ final class BuildSystemManagerTests: XCTestCase { mainFiles.mainFiles = [a: Set([a])] class DepUpdateDuringRegistrationBS: ManualBuildSystem { - override func registerForChangeNotifications(for uri: DocumentURI, language: Language) { - delegate?.filesDependenciesUpdated([uri]) - super.registerForChangeNotifications(for: uri, language: language) + override func registerForChangeNotifications(for uri: DocumentURI, language: Language) async { + await delegate?.filesDependenciesUpdated([uri]) + await super.registerForChangeNotifications(for: uri, language: language) } } @@ -483,9 +483,9 @@ class ManualBuildSystem: BuildSystem { return map[uri] } - func registerForChangeNotifications(for uri: DocumentURI, language: Language) { + func registerForChangeNotifications(for uri: DocumentURI, language: Language) async { let settings = self.buildSettings(for: uri, language: language) - self.delegate?.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) + await self.delegate?.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)]) } func unregisterForChangeNotifications(for: DocumentURI) { diff --git a/Tests/SourceKitLSPTests/BuildSystemTests.swift b/Tests/SourceKitLSPTests/BuildSystemTests.swift index b8b5a625..3d77efa6 100644 --- a/Tests/SourceKitLSPTests/BuildSystemTests.swift +++ b/Tests/SourceKitLSPTests/BuildSystemTests.swift @@ -60,8 +60,8 @@ final class TestBuildSystem: BuildSystem { return } - DispatchQueue.global().async { - delegate.fileBuildSettingsChanged([uri: .modified(settings)]) + Task { + await delegate.fileBuildSettingsChanged([uri: .modified(settings)]) } } @@ -196,7 +196,7 @@ final class BuildSystemTests: XCTestCase { expectation.fulfill() } - buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) + await buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) try await fulfillmentOfOrThrow([expectation]) } @@ -251,7 +251,7 @@ final class BuildSystemTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 0) expectation.fulfill() } - buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) + await buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) try await fulfillmentOfOrThrow([expectation]) } @@ -304,7 +304,7 @@ final class BuildSystemTests: XCTestCase { expectation.fulfill() } - buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) + await buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(newSettings)]) try await fulfillmentOfOrThrow([expectation]) } @@ -358,7 +358,7 @@ final class BuildSystemTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 2) expectation.fulfill() } - buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(primarySettings)]) + await buildSystem.delegate?.fileBuildSettingsChanged([doc: .modified(primarySettings)]) try await fulfillmentOfOrThrow([expectation]) } @@ -388,7 +388,7 @@ final class BuildSystemTests: XCTestCase { // Modify the build settings and inform the SourceKitServer. // This shouldn't trigger new diagnostics since nothing actually changed (false alarm). - buildSystem.delegate?.fileBuildSettingsChanged([doc: .removedOrUnavailable]) + await buildSystem.delegate?.fileBuildSettingsChanged([doc: .removedOrUnavailable]) let expectation = XCTestExpectation(description: "refresh doesn't occur") expectation.isInverted = true