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)" + ) + } } +