Files
sourcekit-lsp/Sources/SwiftLanguageService/TestDiscovery.swift
Alex Hoppen 425e1322a1 Explicitly close the index when shutting down SourceKit-LSP
`IndexStoreDB` moves its index to the `saved` directory when it is deallocated. Because `IndexStoreDB` is primarily owned by `UncheckedIndex`, we rely on deallocating this object to save the index store. This is fairly brittle because various parts of the codebase may hold transient references to that object as reported in https://github.com/swiftlang/sourcekit-lsp/issues/2455#issuecomment-3873561003.

Explicitly remove the reference from `UncheckedIndex` to `IndexStoreDB`. While this still isn’t perfect because other parts of the code base may hold references to `IndexStoreDB` but those should be a lot rarer, resulting in a more consistent closing of the index.
2026-02-15 18:02:53 +01:00

72 lines
2.9 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 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 BuildServerIntegration
@_spi(SourceKitLSP) import BuildServerProtocol
import Foundation
@_spi(SourceKitLSP) package import LanguageServerProtocol
@_spi(SourceKitLSP) import SKLogging
import SemanticIndex
package import SourceKitLSP
import SwiftExtensions
extension SwiftLanguageService {
package func syntacticDocumentTests(
for uri: DocumentURI,
in workspace: Workspace
) async throws -> [AnnotatedTestItem]? {
let targetIdentifiers = await workspace.buildServerManager.targets(for: uri)
let isInTestTarget = await targetIdentifiers.asyncContains(where: {
await workspace.buildServerManager.buildTarget(named: $0)?.tags.contains(.test) ?? true
})
if !targetIdentifiers.isEmpty && !isInTestTarget {
// If we know the targets for the file and the file is not part of any test target, don't scan it for tests.
return nil
}
let snapshot = try documentManager.latestSnapshot(uri)
let semanticSymbols = try await workspace.index(checkedFor: .deletedFiles)?.symbols(
inFilePath: snapshot.uri.pseudoPath
)
let xctestSymbols = await SyntacticSwiftXCTestScanner.findTestSymbols(
in: snapshot,
syntaxTreeManager: syntaxTreeManager
)
.compactMap { $0.filterUsing(semanticSymbols: semanticSymbols) }
let swiftTestingSymbols = await SyntacticSwiftTestingTestScanner.findTestSymbols(
in: snapshot,
syntaxTreeManager: syntaxTreeManager
)
return (xctestSymbols + swiftTestingSymbols).sorted { $0.testItem.location < $1.testItem.location }
}
/// Syntactically scans the snapshot for tests declared within it.
///
/// Does not write the results to the index.
///
/// The order of the returned tests is not defined. The results should be sorted before being returned to the editor.
package func syntacticTestItems(
for snapshot: DocumentSnapshot,
) async -> [AnnotatedTestItem] {
// Don't use the `syntaxTreeManager` instance variable in `SwiftLanguageService` in `DocumentSnapshot`
// loaded from the disk will always have version number 0
let syntaxTreeManager = SyntaxTreeManager()
async let swiftTestingTests = SyntacticSwiftTestingTestScanner.findTestSymbols(
in: snapshot,
syntaxTreeManager: syntaxTreeManager
)
async let xcTests = SyntacticSwiftXCTestScanner.findTestSymbols(in: snapshot, syntaxTreeManager: syntaxTreeManager)
return await swiftTestingTests + xcTests
}
}