From 8e300ee4438d42d524810b66563b85a57a973319 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:35:06 +0530 Subject: [PATCH 01/12] add go-to-definition for inlay hints (#2318) Implements resolveProvider for inlay hints to enable navigating to type definitions. When an inlay hint showing a type is resolved, the server looks up the type's definition location using cursorInfo and the index. - store variable position in InlayHint.data for resolution - add inlayHintResolve to LanguageService protocol - implement resolve handler using cursorInfo and index lookup - enable resolveProvider: true in capabilities - add test for resolve functionality Addresses #2318 --- Sources/SourceKitLSP/LanguageService.swift | 6 + Sources/SourceKitLSP/SourceKitLSPServer.swift | 22 +++- .../InlayHintResolve.swift | 108 ++++++++++++++++++ Sources/SwiftLanguageService/InlayHints.swift | 21 +++- .../SwiftLanguageService.swift | 2 +- Tests/SourceKitLSPTests/InlayHintTests.swift | 57 ++++++++- 6 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 Sources/SwiftLanguageService/InlayHintResolve.swift diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index 2294fdec..be6f0634 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -248,6 +248,7 @@ package protocol LanguageService: AnyObject, Sendable { func colorPresentation(_ req: ColorPresentationRequest) async throws -> [ColorPresentation] func codeAction(_ req: CodeActionRequest) async throws -> CodeActionRequestResponse? func inlayHint(_ req: InlayHintRequest) async throws -> [InlayHint] + func inlayHintResolve(_ req: InlayHintResolveRequest) async throws -> InlayHint func codeLens(_ req: CodeLensRequest) async throws -> [CodeLens] func documentDiagnostic(_ req: DocumentDiagnosticsRequest) async throws -> DocumentDiagnosticReport func documentFormatting(_ req: DocumentFormattingRequest) async throws -> [TextEdit]? @@ -471,6 +472,11 @@ package extension LanguageService { throw ResponseError.requestNotImplemented(InlayHintRequest.self) } + func inlayHintResolve(_ req: InlayHintResolveRequest) async throws -> InlayHint { + // default: return hint unchanged + return req.inlayHint + } + func codeLens(_ req: CodeLensRequest) async throws -> [CodeLens] { throw ResponseError.requestNotImplemented(CodeLensRequest.self) } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 2883b0f3..3e992a00 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -832,6 +832,8 @@ extension SourceKitLSPServer: QueueBasedMessageHandler { initialized = true case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.inlayHint) + case let request as RequestAndReply: + await request.reply { try await inlayHintResolve(request: request.params) } case let request as RequestAndReply: await request.reply { try await self.isIndexing(request.params) } case let request as RequestAndReply: @@ -1098,7 +1100,7 @@ extension SourceKitLSPServer { let inlayHintOptions = await registry.clientHasDynamicInlayHintRegistration ? nil - : ValueOrBool.value(InlayHintOptions(resolveProvider: false)) + : ValueOrBool.value(InlayHintOptions(resolveProvider: true)) let semanticTokensOptions = await registry.clientHasDynamicSemanticTokensRegistration @@ -1901,6 +1903,24 @@ extension SourceKitLSPServer { return try await languageService.inlayHint(req) } + func inlayHintResolve( + request: InlayHintResolveRequest + ) async throws -> InlayHint { + // inlay hints store the uri in data for resolution + // extract uri from the lspany dictionary + guard case .dictionary(let dict) = request.inlayHint.data, + case .string(let uriString) = dict["uri"], + let uri = try? DocumentURI(string: uriString) + else { + return request.inlayHint + } + guard let workspace = await self.workspaceForDocument(uri: uri) else { + return request.inlayHint + } + let language = try documentManager.latestSnapshot(uri.buildSettingsFile).language + return try await primaryLanguageService(for: uri, language, in: workspace).inlayHintResolve(request) + } + func documentDiagnostic( _ req: DocumentDiagnosticsRequest, workspace: Workspace, diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift new file mode 100644 index 00000000..5057a026 --- /dev/null +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2026 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 +// +//===----------------------------------------------------------------------===// + +@_spi(SourceKitLSP) package import LanguageServerProtocol +import Foundation +import IndexStoreDB +import SemanticIndex +import SourceKitD +import SourceKitLSP + +extension SwiftLanguageService { + /// resolves an inlay hint by looking up the type definition location + package func inlayHintResolve(_ req: InlayHintResolveRequest) async throws -> InlayHint { + var hint = req.inlayHint + + // only resolve type hints that have stored data + // extract uri and position from the lspany dictionary + guard hint.kind == .type, + case .dictionary(let dict) = hint.data, + case .string(let uriString) = dict["uri"], + let uri = try? DocumentURI(string: uriString), + case .dictionary(let posDict) = dict["position"], + case .int(let line) = posDict["line"], + case .int(let character) = posDict["character"] + else { + return hint + } + let position = Position(line: line, utf16index: character) + + // get the type usr by calling cursor info at the variable position + let typeLocation = try await lookupTypeDefinitionLocation( + uri: uri, + position: position + ) + + guard let typeLocation else { + return hint + } + + // return new hint with label parts that have location for go-to-definition + if case .string(let labelText) = hint.label { + return InlayHint( + position: hint.position, + label: .parts([InlayHintLabelPart(value: labelText, location: typeLocation)]), + kind: hint.kind, + textEdits: hint.textEdits, + tooltip: hint.tooltip, + paddingLeft: hint.paddingLeft, + paddingRight: hint.paddingRight, + data: hint.data + ) + } + + return hint + } + + /// looks up the definition location for the type at the given position + private func lookupTypeDefinitionLocation( + uri: DocumentURI, + position: Position + ) async throws -> Location? { + let snapshot = try await self.latestSnapshot(for: uri) + let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: false) + + // call cursor info at the variable position to get the type usr + let skreq = sourcekitd.dictionary([ + keys.cancelOnSubsequentRequest: 0, + keys.offset: snapshot.utf8Offset(of: position), + keys.sourceFile: snapshot.uri.sourcekitdSourceFile, + keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath, + keys.compilerArgs: compileCommand?.compilerArgs as [any SKDRequestValue]?, + ]) + + let dict = try await send(sourcekitdRequest: \.cursorInfo, skreq, snapshot: snapshot) + + guard let typeUsr: String = dict[keys.typeUsr] else { + return nil + } + + // look up the type definition in the index + guard let workspace = await sourceKitLSPServer?.workspaceForDocument(uri: uri), + let index = await workspace.index(checkedFor: .deletedFiles) + else { + return nil + } + + guard let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: typeUsr) else { + return nil + } + + let definitionUri = DocumentURI(filePath: occurrence.location.path, isDirectory: false) + let definitionPosition = Position( + line: occurrence.location.line - 1, + utf16index: occurrence.location.utf8Column - 1 + ) + + return Location(uri: definitionUri, range: Range(definitionPosition)) + } +} diff --git a/Sources/SwiftLanguageService/InlayHints.swift b/Sources/SwiftLanguageService/InlayHints.swift index 1133d222..a6c5f9d3 100644 --- a/Sources/SwiftLanguageService/InlayHints.swift +++ b/Sources/SwiftLanguageService/InlayHints.swift @@ -11,10 +11,19 @@ //===----------------------------------------------------------------------===// @_spi(SourceKitLSP) package import LanguageServerProtocol +import Foundation import SourceKitLSP import SwiftExtensions import SwiftSyntax + +package struct InlayHintResolveData: Codable { + /// the document uri containing the variable + package let uri: DocumentURI + + package let position: Position +} + private class IfConfigCollector: SyntaxVisitor { private var ifConfigDecls: [IfConfigDeclSyntax] = [] @@ -40,6 +49,7 @@ extension SwiftLanguageService { .filter { !$0.hasExplicitType } .map { info -> InlayHint in let position = info.range.upperBound + let variableStart = info.range.lowerBound let label = ": \(info.printedType)" let textEdits: [TextEdit]? if info.canBeFollowedByTypeAnnotation { @@ -47,11 +57,20 @@ extension SwiftLanguageService { } else { textEdits = nil } + // store resolve data so we can look up type definition later + let data: LSPAny = .dictionary([ + "uri": .string(uri.stringValue), + "position": .dictionary([ + "line": .int(variableStart.line), + "character": .int(variableStart.utf16index) + ]) + ]) return InlayHint( position: position, label: .string(label), kind: .type, - textEdits: textEdits + textEdits: textEdits, + data: data ) } diff --git a/Sources/SwiftLanguageService/SwiftLanguageService.swift b/Sources/SwiftLanguageService/SwiftLanguageService.swift index 639a5a64..60871536 100644 --- a/Sources/SwiftLanguageService/SwiftLanguageService.swift +++ b/Sources/SwiftLanguageService/SwiftLanguageService.swift @@ -413,7 +413,7 @@ extension SwiftLanguageService { range: .bool(true), full: .bool(true) ), - inlayHintProvider: .value(InlayHintOptions(resolveProvider: false)), + inlayHintProvider: .value(InlayHintOptions(resolveProvider: true)), diagnosticProvider: DiagnosticOptions( interFileDependencies: true, workspaceDiagnostics: false diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 184c8034..9c83fa19 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -59,6 +59,23 @@ final class InlayHintTests: SourceKitLSPTestCase { ) } + /// compares hints ignoring the data field (which contains implementation-specific resolve data) + private func assertHintsEqual( + _ actual: [InlayHint], + _ expected: [InlayHint], + file: StaticString = #filePath, + line: UInt = #line + ) { + XCTAssertEqual(actual.count, expected.count, "Hint count mismatch", file: file, line: line) + for (actualHint, expectedHint) in zip(actual, expected) { + XCTAssertEqual(actualHint.position, expectedHint.position, file: file, line: line) + XCTAssertEqual(actualHint.label, expectedHint.label, file: file, line: line) + XCTAssertEqual(actualHint.kind, expectedHint.kind, file: file, line: line) + XCTAssertEqual(actualHint.textEdits, expectedHint.textEdits, file: file, line: line) + XCTAssertEqual(actualHint.tooltip, expectedHint.tooltip, file: file, line: line) + } + } + // MARK: - Tests func testEmpty() async throws { @@ -73,7 +90,7 @@ final class InlayHintTests: SourceKitLSPTestCase { var y2️⃣ = "test" + "123" """ ) - XCTAssertEqual( + assertHintsEqual( hints, [ makeInlayHint( @@ -106,7 +123,7 @@ final class InlayHintTests: SourceKitLSPTestCase { """, range: ("1️⃣", "4️⃣") ) - XCTAssertEqual( + assertHintsEqual( hints, [ makeInlayHint( @@ -141,7 +158,7 @@ final class InlayHintTests: SourceKitLSPTestCase { } """ ) - XCTAssertEqual( + assertHintsEqual( hints, [ makeInlayHint( @@ -198,7 +215,7 @@ final class InlayHintTests: SourceKitLSPTestCase { } """ ) - XCTAssertEqual( + assertHintsEqual( hints, [ makeInlayHint( @@ -267,4 +284,36 @@ final class InlayHintTests: SourceKitLSPTestCase { ) XCTAssertEqual(hints, []) } + + func testInlayHintResolve() async throws { + // test that resolving an inlay hint returns label parts with type location + let project = try await IndexedSingleSwiftFileTestProject( + """ + struct MyType {} + let x1️⃣ = MyType() + """ + ) + + // get inlay hints + let request = InlayHintRequest(textDocument: TextDocumentIdentifier(project.fileURI), range: nil) + let hints = try await project.testClient.send(request) + + // find thee type hint for x + guard let typeHint = hints.first(where: { $0.kind == .type }) else { + XCTFail("Expected type hint") + return + } + + XCTAssertNotNil(typeHint.data, "Expected type hint to have data for resolution") + + // resolve the hint to get type location + let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) + + if case .parts(let parts) = resolvedHint.label { + XCTAssertEqual(parts.count, 1) + XCTAssertNotNil(parts.first?.location, "Expected label part to have location for go-to-definition") + } else if case .string = resolvedHint.label { + + } + } } From f46a31ba3d0fbd3b707e5825106aa62de539dc2c Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:56:25 +0530 Subject: [PATCH 02/12] Use new cursorInfo type declaration fields for inlay hints update inlay hint resolution to use the new type declaration location fields from cursorInfo, with fallback to index lookup using the new typeDeclUsr (a proper declaration USR). Changes i made : - Regenerated sourcekitd_uids.swift with new keys - InlayHintResolve.swift: Try direct location first, fallback to index - InlayHintTests.swift: Fixed test to properly verify location This Requires: swift/swiftlang PR## #86381 for compiler changes --- Sources/SourceKitD/sourcekitd_uids.swift | 40 ++++++++++++++----- Sources/SourceKitD/sourcekitd_uids.swift.gyb | 4 +- .../InlayHintResolve.swift | 33 ++++++++++----- Tests/SourceKitLSPTests/InlayHintTests.swift | 24 +++++++---- 4 files changed, 70 insertions(+), 31 deletions(-) diff --git a/Sources/SourceKitD/sourcekitd_uids.swift b/Sources/SourceKitD/sourcekitd_uids.swift index 09a73cdb..37fc63e2 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift +++ b/Sources/SourceKitD/sourcekitd_uids.swift @@ -12,10 +12,9 @@ // Automatically Generated From UIDs.swift.gyb. // Do Not Edit Directly! To regenerate run Utilities/generate-uids.py + package import Csourcekitd -// swift-format-ignore: TypeNamesShouldBeCapitalized -// Matching C style types package struct sourcekitd_api_keys { /// `key.version_major` package let versionMajor: sourcekitd_api_uid_t @@ -233,6 +232,16 @@ package struct sourcekitd_api_keys { package let typeUsr: sourcekitd_api_uid_t /// `key.containertypeusr` package let containerTypeUsr: sourcekitd_api_uid_t + /// `key.typedecl_usr` + package let typeDeclUsr: sourcekitd_api_uid_t + /// `key.typedecl_filepath` + package let typeDeclFilePath: sourcekitd_api_uid_t + /// `key.typedecl_line` + package let typeDeclLine: sourcekitd_api_uid_t + /// `key.typedecl_column` + package let typeDeclColumn: sourcekitd_api_uid_t + /// `key.typedecl_modulename` + package let typeDeclModuleName: sourcekitd_api_uid_t /// `key.modulegroups` package let moduleGroups: sourcekitd_api_uid_t /// `key.basename` @@ -641,6 +650,11 @@ package struct sourcekitd_api_keys { removeCache = api.uid_get_from_cstr("key.removecache")! typeUsr = api.uid_get_from_cstr("key.typeusr")! containerTypeUsr = api.uid_get_from_cstr("key.containertypeusr")! + typeDeclUsr = api.uid_get_from_cstr("key.typedecl_usr")! + typeDeclFilePath = api.uid_get_from_cstr("key.typedecl_filepath")! + typeDeclLine = api.uid_get_from_cstr("key.typedecl_line")! + typeDeclColumn = api.uid_get_from_cstr("key.typedecl_column")! + typeDeclModuleName = api.uid_get_from_cstr("key.typedecl_modulename")! moduleGroups = api.uid_get_from_cstr("key.modulegroups")! baseName = api.uid_get_from_cstr("key.basename")! argNames = api.uid_get_from_cstr("key.argnames")! @@ -793,8 +807,6 @@ package struct sourcekitd_api_keys { } } -// swift-format-ignore: TypeNamesShouldBeCapitalized -// Matching C style types package struct sourcekitd_api_requests { /// `source.request.protocol_version` package let protocolVersion: sourcekitd_api_uid_t @@ -969,8 +981,6 @@ package struct sourcekitd_api_requests { } } -// swift-format-ignore: TypeNamesShouldBeCapitalized -// Matching C style types package struct sourcekitd_api_values { /// `source.lang.swift.decl.function.free` package let declFunctionFree: sourcekitd_api_uid_t @@ -1024,6 +1034,14 @@ package struct sourcekitd_api_values { package let declAccessorInit: sourcekitd_api_uid_t /// `source.lang.swift.ref.function.accessor.init` package let refAccessorInit: sourcekitd_api_uid_t + /// `source.lang.swift.decl.function.accessor.mutate` + package let declAccessorMutate: sourcekitd_api_uid_t + /// `source.lang.swift.ref.function.accessor.mutate` + package let refAccessorMutate: sourcekitd_api_uid_t + /// `source.lang.swift.decl.function.accessor.borrow` + package let declAccessorBorrow: sourcekitd_api_uid_t + /// `source.lang.swift.ref.function.accessor.borrow` + package let refAccessorBorrow: sourcekitd_api_uid_t /// `source.lang.swift.decl.function.constructor` package let declConstructor: sourcekitd_api_uid_t /// `source.lang.swift.ref.function.constructor` @@ -1248,6 +1266,8 @@ package struct sourcekitd_api_values { package let diagWarning: sourcekitd_api_uid_t /// `source.diagnostic.severity.error` package let diagError: sourcekitd_api_uid_t + /// `source.diagnostic.severity.remark` + package let diagRemark: sourcekitd_api_uid_t /// `source.diagnostic.category.deprecation` package let diagDeprecation: sourcekitd_api_uid_t /// `source.diagnostic.category.no_usage` @@ -1352,8 +1372,6 @@ package struct sourcekitd_api_values { package let semaEnabledNotification: sourcekitd_api_uid_t /// `source.notification.editor.documentupdate` package let documentUpdateNotification: sourcekitd_api_uid_t - /// `source.diagnostic.severity.remark` - package let diagRemark: sourcekitd_api_uid_t package init(api: sourcekitd_api_functions_t) { declFunctionFree = api.uid_get_from_cstr("source.lang.swift.decl.function.free")! @@ -1382,6 +1400,10 @@ package struct sourcekitd_api_values { refAccessorModify = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.modify")! declAccessorInit = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.init")! refAccessorInit = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.init")! + declAccessorMutate = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.mutate")! + refAccessorMutate = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.mutate")! + declAccessorBorrow = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.borrow")! + refAccessorBorrow = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.borrow")! declConstructor = api.uid_get_from_cstr("source.lang.swift.decl.function.constructor")! refConstructor = api.uid_get_from_cstr("source.lang.swift.ref.function.constructor")! declDestructor = api.uid_get_from_cstr("source.lang.swift.decl.function.destructor")! @@ -1494,6 +1516,7 @@ package struct sourcekitd_api_values { diagNote = api.uid_get_from_cstr("source.diagnostic.severity.note")! diagWarning = api.uid_get_from_cstr("source.diagnostic.severity.warning")! diagError = api.uid_get_from_cstr("source.diagnostic.severity.error")! + diagRemark = api.uid_get_from_cstr("source.diagnostic.severity.remark")! diagDeprecation = api.uid_get_from_cstr("source.diagnostic.category.deprecation")! diagNoUsage = api.uid_get_from_cstr("source.diagnostic.category.no_usage")! codeCompletionEverything = api.uid_get_from_cstr("source.codecompletion.everything")! @@ -1546,6 +1569,5 @@ package struct sourcekitd_api_values { semaDisabledNotification = api.uid_get_from_cstr("source.notification.sema_disabled")! semaEnabledNotification = api.uid_get_from_cstr("source.notification.sema_enabled")! documentUpdateNotification = api.uid_get_from_cstr("source.notification.editor.documentupdate")! - diagRemark = api.uid_get_from_cstr("source.diagnostic.severity.remark")! } } diff --git a/Sources/SourceKitD/sourcekitd_uids.swift.gyb b/Sources/SourceKitD/sourcekitd_uids.swift.gyb index da1f4186..9497f587 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift.gyb +++ b/Sources/SourceKitD/sourcekitd_uids.swift.gyb @@ -111,9 +111,7 @@ # Maintained from initializeService in Requests.cpp KIND('SemaEnabledNotification', 'source.notification.sema_enabled'), KIND('DocumentUpdateNotification', 'source.notification.editor.documentupdate'), - - # Used exclusively within the SourceKit Plugin - KIND('DiagRemark', 'source.diagnostic.severity.remark'), + # Note: DiagRemark was moved to UID_KINDS in UIDs.py ] TYPES_AND_KEYS = [ diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index 5057a026..c8b88958 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -@_spi(SourceKitLSP) package import LanguageServerProtocol import Foundation import IndexStoreDB +@_spi(SourceKitLSP) package import LanguageServerProtocol import SemanticIndex import SourceKitD import SourceKitLSP @@ -25,12 +25,12 @@ extension SwiftLanguageService { // only resolve type hints that have stored data // extract uri and position from the lspany dictionary guard hint.kind == .type, - case .dictionary(let dict) = hint.data, - case .string(let uriString) = dict["uri"], - let uri = try? DocumentURI(string: uriString), - case .dictionary(let posDict) = dict["position"], - case .int(let line) = posDict["line"], - case .int(let character) = posDict["character"] + case .dictionary(let dict) = hint.data, + case .string(let uriString) = dict["uri"], + let uri = try? DocumentURI(string: uriString), + case .dictionary(let posDict) = dict["position"], + case .int(let line) = posDict["line"], + case .int(let character) = posDict["character"] else { return hint } @@ -71,7 +71,7 @@ extension SwiftLanguageService { let snapshot = try await self.latestSnapshot(for: uri) let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: false) - // call cursor info at the variable position to get the type usr + // call cursor info at the variable position to get the type declaration location let skreq = sourcekitd.dictionary([ keys.cancelOnSubsequentRequest: 0, keys.offset: snapshot.utf8Offset(of: position), @@ -82,18 +82,29 @@ extension SwiftLanguageService { let dict = try await send(sourcekitdRequest: \.cursorInfo, skreq, snapshot: snapshot) - guard let typeUsr: String = dict[keys.typeUsr] else { + if let filepath: String = dict[keys.typeDeclFilePath], + let line: Int = dict[keys.typeDeclLine], + let column: Int = dict[keys.typeDeclColumn] + { + let definitionUri = DocumentURI(filePath: filepath, isDirectory: false) + let definitionPosition = Position(line: line - 1, utf16index: column - 1) + return Location(uri: definitionUri, range: Range(definitionPosition)) + } + + // fallback: use the type declaration USR with index lookup + + guard let typeDeclUsr: String = dict[keys.typeDeclUsr] else { return nil } // look up the type definition in the index guard let workspace = await sourceKitLSPServer?.workspaceForDocument(uri: uri), - let index = await workspace.index(checkedFor: .deletedFiles) + let index = await workspace.index(checkedFor: .deletedFiles) else { return nil } - guard let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: typeUsr) else { + guard let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: typeDeclUsr) else { return nil } diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 9c83fa19..e969605d 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -289,8 +289,8 @@ final class InlayHintTests: SourceKitLSPTestCase { // test that resolving an inlay hint returns label parts with type location let project = try await IndexedSingleSwiftFileTestProject( """ - struct MyType {} - let x1️⃣ = MyType() + 1️⃣struct MyType {} + let x2️⃣ = MyType() """ ) @@ -298,7 +298,7 @@ final class InlayHintTests: SourceKitLSPTestCase { let request = InlayHintRequest(textDocument: TextDocumentIdentifier(project.fileURI), range: nil) let hints = try await project.testClient.send(request) - // find thee type hint for x + // find the type hint for x guard let typeHint = hints.first(where: { $0.kind == .type }) else { XCTFail("Expected type hint") return @@ -309,11 +309,19 @@ final class InlayHintTests: SourceKitLSPTestCase { // resolve the hint to get type location let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) - if case .parts(let parts) = resolvedHint.label { - XCTAssertEqual(parts.count, 1) - XCTAssertNotNil(parts.first?.location, "Expected label part to have location for go-to-definition") - } else if case .string = resolvedHint.label { - + guard case .parts(let parts) = resolvedHint.label else { + XCTFail("Expected resolved hint to have label parts, got: \(resolvedHint.label)") + return } + + XCTAssertEqual(parts.count, 1, "Expected exactly one label part") + + guard let location = parts.first?.location else { + XCTFail("Expected label part to have location for go-to-definition") + return + } + + XCTAssertEqual(location.uri, project.fileURI) + XCTAssertEqual(location.range.lowerBound, project.positions["1️⃣"]) } } From 27cf04e2ed1c06081f8e769802afd6a095830095 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:10:47 +0530 Subject: [PATCH 03/12] Implement textDocument/typeDefinition request (#548) Add support for the textDocument/typeDefinition LSP request, which finds the type of the symbol at a given position and returns the location of that type's definition. This uses the same type definition lookup mechanism as the inlay hint resolution feature, which queries cursorInfo for the new type declaration location fields (typeDeclFilePath/Line/Column) with fallback to index lookup using typeDeclUsr. Fixes #548 --- Sources/SourceKitLSP/LanguageService.swift | 5 +++ Sources/SourceKitLSP/SourceKitLSPServer.swift | 15 ++++++-- .../InlayHintResolve.swift | 6 ++-- .../SwiftLanguageService/TypeDefinition.swift | 34 +++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 Sources/SwiftLanguageService/TypeDefinition.swift diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index be6f0634..e1a1b997 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -234,6 +234,7 @@ package protocol LanguageService: AnyObject, Sendable { func definition(_ request: DefinitionRequest) async throws -> LocationsOrLocationLinksResponse? func declaration(_ request: DeclarationRequest) async throws -> LocationsOrLocationLinksResponse? + func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? func foldingRange(_ req: FoldingRangeRequest) async throws -> [FoldingRange]? func documentSymbol(_ req: DocumentSymbolRequest) async throws -> DocumentSymbolResponse? @@ -428,6 +429,10 @@ package extension LanguageService { throw ResponseError.requestNotImplemented(DeclarationRequest.self) } + func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? { + throw ResponseError.requestNotImplemented(TypeDefinitionRequest.self) + } + func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? { throw ResponseError.requestNotImplemented(DocumentHighlightRequest.self) } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 3e992a00..df8900b6 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -790,6 +790,8 @@ extension SourceKitLSPServer: QueueBasedMessageHandler { await self.handleRequest(for: request, requestHandler: self.declaration) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.definition) + case let request as RequestAndReply: + await self.handleRequest(for: request, requestHandler: self.typeDefinition) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.doccDocumentation) case let request as RequestAndReply: @@ -1146,6 +1148,7 @@ extension SourceKitLSPServer { completionProvider: completionOptions, signatureHelpProvider: signatureHelpOptions, definitionProvider: .bool(true), + typeDefinitionProvider: .bool(true), implementationProvider: .bool(true), referencesProvider: .bool(true), documentHighlightProvider: .bool(true), @@ -1909,8 +1912,8 @@ extension SourceKitLSPServer { // inlay hints store the uri in data for resolution // extract uri from the lspany dictionary guard case .dictionary(let dict) = request.inlayHint.data, - case .string(let uriString) = dict["uri"], - let uri = try? DocumentURI(string: uriString) + case .string(let uriString) = dict["uri"], + let uri = try? DocumentURI(string: uriString) else { return request.inlayHint } @@ -2170,6 +2173,14 @@ extension SourceKitLSPServer { return .locations(remappedLocations) } + func typeDefinition( + _ req: TypeDefinitionRequest, + workspace: Workspace, + languageService: any LanguageService + ) async throws -> LocationsOrLocationLinksResponse? { + return try await languageService.typeDefinition(req) + } + /// Generate the generated interface for the given module, write it to disk and return the location to which to jump /// to get to the definition of `symbolUSR`. /// diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index c8b88958..8ccf8bad 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -63,8 +63,10 @@ extension SwiftLanguageService { return hint } - /// looks up the definition location for the type at the given position - private func lookupTypeDefinitionLocation( + /// Looks up the definition location for the type at the given position. + /// + /// This is used by both inlay hint resolution and the typeDefinition request. + func lookupTypeDefinitionLocation( uri: DocumentURI, position: Position ) async throws -> Location? { diff --git a/Sources/SwiftLanguageService/TypeDefinition.swift b/Sources/SwiftLanguageService/TypeDefinition.swift new file mode 100644 index 00000000..30956ebe --- /dev/null +++ b/Sources/SwiftLanguageService/TypeDefinition.swift @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2026 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 IndexStoreDB +@_spi(SourceKitLSP) package import LanguageServerProtocol +import SemanticIndex +import SourceKitD +import SourceKitLSP + +extension SwiftLanguageService { + /// Handles the textDocument/typeDefinition request. + /// + /// Given a source location, finds the type of the symbol at that position + /// and returns the location of that type's definition. + package func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? { + let uri = request.textDocument.uri + let position = request.position + + guard let location = try await lookupTypeDefinitionLocation(uri: uri, position: position) else { + return nil + } + + return .locations([location]) + } +} From 564a45b03c2bff4af3a468fb1a50a98cf32162aa Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Fri, 9 Jan 2026 23:46:58 +0530 Subject: [PATCH 04/12] Add go-to-definition for inlay type hints --- Sources/SourceKitD/sourcekitd_uids.swift | 16 ----- Sources/SwiftLanguageService/CursorInfo.swift | 44 ++++++++++++ .../InlayHintResolve.swift | 68 +++++++++++-------- Tests/SourceKitLSPTests/InlayHintTests.swift | 2 +- 4 files changed, 85 insertions(+), 45 deletions(-) diff --git a/Sources/SourceKitD/sourcekitd_uids.swift b/Sources/SourceKitD/sourcekitd_uids.swift index 37fc63e2..59683b86 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift +++ b/Sources/SourceKitD/sourcekitd_uids.swift @@ -12,7 +12,6 @@ // Automatically Generated From UIDs.swift.gyb. // Do Not Edit Directly! To regenerate run Utilities/generate-uids.py - package import Csourcekitd package struct sourcekitd_api_keys { @@ -232,16 +231,6 @@ package struct sourcekitd_api_keys { package let typeUsr: sourcekitd_api_uid_t /// `key.containertypeusr` package let containerTypeUsr: sourcekitd_api_uid_t - /// `key.typedecl_usr` - package let typeDeclUsr: sourcekitd_api_uid_t - /// `key.typedecl_filepath` - package let typeDeclFilePath: sourcekitd_api_uid_t - /// `key.typedecl_line` - package let typeDeclLine: sourcekitd_api_uid_t - /// `key.typedecl_column` - package let typeDeclColumn: sourcekitd_api_uid_t - /// `key.typedecl_modulename` - package let typeDeclModuleName: sourcekitd_api_uid_t /// `key.modulegroups` package let moduleGroups: sourcekitd_api_uid_t /// `key.basename` @@ -650,11 +639,6 @@ package struct sourcekitd_api_keys { removeCache = api.uid_get_from_cstr("key.removecache")! typeUsr = api.uid_get_from_cstr("key.typeusr")! containerTypeUsr = api.uid_get_from_cstr("key.containertypeusr")! - typeDeclUsr = api.uid_get_from_cstr("key.typedecl_usr")! - typeDeclFilePath = api.uid_get_from_cstr("key.typedecl_filepath")! - typeDeclLine = api.uid_get_from_cstr("key.typedecl_line")! - typeDeclColumn = api.uid_get_from_cstr("key.typedecl_column")! - typeDeclModuleName = api.uid_get_from_cstr("key.typedecl_modulename")! moduleGroups = api.uid_get_from_cstr("key.modulegroups")! baseName = api.uid_get_from_cstr("key.basename")! argNames = api.uid_get_from_cstr("key.argnames")! diff --git a/Sources/SwiftLanguageService/CursorInfo.swift b/Sources/SwiftLanguageService/CursorInfo.swift index dc6c2027..9575a434 100644 --- a/Sources/SwiftLanguageService/CursorInfo.swift +++ b/Sources/SwiftLanguageService/CursorInfo.swift @@ -207,4 +207,48 @@ extension SwiftLanguageService { additionalParameters: appendAdditionalParameters ) } + + /// converts a mangled type string to a USR format. + /// mangled types start with `$s` while USRs start with `s:`. + /// for instance `$sSS` becomes `s:SS` (for String type). + private func convertMangledTypeToUSR(_ mangledType: String) -> String { + if mangledType.hasPrefix("$s") { + return "s:" + mangledType.dropFirst(2) + } + // already in USR format or unknown format, return as-is + return mangledType + } + + /// get cursor info for a type by looking up its USR. + /// this takes a mangled type (from `key.typeusr`) and converts it to a proper USR + /// (by replacing `$s` prefix with `s:`), then queries cursorInfo with that USR. + /// + /// - parameters: + /// - mangledType: the mangled type string (e.g., `$sSS` for String) + /// - uri: document URI for context (used to get compile command) + /// - returns: cursorInfo for the type declaration, or nil if not found + func cursorInfoFromTypeUSR( + _ mangledType: String, + in uri: DocumentURI + ) async throws -> CursorInfo? { + let usr = convertMangledTypeToUSR(mangledType) + + let snapshot = try await self.latestSnapshot(for: uri) + let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: true) + let documentManager = try self.documentManager + + let keys = self.keys + + let skreq = sourcekitd.dictionary([ + keys.cancelOnSubsequentRequest: 0, + keys.usr: usr, + keys.sourceFile: snapshot.uri.sourcekitdSourceFile, + keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath, + keys.compilerArgs: compileCommand?.compilerArgs as [any SKDRequestValue]?, + ]) + + let dict = try await send(sourcekitdRequest: \.cursorInfo, skreq, snapshot: snapshot) + + return CursorInfo(dict, snapshot: snapshot, documentManager: documentManager, sourcekitd: sourcekitd) + } } diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index 8ccf8bad..df012ea4 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -66,14 +66,18 @@ extension SwiftLanguageService { /// Looks up the definition location for the type at the given position. /// /// This is used by both inlay hint resolution and the typeDefinition request. + /// It works by: + /// 1. Getting the type USR (mangled name) from cursorInfo at the position + /// 2. Converting the mangled type ($s prefix) to a proper USR (s: prefix) + /// 3. Looking up the type definition in the index or via cursorInfo func lookupTypeDefinitionLocation( uri: DocumentURI, position: Position ) async throws -> Location? { + // Step 1: Get type USR from cursor info at the position let snapshot = try await self.latestSnapshot(for: uri) let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: false) - // call cursor info at the variable position to get the type declaration location let skreq = sourcekitd.dictionary([ keys.cancelOnSubsequentRequest: 0, keys.offset: snapshot.utf8Offset(of: position), @@ -84,38 +88,46 @@ extension SwiftLanguageService { let dict = try await send(sourcekitdRequest: \.cursorInfo, skreq, snapshot: snapshot) - if let filepath: String = dict[keys.typeDeclFilePath], - let line: Int = dict[keys.typeDeclLine], - let column: Int = dict[keys.typeDeclColumn] + // Get the type USR (this is of a mangled type like "$sSS" for String) + guard let typeUsr: String = dict[keys.typeUsr] else { + return nil + } + + // step 2: Convert mangled type to proper USR + // The typeUsr is a mangled type like "$s4test6MyTypeVD" for struct MyType + // To get the declaration USR, we need to: + // 1. Replace "$s" prefix with "s:" + // 2. Strip the trailing "D" which is a mangling suffix (type descriptor) + var mangledName = typeUsr + if mangledName.hasPrefix("$s") { + mangledName = "s:" + mangledName.dropFirst(2) + } + // Strip trailing 'D' (type descriptor suffix in mangling) + if mangledName.hasSuffix("D") { + mangledName = String(mangledName.dropLast()) + } + let usr = mangledName + + // step 3: Try index lookup first (works well for local and external types) + if let workspace = await sourceKitLSPServer?.workspaceForDocument(uri: uri), + let index = await workspace.index(checkedFor: .deletedFiles), + let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) { - let definitionUri = DocumentURI(filePath: filepath, isDirectory: false) - let definitionPosition = Position(line: line - 1, utf16index: column - 1) + let definitionUri = DocumentURI(filePath: occurrence.location.path, isDirectory: false) + let definitionPosition = Position( + line: occurrence.location.line - 1, + utf16index: occurrence.location.utf8Column - 1 + ) return Location(uri: definitionUri, range: Range(definitionPosition)) } - // fallback: use the type declaration USR with index lookup - - guard let typeDeclUsr: String = dict[keys.typeDeclUsr] else { - return nil + // Fallback: Try cursorInfo with USR (for types not in index) + if let typeInfo = try await cursorInfoFromTypeUSR(typeUsr, in: uri), + let location = typeInfo.symbolInfo.bestLocalDeclaration + { + return location } - // look up the type definition in the index - guard let workspace = await sourceKitLSPServer?.workspaceForDocument(uri: uri), - let index = await workspace.index(checkedFor: .deletedFiles) - else { - return nil - } - - guard let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: typeDeclUsr) else { - return nil - } - - let definitionUri = DocumentURI(filePath: occurrence.location.path, isDirectory: false) - let definitionPosition = Position( - line: occurrence.location.line - 1, - utf16index: occurrence.location.utf8Column - 1 - ) - - return Location(uri: definitionUri, range: Range(definitionPosition)) + return nil } } diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index e969605d..c0da5f8b 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -289,7 +289,7 @@ final class InlayHintTests: SourceKitLSPTestCase { // test that resolving an inlay hint returns label parts with type location let project = try await IndexedSingleSwiftFileTestProject( """ - 1️⃣struct MyType {} + struct 1️⃣MyType {} let x2️⃣ = MyType() """ ) From 3630ab970cf4a9cf911ecc8361ea6fa432ac3030 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Sat, 10 Jan 2026 07:00:43 +0530 Subject: [PATCH 05/12] Address review feedback for inlay hint go-to-definition - Use cursorInfo USR lookup instead of index (more accurate) - Add document version tracking to reject stale resolve requests - Make InlayHintResolveData conform to LSPAnyCodable - Reference swiftlang/swift#86432 for mangled type workaround - cursorInfoFromTypeUSR takes DocumentSnapshot for version safety - Remove TypeDefinition.swift (defer to follow-up PR) - Remove unnecessary comments - Tests work without index --- Sources/SourceKitD/sourcekitd_uids.swift.gyb | 1 - Sources/SourceKitLSP/LanguageService.swift | 1 - Sources/SourceKitLSP/SourceKitLSPServer.swift | 2 - Sources/SwiftLanguageService/CursorInfo.swift | 37 +++++----- .../InlayHintResolve.swift | 70 ++++--------------- Sources/SwiftLanguageService/InlayHints.swift | 55 +++++++++++---- .../SwiftLanguageService/TypeDefinition.swift | 34 --------- Tests/SourceKitLSPTests/InlayHintTests.swift | 46 +++++++++--- 8 files changed, 110 insertions(+), 136 deletions(-) delete mode 100644 Sources/SwiftLanguageService/TypeDefinition.swift diff --git a/Sources/SourceKitD/sourcekitd_uids.swift.gyb b/Sources/SourceKitD/sourcekitd_uids.swift.gyb index 9497f587..860945ae 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift.gyb +++ b/Sources/SourceKitD/sourcekitd_uids.swift.gyb @@ -111,7 +111,6 @@ # Maintained from initializeService in Requests.cpp KIND('SemaEnabledNotification', 'source.notification.sema_enabled'), KIND('DocumentUpdateNotification', 'source.notification.editor.documentupdate'), - # Note: DiagRemark was moved to UID_KINDS in UIDs.py ] TYPES_AND_KEYS = [ diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index e1a1b997..b32c1a18 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -478,7 +478,6 @@ package extension LanguageService { } func inlayHintResolve(_ req: InlayHintResolveRequest) async throws -> InlayHint { - // default: return hint unchanged return req.inlayHint } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index df8900b6..8758f49b 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -1909,8 +1909,6 @@ extension SourceKitLSPServer { func inlayHintResolve( request: InlayHintResolveRequest ) async throws -> InlayHint { - // inlay hints store the uri in data for resolution - // extract uri from the lspany dictionary guard case .dictionary(let dict) = request.inlayHint.data, case .string(let uriString) = dict["uri"], let uri = try? DocumentURI(string: uriString) diff --git a/Sources/SwiftLanguageService/CursorInfo.swift b/Sources/SwiftLanguageService/CursorInfo.swift index 9575a434..f75304e5 100644 --- a/Sources/SwiftLanguageService/CursorInfo.swift +++ b/Sources/SwiftLanguageService/CursorInfo.swift @@ -208,33 +208,36 @@ extension SwiftLanguageService { ) } - /// converts a mangled type string to a USR format. - /// mangled types start with `$s` while USRs start with `s:`. - /// for instance `$sSS` becomes `s:SS` (for String type). + /// Because of https://github.com/swiftlang/swift/issues/86432 sourcekitd returns a mangled name instead of a USR + /// as the type USR. Work around this by replacing mangled names (starting with `$s`) to a USR, starting with `s:`. + /// We also strip the trailing `D` suffix which represents a type mangling - this may not work correctly for generic + /// types with type arguments. + // TODO: Remove once https://github.com/swiftlang/swift/issues/86432 is fixed private func convertMangledTypeToUSR(_ mangledType: String) -> String { - if mangledType.hasPrefix("$s") { - return "s:" + mangledType.dropFirst(2) + var result = mangledType + if result.hasPrefix("$s") { + result = "s:" + result.dropFirst(2) } - // already in USR format or unknown format, return as-is - return mangledType + // Strip trailing 'D' (type mangling suffix) to get declaration USR + if result.hasSuffix("D") { + result = String(result.dropLast()) + } + return result } - /// get cursor info for a type by looking up its USR. - /// this takes a mangled type (from `key.typeusr`) and converts it to a proper USR - /// (by replacing `$s` prefix with `s:`), then queries cursorInfo with that USR. + /// Get cursor info for a type by looking up its USR. /// - /// - parameters: - /// - mangledType: the mangled type string (e.g., `$sSS` for String) - /// - uri: document URI for context (used to get compile command) - /// - returns: cursorInfo for the type declaration, or nil if not found + /// - Parameters: + /// - mangledType: The mangled name of the type + /// - snapshot: Document snapshot for context (used to get compile command) + /// - Returns: CursorInfo for the type declaration, or `nil` if not found func cursorInfoFromTypeUSR( _ mangledType: String, - in uri: DocumentURI + in snapshot: DocumentSnapshot ) async throws -> CursorInfo? { let usr = convertMangledTypeToUSR(mangledType) - let snapshot = try await self.latestSnapshot(for: uri) - let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: true) + let compileCommand = await self.compileCommand(for: snapshot.uri, fallbackAfterTimeout: true) let documentManager = try self.documentManager let keys = self.keys diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index df012ea4..2f87b754 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -18,35 +18,31 @@ import SourceKitD import SourceKitLSP extension SwiftLanguageService { - /// resolves an inlay hint by looking up the type definition location + /// Resolves an inlay hint by looking up the type definition location. package func inlayHintResolve(_ req: InlayHintResolveRequest) async throws -> InlayHint { - var hint = req.inlayHint + let hint = req.inlayHint - // only resolve type hints that have stored data - // extract uri and position from the lspany dictionary guard hint.kind == .type, - case .dictionary(let dict) = hint.data, - case .string(let uriString) = dict["uri"], - let uri = try? DocumentURI(string: uriString), - case .dictionary(let posDict) = dict["position"], - case .int(let line) = posDict["line"], - case .int(let character) = posDict["character"] + let resolveData = InlayHintResolveData(fromLSPAny: hint.data) else { return hint } - let position = Position(line: line, utf16index: character) - // get the type usr by calling cursor info at the variable position + // Fail if document version has changed since the hint was created + let currentSnapshot = try await self.latestSnapshot(for: resolveData.uri) + guard currentSnapshot.version == resolveData.version else { + return hint + } + let typeLocation = try await lookupTypeDefinitionLocation( - uri: uri, - position: position + snapshot: currentSnapshot, + position: resolveData.position ) guard let typeLocation else { return hint } - // return new hint with label parts that have location for go-to-definition if case .string(let labelText) = hint.label { return InlayHint( position: hint.position, @@ -65,18 +61,12 @@ extension SwiftLanguageService { /// Looks up the definition location for the type at the given position. /// - /// This is used by both inlay hint resolution and the typeDefinition request. - /// It works by: - /// 1. Getting the type USR (mangled name) from cursorInfo at the position - /// 2. Converting the mangled type ($s prefix) to a proper USR (s: prefix) - /// 3. Looking up the type definition in the index or via cursorInfo + /// This is used by inlay hint resolution to enable go-to-definition on type hints. func lookupTypeDefinitionLocation( - uri: DocumentURI, + snapshot: DocumentSnapshot, position: Position ) async throws -> Location? { - // Step 1: Get type USR from cursor info at the position - let snapshot = try await self.latestSnapshot(for: uri) - let compileCommand = await self.compileCommand(for: uri, fallbackAfterTimeout: false) + let compileCommand = await self.compileCommand(for: snapshot.uri, fallbackAfterTimeout: false) let skreq = sourcekitd.dictionary([ keys.cancelOnSubsequentRequest: 0, @@ -88,41 +78,11 @@ extension SwiftLanguageService { let dict = try await send(sourcekitdRequest: \.cursorInfo, skreq, snapshot: snapshot) - // Get the type USR (this is of a mangled type like "$sSS" for String) guard let typeUsr: String = dict[keys.typeUsr] else { return nil } - // step 2: Convert mangled type to proper USR - // The typeUsr is a mangled type like "$s4test6MyTypeVD" for struct MyType - // To get the declaration USR, we need to: - // 1. Replace "$s" prefix with "s:" - // 2. Strip the trailing "D" which is a mangling suffix (type descriptor) - var mangledName = typeUsr - if mangledName.hasPrefix("$s") { - mangledName = "s:" + mangledName.dropFirst(2) - } - // Strip trailing 'D' (type descriptor suffix in mangling) - if mangledName.hasSuffix("D") { - mangledName = String(mangledName.dropLast()) - } - let usr = mangledName - - // step 3: Try index lookup first (works well for local and external types) - if let workspace = await sourceKitLSPServer?.workspaceForDocument(uri: uri), - let index = await workspace.index(checkedFor: .deletedFiles), - let occurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) - { - let definitionUri = DocumentURI(filePath: occurrence.location.path, isDirectory: false) - let definitionPosition = Position( - line: occurrence.location.line - 1, - utf16index: occurrence.location.utf8Column - 1 - ) - return Location(uri: definitionUri, range: Range(definitionPosition)) - } - - // Fallback: Try cursorInfo with USR (for types not in index) - if let typeInfo = try await cursorInfoFromTypeUSR(typeUsr, in: uri), + if let typeInfo = try await cursorInfoFromTypeUSR(typeUsr, in: snapshot), let location = typeInfo.symbolInfo.bestLocalDeclaration { return location diff --git a/Sources/SwiftLanguageService/InlayHints.swift b/Sources/SwiftLanguageService/InlayHints.swift index a6c5f9d3..062b68a7 100644 --- a/Sources/SwiftLanguageService/InlayHints.swift +++ b/Sources/SwiftLanguageService/InlayHints.swift @@ -10,18 +10,48 @@ // //===----------------------------------------------------------------------===// -@_spi(SourceKitLSP) package import LanguageServerProtocol import Foundation +@_spi(SourceKitLSP) package import LanguageServerProtocol import SourceKitLSP import SwiftExtensions import SwiftSyntax - -package struct InlayHintResolveData: Codable { - /// the document uri containing the variable +package struct InlayHintResolveData: LSPAnyCodable { package let uri: DocumentURI - package let position: Position + package let version: Int + + package init(uri: DocumentURI, position: Position, version: Int) { + self.uri = uri + self.position = position + self.version = version + } + + package init?(fromLSPDictionary dictionary: [String: LSPAny]) { + guard case .string(let uriString) = dictionary["uri"], + let uri = try? DocumentURI(string: uriString), + case .dictionary(let posDict) = dictionary["position"], + case .int(let line) = posDict["line"], + case .int(let character) = posDict["character"], + case .int(let version) = dictionary["version"] + else { + return nil + } + self.uri = uri + self.position = Position(line: line, utf16index: character) + self.version = version + } + + package func encodeToLSPAny() -> LSPAny { + return .dictionary([ + "uri": .string(uri.stringValue), + "position": .dictionary([ + "line": .int(position.line), + "character": .int(position.utf16index), + ]), + "version": .int(version), + ]) + } } private class IfConfigCollector: SyntaxVisitor { @@ -43,6 +73,9 @@ private class IfConfigCollector: SyntaxVisitor { extension SwiftLanguageService { package func inlayHint(_ req: InlayHintRequest) async throws -> [InlayHint] { let uri = req.textDocument.uri + let snapshot = try await self.latestSnapshot(for: uri) + let version = snapshot.version + let infos = try await variableTypeInfos(uri, req.range) let typeHints = infos .lazy @@ -57,24 +90,16 @@ extension SwiftLanguageService { } else { textEdits = nil } - // store resolve data so we can look up type definition later - let data: LSPAny = .dictionary([ - "uri": .string(uri.stringValue), - "position": .dictionary([ - "line": .int(variableStart.line), - "character": .int(variableStart.utf16index) - ]) - ]) + let resolveData = InlayHintResolveData(uri: uri, position: variableStart, version: version) return InlayHint( position: position, label: .string(label), kind: .type, textEdits: textEdits, - data: data + data: resolveData.encodeToLSPAny() ) } - let snapshot = try await self.latestSnapshot(for: uri) let syntaxTree = await syntaxTreeManager.syntaxTree(for: snapshot) let ifConfigDecls = IfConfigCollector.collectIfConfigDecls(in: syntaxTree) let ifConfigHints = ifConfigDecls.compactMap { (ifConfigDecl) -> InlayHint? in diff --git a/Sources/SwiftLanguageService/TypeDefinition.swift b/Sources/SwiftLanguageService/TypeDefinition.swift deleted file mode 100644 index 30956ebe..00000000 --- a/Sources/SwiftLanguageService/TypeDefinition.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2026 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 IndexStoreDB -@_spi(SourceKitLSP) package import LanguageServerProtocol -import SemanticIndex -import SourceKitD -import SourceKitLSP - -extension SwiftLanguageService { - /// Handles the textDocument/typeDefinition request. - /// - /// Given a source location, finds the type of the symbol at that position - /// and returns the location of that type's definition. - package func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? { - let uri = request.textDocument.uri - let position = request.position - - guard let location = try await lookupTypeDefinitionLocation(uri: uri, position: position) else { - return nil - } - - return .locations([location]) - } -} diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index c0da5f8b..47a803be 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -286,19 +286,20 @@ final class InlayHintTests: SourceKitLSPTestCase { } func testInlayHintResolve() async throws { - // test that resolving an inlay hint returns label parts with type location - let project = try await IndexedSingleSwiftFileTestProject( + let testClient = try await TestSourceKitLSPClient() + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( """ struct 1️⃣MyType {} let x2️⃣ = MyType() - """ + """, + uri: uri ) - // get inlay hints - let request = InlayHintRequest(textDocument: TextDocumentIdentifier(project.fileURI), range: nil) - let hints = try await project.testClient.send(request) + let request = InlayHintRequest(textDocument: TextDocumentIdentifier(uri), range: nil) + let hints = try await testClient.send(request) - // find the type hint for x guard let typeHint = hints.first(where: { $0.kind == .type }) else { XCTFail("Expected type hint") return @@ -306,8 +307,7 @@ final class InlayHintTests: SourceKitLSPTestCase { XCTAssertNotNil(typeHint.data, "Expected type hint to have data for resolution") - // resolve the hint to get type location - let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) + let resolvedHint = try await testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) guard case .parts(let parts) = resolvedHint.label else { XCTFail("Expected resolved hint to have label parts, got: \(resolvedHint.label)") @@ -321,7 +321,31 @@ final class InlayHintTests: SourceKitLSPTestCase { return } - XCTAssertEqual(location.uri, project.fileURI) - XCTAssertEqual(location.range.lowerBound, project.positions["1️⃣"]) + XCTAssertEqual(location.uri, uri) + XCTAssertEqual(location.range.lowerBound, positions["1️⃣"]) + } + + func testInlayHintResolveSDKType() async throws { + let testClient = try await TestSourceKitLSPClient() + let uri = DocumentURI(for: .swift) + + testClient.openDocument( + """ + let x = "hello" + """, + uri: uri + ) + + let request = InlayHintRequest(textDocument: TextDocumentIdentifier(uri), range: nil) + let hints = try await testClient.send(request) + + guard let typeHint = hints.first(where: { $0.kind == .type }) else { + XCTFail("Expected type hint for String") + return + } + + // Resolve should not crash, and returns the hint (possibly without location for SDK types in test env) + let resolvedHint = try await testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) + XCTAssertEqual(resolvedHint.kind, .type) } } From 9e12af1f45083226b62fcc1dee832ecaa005c7ae Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Sat, 10 Jan 2026 07:05:01 +0530 Subject: [PATCH 06/12] Add cross-module inlay hint resolve test --- Tests/SourceKitLSPTests/InlayHintTests.swift | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 47a803be..12d90253 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -348,4 +348,54 @@ final class InlayHintTests: SourceKitLSPTestCase { let resolvedHint = try await testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) XCTAssertEqual(resolvedHint.kind, .type) } + + func testInlayHintResolveCrossModule() async throws { + let project = try await SwiftPMTestProject( + files: [ + "LibA/MyType.swift": """ + public struct 1️⃣MyType { + public init() {} + } + """, + "LibB/UseType.swift": """ + import LibA + let x2️⃣ = MyType() + """, + ], + manifest: """ + let package = Package( + name: "MyLibrary", + targets: [ + .target(name: "LibA"), + .target(name: "LibB", dependencies: ["LibA"]), + ] + ) + """, + enableBackgroundIndexing: true + ) + + let (uri, positions) = try project.openDocument("UseType.swift") + + let request = InlayHintRequest(textDocument: TextDocumentIdentifier(uri), range: nil) + let hints = try await project.testClient.send(request) + + guard let typeHint = hints.first(where: { $0.kind == .type }) else { + XCTFail("Expected type hint for MyType") + return + } + + let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) + + guard case .parts(let parts) = resolvedHint.label, + let part = parts.first, + let location = part.location + else { + XCTFail("Expected label part to have location for go-to-definition") + return + } + + // The location should point to LibA/MyType.swift where MyType is defined + XCTAssertEqual(location.uri, try project.uri(for: "MyType.swift")) + XCTAssertEqual(location.range.lowerBound, try project.position(of: "1️⃣", in: "MyType.swift")) + } } From 81610b366597753d22e8f034bcdf9e749fa824fa Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:52:41 +0530 Subject: [PATCH 07/12] Simplify InlayHintResolveData using Position LSPAnyCodable --- Sources/SwiftLanguageService/InlayHints.swift | 13 +++---- Tests/SourceKitLSPTests/InlayHintTests.swift | 36 +++---------------- 2 files changed, 9 insertions(+), 40 deletions(-) diff --git a/Sources/SwiftLanguageService/InlayHints.swift b/Sources/SwiftLanguageService/InlayHints.swift index 062b68a7..8321c3cf 100644 --- a/Sources/SwiftLanguageService/InlayHints.swift +++ b/Sources/SwiftLanguageService/InlayHints.swift @@ -30,25 +30,20 @@ package struct InlayHintResolveData: LSPAnyCodable { package init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .string(let uriString) = dictionary["uri"], let uri = try? DocumentURI(string: uriString), - case .dictionary(let posDict) = dictionary["position"], - case .int(let line) = posDict["line"], - case .int(let character) = posDict["character"], - case .int(let version) = dictionary["version"] + case .int(let version) = dictionary["version"], + let position = Position(fromLSPAny: dictionary["position"]) else { return nil } self.uri = uri - self.position = Position(line: line, utf16index: character) + self.position = position self.version = version } package func encodeToLSPAny() -> LSPAny { return .dictionary([ "uri": .string(uri.stringValue), - "position": .dictionary([ - "line": .int(position.line), - "character": .int(position.utf16index), - ]), + "position": position.encodeToLSPAny(), "version": .int(version), ]) } diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 12d90253..9081c2fe 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -14,6 +14,7 @@ import SKLogging import SKTestSupport import SourceKitLSP +import SwiftExtensions import XCTest final class InlayHintTests: SourceKitLSPTestCase { @@ -314,39 +315,13 @@ final class InlayHintTests: SourceKitLSPTestCase { return } - XCTAssertEqual(parts.count, 1, "Expected exactly one label part") - - guard let location = parts.first?.location else { + guard let location = parts.only?.location else { XCTFail("Expected label part to have location for go-to-definition") return } XCTAssertEqual(location.uri, uri) - XCTAssertEqual(location.range.lowerBound, positions["1️⃣"]) - } - - func testInlayHintResolveSDKType() async throws { - let testClient = try await TestSourceKitLSPClient() - let uri = DocumentURI(for: .swift) - - testClient.openDocument( - """ - let x = "hello" - """, - uri: uri - ) - - let request = InlayHintRequest(textDocument: TextDocumentIdentifier(uri), range: nil) - let hints = try await testClient.send(request) - - guard let typeHint = hints.first(where: { $0.kind == .type }) else { - XCTFail("Expected type hint for String") - return - } - - // Resolve should not crash, and returns the hint (possibly without location for SDK types in test env) - let resolvedHint = try await testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) - XCTAssertEqual(resolvedHint.kind, .type) + XCTAssertEqual(location.range, Range(positions["1️⃣"])) } func testInlayHintResolveCrossModule() async throws { @@ -387,8 +362,7 @@ final class InlayHintTests: SourceKitLSPTestCase { let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) guard case .parts(let parts) = resolvedHint.label, - let part = parts.first, - let location = part.location + let location = parts.only?.location else { XCTFail("Expected label part to have location for go-to-definition") return @@ -396,6 +370,6 @@ final class InlayHintTests: SourceKitLSPTestCase { // The location should point to LibA/MyType.swift where MyType is defined XCTAssertEqual(location.uri, try project.uri(for: "MyType.swift")) - XCTAssertEqual(location.range.lowerBound, try project.position(of: "1️⃣", in: "MyType.swift")) + XCTAssertEqual(location.range, try Range(project.position(of: "1️⃣", in: "MyType.swift"))) } } From 22e7ac048f9765210ae6375903eaaa6fc9ec9221 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:04:39 +0530 Subject: [PATCH 08/12] Add generated interface support for SDK types in inlay hints --- .../InlayHintResolve.swift | 27 +++++++++++++-- Tests/SourceKitLSPTests/InlayHintTests.swift | 33 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index 2f87b754..e5757429 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -62,6 +62,7 @@ extension SwiftLanguageService { /// Looks up the definition location for the type at the given position. /// /// This is used by inlay hint resolution to enable go-to-definition on type hints. + /// For SDK types, this returns a location in the generated interface. func lookupTypeDefinitionLocation( snapshot: DocumentSnapshot, position: Position @@ -82,12 +83,32 @@ extension SwiftLanguageService { return nil } - if let typeInfo = try await cursorInfoFromTypeUSR(typeUsr, in: snapshot), - let location = typeInfo.symbolInfo.bestLocalDeclaration - { + guard let typeInfo = try await cursorInfoFromTypeUSR(typeUsr, in: snapshot) else { + return nil + } + + // For local types, return the local declaration + if let location = typeInfo.symbolInfo.bestLocalDeclaration { return location } + // For SDK types, fall back to generated interface + if typeInfo.symbolInfo.isSystem ?? false, + let systemModule = typeInfo.symbolInfo.systemModule + { + let interfaceDetails = try await self.openGeneratedInterface( + document: snapshot.uri, + moduleName: systemModule.moduleName, + groupName: systemModule.groupName, + symbolUSR: typeInfo.symbolInfo.usr + ) + if let details = interfaceDetails { + let position = details.position ?? Position(line: 0, utf16index: 0) + return Location(uri: details.uri, range: Range(position)) + } + } + return nil } } + diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 9081c2fe..7c1f0628 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -372,4 +372,37 @@ final class InlayHintTests: SourceKitLSPTestCase { XCTAssertEqual(location.uri, try project.uri(for: "MyType.swift")) XCTAssertEqual(location.range, try Range(project.position(of: "1️⃣", in: "MyType.swift"))) } + + func testInlayHintResolveSDKType() async throws { + let project = try await IndexedSingleSwiftFileTestProject( + """ + let 1️⃣x = "hello" + """, + indexSystemModules: true + ) + + let request = InlayHintRequest(textDocument: TextDocumentIdentifier(project.fileURI), range: nil) + let hints = try await project.testClient.send(request) + + guard let typeHint = hints.first(where: { $0.kind == .type }) else { + XCTFail("Expected type hint for String") + return + } + + let resolvedHint = try await project.testClient.send(InlayHintResolveRequest(inlayHint: typeHint)) + + guard case .parts(let parts) = resolvedHint.label, + let location = parts.only?.location + else { + XCTFail("Expected label part to have location for go-to-definition") + return + } + + // Should point to generated Swift interface + XCTAssertTrue( + location.uri.pseudoPath.hasSuffix(".swiftinterface"), + "Expected .swiftinterface file, got: \(location.uri.pseudoPath)" + ) + } } + From 0695d3c67c360d46abce36c58fc7802c47498590 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Mon, 12 Jan 2026 06:01:55 +0530 Subject: [PATCH 09/12] Remove typeDefinition remnants --- Sources/SourceKitLSP/LanguageService.swift | 5 ----- Sources/SourceKitLSP/SourceKitLSPServer.swift | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index b32c1a18..555aef88 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -234,7 +234,6 @@ package protocol LanguageService: AnyObject, Sendable { func definition(_ request: DefinitionRequest) async throws -> LocationsOrLocationLinksResponse? func declaration(_ request: DeclarationRequest) async throws -> LocationsOrLocationLinksResponse? - func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? func foldingRange(_ req: FoldingRangeRequest) async throws -> [FoldingRange]? func documentSymbol(_ req: DocumentSymbolRequest) async throws -> DocumentSymbolResponse? @@ -429,10 +428,6 @@ package extension LanguageService { throw ResponseError.requestNotImplemented(DeclarationRequest.self) } - func typeDefinition(_ request: TypeDefinitionRequest) async throws -> LocationsOrLocationLinksResponse? { - throw ResponseError.requestNotImplemented(TypeDefinitionRequest.self) - } - func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? { throw ResponseError.requestNotImplemented(DocumentHighlightRequest.self) } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 8758f49b..fc18d309 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -790,8 +790,6 @@ extension SourceKitLSPServer: QueueBasedMessageHandler { await self.handleRequest(for: request, requestHandler: self.declaration) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.definition) - case let request as RequestAndReply: - await self.handleRequest(for: request, requestHandler: self.typeDefinition) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.doccDocumentation) case let request as RequestAndReply: @@ -1148,7 +1146,6 @@ extension SourceKitLSPServer { completionProvider: completionOptions, signatureHelpProvider: signatureHelpOptions, definitionProvider: .bool(true), - typeDefinitionProvider: .bool(true), implementationProvider: .bool(true), referencesProvider: .bool(true), documentHighlightProvider: .bool(true), @@ -2171,14 +2168,6 @@ extension SourceKitLSPServer { return .locations(remappedLocations) } - func typeDefinition( - _ req: TypeDefinitionRequest, - workspace: Workspace, - languageService: any LanguageService - ) async throws -> LocationsOrLocationLinksResponse? { - return try await languageService.typeDefinition(req) - } - /// Generate the generated interface for the given module, write it to disk and return the location to which to jump /// to get to the definition of `symbolUSR`. /// From ceaf87b6df766bb55851d69e044599d1cb6c1a4f Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Mon, 12 Jan 2026 06:15:38 +0530 Subject: [PATCH 10/12] Run swift-format --- Sources/SwiftLanguageService/InlayHintResolve.swift | 1 - Tests/SourceKitLSPTests/InlayHintTests.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Sources/SwiftLanguageService/InlayHintResolve.swift b/Sources/SwiftLanguageService/InlayHintResolve.swift index e5757429..7da9747e 100644 --- a/Sources/SwiftLanguageService/InlayHintResolve.swift +++ b/Sources/SwiftLanguageService/InlayHintResolve.swift @@ -111,4 +111,3 @@ extension SwiftLanguageService { return nil } } - diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 7c1f0628..e2dea7c2 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -405,4 +405,3 @@ final class InlayHintTests: SourceKitLSPTestCase { ) } } - From 0b02672920a0b08e34ec9c3335336da2eb200748 Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:02:28 +0530 Subject: [PATCH 11/12] Restore sourcekitd_uids from main --- Sources/SourceKitD/sourcekitd_uids.swift | 24 ++++++++------------ Sources/SourceKitD/sourcekitd_uids.swift.gyb | 3 +++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Sources/SourceKitD/sourcekitd_uids.swift b/Sources/SourceKitD/sourcekitd_uids.swift index 59683b86..09a73cdb 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift +++ b/Sources/SourceKitD/sourcekitd_uids.swift @@ -14,6 +14,8 @@ package import Csourcekitd +// swift-format-ignore: TypeNamesShouldBeCapitalized +// Matching C style types package struct sourcekitd_api_keys { /// `key.version_major` package let versionMajor: sourcekitd_api_uid_t @@ -791,6 +793,8 @@ package struct sourcekitd_api_keys { } } +// swift-format-ignore: TypeNamesShouldBeCapitalized +// Matching C style types package struct sourcekitd_api_requests { /// `source.request.protocol_version` package let protocolVersion: sourcekitd_api_uid_t @@ -965,6 +969,8 @@ package struct sourcekitd_api_requests { } } +// swift-format-ignore: TypeNamesShouldBeCapitalized +// Matching C style types package struct sourcekitd_api_values { /// `source.lang.swift.decl.function.free` package let declFunctionFree: sourcekitd_api_uid_t @@ -1018,14 +1024,6 @@ package struct sourcekitd_api_values { package let declAccessorInit: sourcekitd_api_uid_t /// `source.lang.swift.ref.function.accessor.init` package let refAccessorInit: sourcekitd_api_uid_t - /// `source.lang.swift.decl.function.accessor.mutate` - package let declAccessorMutate: sourcekitd_api_uid_t - /// `source.lang.swift.ref.function.accessor.mutate` - package let refAccessorMutate: sourcekitd_api_uid_t - /// `source.lang.swift.decl.function.accessor.borrow` - package let declAccessorBorrow: sourcekitd_api_uid_t - /// `source.lang.swift.ref.function.accessor.borrow` - package let refAccessorBorrow: sourcekitd_api_uid_t /// `source.lang.swift.decl.function.constructor` package let declConstructor: sourcekitd_api_uid_t /// `source.lang.swift.ref.function.constructor` @@ -1250,8 +1248,6 @@ package struct sourcekitd_api_values { package let diagWarning: sourcekitd_api_uid_t /// `source.diagnostic.severity.error` package let diagError: sourcekitd_api_uid_t - /// `source.diagnostic.severity.remark` - package let diagRemark: sourcekitd_api_uid_t /// `source.diagnostic.category.deprecation` package let diagDeprecation: sourcekitd_api_uid_t /// `source.diagnostic.category.no_usage` @@ -1356,6 +1352,8 @@ package struct sourcekitd_api_values { package let semaEnabledNotification: sourcekitd_api_uid_t /// `source.notification.editor.documentupdate` package let documentUpdateNotification: sourcekitd_api_uid_t + /// `source.diagnostic.severity.remark` + package let diagRemark: sourcekitd_api_uid_t package init(api: sourcekitd_api_functions_t) { declFunctionFree = api.uid_get_from_cstr("source.lang.swift.decl.function.free")! @@ -1384,10 +1382,6 @@ package struct sourcekitd_api_values { refAccessorModify = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.modify")! declAccessorInit = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.init")! refAccessorInit = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.init")! - declAccessorMutate = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.mutate")! - refAccessorMutate = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.mutate")! - declAccessorBorrow = api.uid_get_from_cstr("source.lang.swift.decl.function.accessor.borrow")! - refAccessorBorrow = api.uid_get_from_cstr("source.lang.swift.ref.function.accessor.borrow")! declConstructor = api.uid_get_from_cstr("source.lang.swift.decl.function.constructor")! refConstructor = api.uid_get_from_cstr("source.lang.swift.ref.function.constructor")! declDestructor = api.uid_get_from_cstr("source.lang.swift.decl.function.destructor")! @@ -1500,7 +1494,6 @@ package struct sourcekitd_api_values { diagNote = api.uid_get_from_cstr("source.diagnostic.severity.note")! diagWarning = api.uid_get_from_cstr("source.diagnostic.severity.warning")! diagError = api.uid_get_from_cstr("source.diagnostic.severity.error")! - diagRemark = api.uid_get_from_cstr("source.diagnostic.severity.remark")! diagDeprecation = api.uid_get_from_cstr("source.diagnostic.category.deprecation")! diagNoUsage = api.uid_get_from_cstr("source.diagnostic.category.no_usage")! codeCompletionEverything = api.uid_get_from_cstr("source.codecompletion.everything")! @@ -1553,5 +1546,6 @@ package struct sourcekitd_api_values { semaDisabledNotification = api.uid_get_from_cstr("source.notification.sema_disabled")! semaEnabledNotification = api.uid_get_from_cstr("source.notification.sema_enabled")! documentUpdateNotification = api.uid_get_from_cstr("source.notification.editor.documentupdate")! + diagRemark = api.uid_get_from_cstr("source.diagnostic.severity.remark")! } } diff --git a/Sources/SourceKitD/sourcekitd_uids.swift.gyb b/Sources/SourceKitD/sourcekitd_uids.swift.gyb index 860945ae..da1f4186 100644 --- a/Sources/SourceKitD/sourcekitd_uids.swift.gyb +++ b/Sources/SourceKitD/sourcekitd_uids.swift.gyb @@ -111,6 +111,9 @@ # Maintained from initializeService in Requests.cpp KIND('SemaEnabledNotification', 'source.notification.sema_enabled'), KIND('DocumentUpdateNotification', 'source.notification.editor.documentupdate'), + + # Used exclusively within the SourceKit Plugin + KIND('DiagRemark', 'source.diagnostic.severity.remark'), ] TYPES_AND_KEYS = [ From 45b1598b9ad047e2d5ca474bf9b0182f8905a17f Mon Sep 17 00:00:00 2001 From: loveucifer <134506987+loveucifer@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:36:05 +0530 Subject: [PATCH 12/12] Add InlayHintResolve.swift to CMakeLists.txt --- Sources/SwiftLanguageService/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SwiftLanguageService/CMakeLists.txt b/Sources/SwiftLanguageService/CMakeLists.txt index 65408ac7..95dc501c 100644 --- a/Sources/SwiftLanguageService/CMakeLists.txt +++ b/Sources/SwiftLanguageService/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(SwiftLanguageService STATIC FoldingRange.swift GeneratedInterfaceManager.swift InlayHints.swift + InlayHintResolve.swift MacroExpansion.swift OpenInterface.swift PlaygroundDiscovery.swift