ThreadSafeBox.init takes `consuming sending Value`, so its argument
must be region-isolated. Without `sending` on this init's parameter,
callers in some build configurations couldn't satisfy the sending
requirement and triggered SendingRisksDataRace.
Inner closures captured `sourceKitLSPServer` implicitly. The outer
`Task` already captures it weakly, making it a `var` in scope; inner
@Sendable closures that capture this `var` implicitly trigger the
`SendableClosureCaptures` diagnostic on compiler configurations
without `ImmutableWeakCaptures`.
Add explicit `[weak sourceKitLSPServer]` to each inner closure so the
capture binds cleanly to a fresh weak local.
Logs five points to diagnose flakes where indexing tasks stall in
the scheduling pipeline:
- when scheduleIndexing reaches the post-preparation withTaskGroup
- when schedulePreparation creates a QueuedTask, with priorities
- when updateIndexStore creates a QueuedTask, with priorities
- when poke() rejects a pending task because of budget
- when QueuedTask.resultTask body starts, capturing both the stored
and runtime priorities to detect priority elevation races
Together these distinguish whether the indexTask body wedged, a
QueuedTask was never created, priority escalation never fired
poke() to retry, or the resultTask body started after an elevation
had already happened.
Delete the local `withTimeout` and `withTaskPriorityChangedHandler`
copies in favor of the upstream versions.
Update callers of `withTaskPriorityChangedHandler` to receive the new
priority via the callback argument rather than reading
`Task.currentPriority`.
`QueuedTask.resultTask` passed `self.priority` as `initialPriority` to
`withTaskPriorityChangedHandler`. If `elevatePriority(to:)` ran before
`resultTask`'s body started (e.g. the detached task hadn't been scheduled
yet), `self.priority` was already set to the elevated value, so the
polling baseline matched `Task.currentPriority` and `taskPriorityChanged`
never fired. The callback is what calls `poke()`, so the scheduler never
re-evaluated the elevated task and it could stall until an upstream 180s
timeout cancelled the chain.
Use the captured `priority` parameter instead. It matches the priority
that `Task.detached(priority: priority)` uses to launch the resultTask
and isn't subject to mutation by `elevatePriority`.
`shutDown` only cancels `currentlyExecutingTasks`, leaving
`pendingTasks` untouched. External callers awaiting `waitToFinish()`
on a pending task hang forever — `resultTask` keeps awaiting an
`AsyncStream` that the scheduler will never yield into once
`isShutDown` is set — and each such `QueuedTask` is leaked via the
strong cycle through its `resultTask` closure.
Cancel pending tasks too: `task.cancel()` cancels `resultTask`, the
`for await` over the stream exits immediately on cancellation, and
`waitToFinish()` returns in microseconds.
Wrap each `task.waitToFinish()` in `TaskScheduler.shutDown()` with a
10s `withTimeout` so a stuck QueuedTask cannot block sourcekit-lsp's
shutdown indefinitely; on timeout, log the task description so the
offender is identifiable. The QueuedTask is already cancelled via
`task.cancel()` and continues winding down in the background.
Add debug logs after `Process.waitUntilExit()` returns and after
`Process.run` returns inside `prepare(singleTarget:)`, so a future
shutdown hang can be triaged: did the swift build process actually
exit, did `Process.run` propagate the result, or is the stall higher
up in the prepare chain.
Motivated by a recurring Windows CI hang in
BackgroundIndexingTests.testSymlinkedTargetReferringToSameSourceFile
where prepareForExit stalls for the full test timeout with no log
output during shutdown.
Delete sourcekit-lsp's `Sources/SwiftExtensions/ThreadSafeBox.swift`
and the now-unused `NSLock+WithLock.swift`.
Use the `Mutex`-backed `ThreadSafeBox` from swift-tools-protocols'
Adjust call sites for the new API: e.g.
`foo.value.mutate()` with `foo.withLock { $0.mutate() }` because it
doesn't provide `_modify` accessor.
The `WorkspaceConfiguration.default` was being used to populate
the `Workspace`; there was an extra step here needed to assure that
we are propagating the trait configuration to the workspace.
The added test assures that non-default traits that are enabled are
indeed processed as enabled.
Adds a syntactic refactoring action that converts implicit raw values to
explicit ones for enums whose first inherited type is an integer type
(any width up to Int128/UInt128) or String.
Resolves#2516.
Before:
enum Status: Int {
case active
case inactive
case pending = 10
case archived
}
After:
enum Status: Int {
case active = 0
case inactive = 1
case pending = 10
case archived = 11
}
The action is exposed through `SyntaxRefactoringCodeActionProvider` and
performs all transformations as a list of `SourceEdit`s, leaving the
rest of the enum (including trailing trivia and member ordering)
untouched.
### Motivation:
For enums with implicit raw values, making the values explicit is a
common refactoring step before serialising the values or relying on
their numeric identity. Doing the renumbering by hand is error-prone
once any cases already have explicit values, because the implicit
continuation rule has to be reapplied for every gap.
### Modifications:
- New `Sources/SwiftSyntaxCodeActions/AddExplicitEnumRawValues.swift`
conforming to `EditRefactoringProvider` and
`SyntaxRefactoringCodeActionProvider`. Handles negative integer
literals, hex/binary/octal literals with underscore separators, and
backticked case identifiers in String enums.
- Registered in `Sources/SwiftSyntaxCodeActions/SyntaxCodeActions.swift`
and `Sources/SwiftSyntaxCodeActions/CMakeLists.txt`.
- Twelve LSP-level tests in `Tests/SourceKitLSPTests/CodeActionTests.swift`
covering positive cases (Int, String, negative continuation, hex
continuation, backtick stripping) and negative cases (no raw value
type, raw value type not first in inheritance clause, all cases
already explicit, unsupported raw value expression, #if directives,
freestanding macro expansions, associated value cases).
### Result:
When the cursor is on an enum declaration whose raw value type is
supported and at least one case is missing an explicit raw value,
SourceKit-LSP offers the "Add Explicit Raw Values" code action and
inserts the missing raw values in place. The action is suppressed in
situations where the implicit numbering cannot be computed safely
(unsupported raw value expression, #if blocks, freestanding macros,
associated value cases).
For ID counters and standalone flags (most sites), drop to .relaxed
since nothing reads other memory based on the atomic's value.
MultiEntrySemaphore.signaled is the exception: it is used as a
signal/wait primitive where waiters proceed to use state set up
before signal(). Use .releasing on store and .acquiring on load so
that pre-signal writes are visible to waiters that observe `true`.
.relaxed there would be incorrect on weakly-ordered architectures.
Replace AtomicBool/UInt8/UInt32/Int32 from
ToolsProtocolsSwiftExtensions with Synchronization.Atomic<T> where
the storage is a static, module-level let, or class stored property.
For local lets captured by @Sendable closures (where Atomic's
~Copyable nature prevents capture), use ThreadSafeBox<T> instead.
Rename languageServices(for:), primaryLanguageService(for:), and their
internal counterparts to use the `forOpenDocument` label, so the
precondition that the document must already be open is visible at call
sites.
Also make primaryLanguageService(forOpenDocument:) throw instead of
returning an optional, and switch several resolve-style handlers from
the find-or-create primaryLanguageService(for:_:) to
primaryLanguageService(forOpenDocument:), since those call sites already
have an open document in hand.
vscode provides an alternative action if the target of goto definition is in the same character range we requested.
this requires the start and end in the LSP response to be properly set, so vscode can see that the current character
is actually part of the request
Replace 'weak var' with 'weak let' for weak reference properties that are
set in init and never reassigned, using the Swift 6.3 feature introduced
by SE-0481.
Previously, language services were held in a global registry on
SourceKitLSPServer and shared across workspaces, requiring complex
lifetime tracking (isImmortal, shutdownOrphanedLanguageServices) to
decide when to tear them down. In practice, every language service
already stored workspace-specific properties (buildServerManager,
semanticIndexManagerTask), so sharing them across workspaces was never
truly safe. Giving each Workspace its own service instances simplifies
lifetime management: services are created when needed and shut down
with their workspace.
Remove LanguageService.isImmortal, the workspace parameter from
canHandle(toolchain:), and the initialize/clientInitialized protocol
requirements.
The buildSettingsFile key is an implementation detail of Workspace's
language-service dictionary, so the guard that prevents removing a
still-needed service belongs there rather than in SourceKitLSPServer.
Closing a generated interface document (sourcekit-lsp:// URI) was
removing the language service for the originating source file because
both share the same buildSettingsFile key. Guard the removal so it
only happens when no other open document shares that key.
Relates to #2209
- **`sourcekit/workspace/symbolNames`** — returns a flat, deduplicated
list of every symbol name in the workspace index (source and indexed
system modules). Clients use this to drive their search UI locally.
- **`sourcekit/workspace/symbolInfo`** — given a list of exact symbol
names, returns `WorkspaceSymbolItem` for each occurrence across all
workspaces, for display in the search result list. Source-file symbols
get `SymbolInformation` with a `file://` location. SDK/stdlib symbols
get a `WorkspaceSymbol` with `location: .uri(…)` The client must call
`workspaceSymbol/resolve` after the user selects an SDK/stdlib symbol to
obtain the concrete interface location.
- **`workspaceSymbol/resolve`** — resolves the deferred
`WorkspaceSymbol` location from `sourcekit/workspace/symbolInfo`. Parses
the `?module=` value into `moduleName`/`groupName`, finds a real source
file via `mainFiles(containing:)`, calls `openGeneratedInterface`, and
returns the symbol with `location` replaced by a full
`sourcekit-lsp://generated-swift-interface/` URI + range (or a temp
`file://` path for clients without `workspace/getReferenceDocument`
support).
- WorkspaceEdit.adjusted(for:) was using originalURI(for:) ?? uri, which
skips the file-existence check; switch to adjustedURI(for:)
- TypeHierarchyItem.adjusted(for:) was not adjusting the URI stored in
data, unlike CallHierarchyItem
`CopiedFileMap` previously held methods like
`workspaceEditAdjustedForCopiedFiles`,
`callHierarchyItemAdjustedForCopiedFiles`, etc. that encoded knowledge
of high-level LSP message types into `BuildServerIntegration`.
Replace them with `adjusted(for:)` methods on each LSP type
(`Location`, `[Location]`, `LocationsOrLocationLinksResponse`,
`WorkspaceEdit`, `CallHierarchyItem`, `TypeHierarchyItem`) in a new
`LSP+CopiedFileMap.swift` in the SourceKitLSP module. The primitive
URI remapping is extracted into `CopiedFileMap.adjustedURI(for:)`
which remains in `BuildServerIntegration`.
`CallHierarchyItem.adjusted(for:)` also drops manual `LSPAny`
dictionary construction in favour of `HierarchyItemData`.
Introduce `SourceKitDCore` as the protocol boundary between dylib
lifecycle management and the high-level `SourceKitD` API. Its single
lifecycle entry point, `initializeService(api:notificationCallback:)`,
receives the already-loaded `sourcekitd_api_functions_t` from
`SourceKitD.init(core:)`.
`SourceKitDCoreImpl` is the standard implementation: `init` opens the
dylib; `initializeService` registers any plugin paths, calls
`api.initialize()`, and wires the notification handler; `deinit` calls
`shutdown()` and closes the handle. Pre-initialized conformances
implement `initializeService` as a no-op.
Wire a `sourcekitdCoreInjector` hook through `Hooks` so an embedding
host can return a pre-initialized `SourceKitDCore` for a given toolchain
path, preventing `sourcekitd_initialize()` from being called a second
time.
Declare `SourceKitDCoreForPlugin` at its use sites so each call site
can express the exact deinit behavior it needs: `dlclose` for handles
acquired via `RTLD_NOLOAD`, and `leak` for externally-owned handles.