Skip to content

Make the SourceKitLSP module build in Swift 6 mode #1292

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

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Sources/LSPLogging/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ public typealias LogLevel = os.OSLogType
public typealias Logger = os.Logger
public typealias Signposter = OSSignposter

#if compiler(<5.11)
extension OSSignposter: @unchecked Sendable {}
extension OSSignpostID: @unchecked Sendable {}
extension OSSignpostIntervalState: @unchecked Sendable {}
#else
extension OSSignposter: @retroactive @unchecked Sendable {}
extension OSSignpostID: @retroactive @unchecked Sendable {}
extension OSSignpostIntervalState: @retroactive @unchecked Sendable {}
#endif

extension os.Logger {
public func makeSignposter() -> Signposter {
return OSSignposter(logger: self)
Expand Down
2 changes: 1 addition & 1 deletion Sources/LSPLogging/LoggingScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public func withLoggingScope<Result>(
/// - SeeAlso: ``withLoggingScope(_:_:)-6qtga``
public func withLoggingScope<Result>(
_ scope: String,
_ operation: () async throws -> Result
@_inheritActorContext _ operation: @Sendable () async throws -> Result
) async rethrows -> Result {
return try await LoggingScope.$_scope.withValue(
scope,
Expand Down
6 changes: 3 additions & 3 deletions Sources/LSPLogging/NonDarwinLogging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,14 @@ public struct NonDarwinLogger: Sendable {

// MARK: - Signposter

public struct NonDarwinSignpostID {}
public struct NonDarwinSignpostID: Sendable {}

public struct NonDarwinSignpostIntervalState {}
public struct NonDarwinSignpostIntervalState: Sendable {}

/// A type that is API-compatible to `OSLogMessage` for all uses within sourcekit-lsp.
///
/// Since non-Darwin platforms don't have signposts, the type just has no-op operations.
public struct NonDarwinSignposter {
public struct NonDarwinSignposter: Sendable {
public func makeSignpostID() -> NonDarwinSignpostID {
return NonDarwinSignpostID()
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/LanguageServerProtocol/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public protocol MessageHandler: AnyObject, Sendable {
func handle<Request: RequestType>(
_ request: Request,
id: RequestID,
reply: @escaping (LSPResult<Request.Response>) -> Void
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
)
}

Expand Down Expand Up @@ -110,7 +110,7 @@ public final class LocalConnection: Connection, @unchecked Sendable {

public func send<Request: RequestType>(
_ request: Request,
reply: @escaping (LSPResult<Request.Response>) -> Void
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
) -> RequestID {
let id = nextRequestID()

Expand Down
4 changes: 2 additions & 2 deletions Sources/LanguageServerProtocol/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public protocol _RequestType: MessageType {
func _handle(
_ handler: MessageHandler,
id: RequestID,
reply: @escaping (LSPResult<ResponseType>, RequestID) -> Void
reply: @Sendable @escaping (LSPResult<ResponseType>, RequestID) -> Void
)
}

Expand All @@ -49,7 +49,7 @@ extension RequestType {
public func _handle(
_ handler: MessageHandler,
id: RequestID,
reply: @escaping (LSPResult<ResponseType>, RequestID) -> Void
reply: @Sendable @escaping (LSPResult<ResponseType>, RequestID) -> Void
) {
handler.handle(self, id: id) { response in
reply(response.map({ $0 as ResponseType }), id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//

public typealias CodeActionProvider = (CodeActionRequest) async throws -> [CodeAction]
public typealias CodeActionProvider = @Sendable (CodeActionRequest) async throws -> [CodeAction]

/// Request for returning all possible code actions for a given text document and range.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ public struct DocumentRangeFormattingOptions: WorkDoneProgressOptions, Codable,
}
}

public struct FoldingRangeOptions: Codable, Hashable {
public struct FoldingRangeOptions: Codable, Hashable, Sendable {
/// Currently empty in the spec.
public init() {}
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/SKCore/BuildSystemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import LanguageServerProtocol

import struct TSCBasic.AbsolutePath

#if canImport(os)
import os
#endif

/// `BuildSystem` that integrates client-side information such as main-file lookup as well as providing
/// common functionality such as caching.
///
Expand Down Expand Up @@ -169,7 +173,7 @@ extension BuildSystemManager {
logger.error("Getting build settings failed: \(error.forLogging)")
}

guard var settings = fallbackBuildSystem?.buildSettings(for: document, language: language) else {
guard var settings = await fallbackBuildSystem?.buildSettings(for: document, language: language) else {
return nil
}
if buildSystem == nil {
Expand Down
6 changes: 5 additions & 1 deletion Sources/SKCore/FallbackBuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import struct TSCBasic.AbsolutePath
import class TSCBasic.Process

/// A simple BuildSystem suitable as a fallback when accurate settings are unknown.
public final class FallbackBuildSystem {
public actor FallbackBuildSystem {

let buildSetup: BuildSetup

Expand All @@ -38,6 +38,10 @@ public final class FallbackBuildSystem {
)
}()

@_spi(Testing) public func setSdkPath(_ newValue: AbsolutePath?) {
self.sdkpath = newValue
}

/// Delegate to handle any build system events.
public weak var delegate: BuildSystemDelegate? = nil

Expand Down
5 changes: 4 additions & 1 deletion Sources/SKSupport/LineTable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
//===----------------------------------------------------------------------===//

import LSPLogging
#if canImport(os)
import os
#endif

public struct LineTable: Hashable {
public struct LineTable: Hashable, Sendable {
@usableFromInline
var impl: [String.Index]

Expand Down
6 changes: 6 additions & 0 deletions Sources/SKSupport/ThreadSafeBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public class ThreadSafeBox<T>: @unchecked Sendable {
_value = initialValue
}

public func withLock<Result>(_ body: (inout T) -> Result) -> Result {
return lock.withLock {
return body(&_value)
}
}

/// If the value in the box is an optional, return it and reset it to `nil`
/// in an atomic operation.
public func takeValue<U>() -> T where U? == T {
Expand Down
18 changes: 14 additions & 4 deletions Sources/SourceKitLSP/Clang/ClangLanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ extension NSLock {
}

/// Gathers data from clangd's stderr pipe. When it has accumulated a full line, writes the the line to the logger.
fileprivate class ClangdStderrLogForwarder {
fileprivate actor ClangdStderrLogForwarder {
/// Queue on which all data from `clangd`’s stderr will be forwarded to `stderr`. This allows us to have a
/// nonisolated `handle` function but ensure that data gets processed in order.
private let queue = AsyncQueue<Serial>()
private var buffer = Data()

func handle(_ newData: Data) {
private func handleImpl(_ newData: Data) {
self.buffer += newData
while let newlineIndex = self.buffer.firstIndex(of: UInt8(ascii: "\n")) {
// Output a separate log message for every line in clangd's stderr.
Expand All @@ -50,6 +53,12 @@ fileprivate class ClangdStderrLogForwarder {
buffer = buffer[buffer.index(after: newlineIndex)...]
}
}

nonisolated func handle(_ newData: Data) {
queue.async {
await self.handleImpl(newData)
}
}
}

/// A thin wrapper over a connection to a clangd server providing build setting handling.
Expand Down Expand Up @@ -328,7 +337,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
nonisolated func handle<R: RequestType>(
_ params: R,
id: RequestID,
reply: @escaping (LSPResult<R.Response>) -> Void
reply: @Sendable @escaping (LSPResult<R.Response>) -> Void
) {
logger.info(
"""
Expand Down Expand Up @@ -442,10 +451,11 @@ extension ClangLanguageService {
}

public func shutdown() async {
let clangd = clangd!
await withCheckedContinuation { continuation in
_ = clangd.send(ShutdownRequest()) { _ in
Task {
await self.clangd.send(ExitNotification())
clangd.send(ExitNotification())
continuation.resume()
}
}
Expand Down
12 changes: 7 additions & 5 deletions Sources/SourceKitLSP/DocumentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import SwiftSyntax
/// data structure that is stored internally by the ``DocumentManager`` is a
/// ``Document``. The purpose of a ``DocumentSnapshot`` is to be able to work
/// with one version of a document without having to think about it changing.
public struct DocumentSnapshot: Identifiable {
public struct DocumentSnapshot: Identifiable, Sendable {
/// An ID that uniquely identifies the version of the document stored in this
/// snapshot.
public struct ID: Hashable, Comparable {
public struct ID: Hashable, Comparable, Sendable {
public let uri: DocumentURI
public let version: Int

Expand Down Expand Up @@ -84,16 +84,18 @@ public final class Document {
}
}

public final class DocumentManager: InMemoryDocumentManager {
public final class DocumentManager: InMemoryDocumentManager, Sendable {

public enum Error: Swift.Error {
case alreadyOpen(DocumentURI)
case missingDocument(DocumentURI)
}

let queue: DispatchQueue = DispatchQueue(label: "document-manager-queue")
// FIXME: (async) Migrate this to be an AsyncQueue
private let queue: DispatchQueue = DispatchQueue(label: "document-manager-queue")

var documents: [DocumentURI: Document] = [:]
// `nonisolated(unsafe)` is fine because `documents` is guarded by queue.
nonisolated(unsafe) var documents: [DocumentURI: Document] = [:]

public init() {}

Expand Down
6 changes: 3 additions & 3 deletions Sources/SourceKitLSP/LanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum LanguageServerState {
case semanticFunctionalityDisabled
}

public struct RenameLocation {
public struct RenameLocation: Sendable {
/// How the identifier at a given location is being used.
///
/// This is primarily used to influence how argument labels should be renamed in Swift and if a location should be
Expand Down Expand Up @@ -64,7 +64,7 @@ public struct RenameLocation {
///
/// For example, we may have a language service that provides semantic functionality for c-family using a clangd server,
/// launched from a specific toolchain or from sourcekitd.
public protocol LanguageService: AnyObject {
public protocol LanguageService: AnyObject, Sendable {

// MARK: - Creation

Expand All @@ -90,7 +90,7 @@ public protocol LanguageService: AnyObject {

/// Add a handler that is called whenever the state of the language server changes.
func addStateChangeHandler(
handler: @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void
handler: @Sendable @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void
) async

// MARK: - Text synchronization
Expand Down
35 changes: 27 additions & 8 deletions Sources/SourceKitLSP/Rename.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//

import IndexStoreDB
@preconcurrency import IndexStoreDB
import LSPLogging
import LanguageServerProtocol
import SKSupport
Expand Down Expand Up @@ -445,7 +445,7 @@ extension SwiftLanguageService {
/// These names might differ. For example, an Objective-C method gets translated by the clang importer to form the Swift
/// name or it could have a `SWIFT_NAME` attribute that defines the method's name in Swift. Similarly, a Swift symbol
/// might specify the name by which it gets exposed to Objective-C using the `@objc` attribute.
public struct CrossLanguageName {
public struct CrossLanguageName: Sendable {
/// The name of the symbol in clang languages or `nil` if the symbol is defined in Swift, doesn't have any references
/// from clang languages and thus hasn't been translated.
fileprivate let clangName: String?
Expand Down Expand Up @@ -564,6 +564,22 @@ extension SourceKitLSPServer {
return nil
}

// FIXME: (async-workaround): Needed to work around rdar://127977642
private func translateClangNameToSwift(
_ swiftLanguageService: SwiftLanguageService,
at symbolLocation: SymbolLocation,
in snapshot: DocumentSnapshot,
isObjectiveCSelector: Bool,
name: String
) async throws -> String {
return try await swiftLanguageService.translateClangNameToSwift(
at: symbolLocation,
in: snapshot,
isObjectiveCSelector: isObjectiveCSelector,
name: name
)
}

private func getCrossLanguageName(
forDefinitionOccurrence definitionOccurrence: SymbolOccurrence,
overrideName: String? = nil,
Expand Down Expand Up @@ -598,7 +614,8 @@ extension SourceKitLSPServer {
let swiftName: String?
if let swiftReference = await getReferenceFromSwift(usr: usr, index: index, workspace: workspace) {
let isObjectiveCSelector = definitionLanguage == .objective_c && definitionSymbol.kind.isMethod
swiftName = try await swiftReference.swiftLanguageService.translateClangNameToSwift(
swiftName = try await self.translateClangNameToSwift(
swiftReference.swiftLanguageService,
at: swiftReference.location,
in: swiftReference.snapshot,
isObjectiveCSelector: isObjectiveCSelector,
Expand Down Expand Up @@ -670,7 +687,7 @@ extension SourceKitLSPServer {
guard let workspace = await workspaceForDocument(uri: uri) else {
throw ResponseError.workspaceNotOpen(uri)
}
guard let primaryFileLanguageService = workspace.documentService[uri] else {
guard let primaryFileLanguageService = workspace.documentService.value[uri] else {
return nil
}

Expand Down Expand Up @@ -716,24 +733,26 @@ extension SourceKitLSPServer {
var locationsByFile: [URL: [RenameLocation]] = [:]

actor LanguageServerTypesCache {
let index: CheckedIndex
let index: UncheckedIndex
var languageServerTypesCache: [URL: LanguageServerType?] = [:]

init(index: CheckedIndex) {
init(index: UncheckedIndex) {
self.index = index
}

func languageServerType(for url: URL) -> LanguageServerType? {
if let cachedValue = languageServerTypesCache[url] {
return cachedValue
}
let serverType = LanguageServerType(symbolProvider: index.symbolProvider(for: url.path))
let serverType = LanguageServerType(
symbolProvider: index.checked(for: .deletedFiles).symbolProvider(for: url.path)
)
languageServerTypesCache[url] = serverType
return serverType
}
}

let languageServerTypesCache = LanguageServerTypesCache(index: index)
let languageServerTypesCache = LanguageServerTypesCache(index: index.unchecked)

let usrsToRename = overridingAndOverriddenUsrs(of: usr, index: index)
let occurrencesToRename = usrsToRename.flatMap { index.occurrences(ofUSR: $0, roles: renameRoles) }
Expand Down
4 changes: 2 additions & 2 deletions Sources/SourceKitLSP/Sequence+AsyncMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
extension Sequence {
/// Just like `Sequence.map` but allows an `async` transform function.
func asyncMap<T>(
_ transform: (Element) async throws -> T
@_inheritActorContext _ transform: @Sendable (Element) async throws -> T
) async rethrows -> [T] {
var result: [T] = []
result.reserveCapacity(self.underestimatedCount)
Expand All @@ -27,7 +27,7 @@ extension Sequence {

/// Just like `Sequence.compactMap` but allows an `async` transform function.
func asyncCompactMap<T>(
_ transform: (Element) async throws -> T?
@_inheritActorContext _ transform: @Sendable (Element) async throws -> T?
) async rethrows -> [T] {
var result: [T] = []

Expand Down
Loading