Skip to content

Commit c1a8301

Browse files
authored
Merge pull request #2233 from aelam/fix/group_of_module_definition
Fix module definition jumping in Swift interface files
2 parents 1ca9780 + a2a3388 commit c1a8301

File tree

5 files changed

+94
-11
lines changed

5 files changed

+94
-11
lines changed

Sources/SourceKitLSP/GeneratedInterfaceDocumentURLData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ package struct GeneratedInterfaceDocumentURLData: Hashable, ReferenceURLData {
6767
self.moduleName = moduleName
6868
self.groupName = groupName
6969
self.sourcekitdDocumentName = sourcekitdDocumentName
70-
self.buildSettingsFrom = primaryFile
70+
self.buildSettingsFrom = primaryFile.buildSettingsFile
7171
}
7272

7373
init(queryItems: [URLQueryItem]) throws {

Sources/SourceKitLSP/ReferenceDocumentURL.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,4 @@ extension DocumentURI {
170170

171171
package struct ReferenceDocumentURLError: Error, CustomStringConvertible {
172172
package var description: String
173-
174-
init(description: String) {
175-
self.description = description
176-
}
177173
}

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,10 +1923,24 @@ extension SourceKitLSPServer {
19231923
languageService: LanguageService
19241924
) async throws -> [Location] {
19251925
// If this symbol is a module then generate a textual interface
1926-
if symbol.kind == .module, let name = symbol.name {
1926+
if symbol.kind == .module {
1927+
// For module symbols, prefer using systemModule information if available
1928+
let moduleName: String
1929+
let groupName: String?
1930+
1931+
if let systemModule = symbol.systemModule {
1932+
moduleName = systemModule.moduleName
1933+
groupName = systemModule.groupName
1934+
} else if let name = symbol.name {
1935+
moduleName = name
1936+
groupName = nil
1937+
} else {
1938+
return []
1939+
}
1940+
19271941
let interfaceLocation = try await self.definitionInInterface(
1928-
moduleName: name,
1929-
groupName: nil,
1942+
moduleName: moduleName,
1943+
groupName: groupName,
19301944
symbolUSR: nil,
19311945
originatorUri: uri,
19321946
languageService: languageService
@@ -2124,9 +2138,12 @@ extension SourceKitLSPServer {
21242138
originatorUri: DocumentURI,
21252139
languageService: LanguageService
21262140
) async throws -> Location {
2141+
// Let openGeneratedInterface handle all the logic, including checking if we're already in the right interface
2142+
let documentForBuildSettings = originatorUri.buildSettingsFile
2143+
21272144
guard
21282145
let interfaceDetails = try await languageService.openGeneratedInterface(
2129-
document: originatorUri,
2146+
document: documentForBuildSettings,
21302147
moduleName: moduleName,
21312148
groupName: groupName,
21322149
symbolUSR: symbolUSR

Sources/SwiftLanguageService/OpenInterface.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,15 @@ extension SwiftLanguageService {
2222
groupName: String?,
2323
symbolUSR symbol: String?
2424
) async throws -> GeneratedInterfaceDetails? {
25+
// Include build settings context to distinguish different versions/configurations
26+
let buildSettingsFileHash = "\(abs(document.buildSettingsFile.stringValue.hashValue))"
27+
let sourcekitdDocumentName = [moduleName, groupName, buildSettingsFileHash].compactMap(\.self)
28+
.joined(separator: ".")
29+
2530
let urlData = GeneratedInterfaceDocumentURLData(
2631
moduleName: moduleName,
2732
groupName: groupName,
28-
sourcekitdDocumentName: "\(moduleName)-\(UUID())",
33+
sourcekitdDocumentName: sourcekitdDocumentName,
2934
primaryFile: document
3035
)
3136
let position: Position? =
@@ -40,7 +45,13 @@ extension SwiftLanguageService {
4045
if self.capabilityRegistry.clientHasExperimentalCapability(GetReferenceDocumentRequest.method) {
4146
return GeneratedInterfaceDetails(uri: try urlData.uri, position: position)
4247
}
43-
let interfaceFilePath = self.generatedInterfacesPath.appendingPathComponent(urlData.displayName)
48+
let interfaceFilePath = self.generatedInterfacesPath
49+
.appendingPathComponent(buildSettingsFileHash)
50+
.appendingPathComponent(urlData.displayName)
51+
try FileManager.default.createDirectory(
52+
at: interfaceFilePath.deletingLastPathComponent(),
53+
withIntermediateDirectories: true
54+
)
4455
try await generatedInterfaceManager.snapshot(of: urlData).text.write(
4556
to: interfaceFilePath,
4657
atomically: true,

Tests/SourceKitLSPTests/SwiftInterfaceTests.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,65 @@ final class SwiftInterfaceTests: XCTestCase {
319319
)
320320
XCTAssertEqual(diagnostics.fullReport?.items, [])
321321
}
322+
323+
func testFoundationImportNavigation() async throws {
324+
let testClient = try await TestSourceKitLSPClient(
325+
capabilities: ClientCapabilities(experimental: [
326+
GetReferenceDocumentRequest.method: .dictionary(["supported": .bool(true)])
327+
])
328+
)
329+
let uri = DocumentURI(for: .swift)
330+
331+
let positions = testClient.openDocument(
332+
"""
333+
import 1️⃣Foundation
334+
""",
335+
uri: uri,
336+
language: .swift
337+
)
338+
339+
// Test navigation to Foundation module
340+
let foundationDefinition = try await testClient.send(
341+
DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
342+
)
343+
let foundationLocation = try XCTUnwrap(foundationDefinition?.locations?.only)
344+
XCTAssertEqual(foundationLocation.uri.scheme, "sourcekit-lsp")
345+
assertContains(foundationLocation.uri.pseudoPath, "Foundation.swiftinterface")
346+
}
347+
348+
func testFoundationSubmoduleNavigation() async throws {
349+
try SkipUnless.platformIsDarwin("Non-Darwin platforms don't have Foundation submodules")
350+
351+
let testClient = try await TestSourceKitLSPClient(
352+
capabilities: ClientCapabilities(experimental: [
353+
GetReferenceDocumentRequest.method: .dictionary(["supported": .bool(true)])
354+
])
355+
)
356+
let uri = DocumentURI(for: .swift)
357+
358+
let positions = testClient.openDocument(
359+
"""
360+
import 1️⃣Foundation.2️⃣NSAffineTransform
361+
""",
362+
uri: uri
363+
)
364+
365+
let foundationDefinition = try await testClient.send(
366+
DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
367+
)
368+
let foundationLocation = try XCTUnwrap(foundationDefinition?.locations?.only)
369+
XCTAssertEqual(foundationLocation.uri.scheme, "sourcekit-lsp")
370+
assertContains(foundationLocation.uri.pseudoPath, "Foundation.swiftinterface")
371+
372+
// Test navigation to NSAffineTransform
373+
let transformDefinition = try await testClient.send(
374+
DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["2️⃣"])
375+
)
376+
let transformLocation = try XCTUnwrap(transformDefinition?.locations?.only)
377+
// Verify we can identify this as a swiftinterface file
378+
XCTAssertEqual(transformLocation.uri.scheme, "sourcekit-lsp")
379+
assertContains(transformLocation.uri.pseudoPath, "Foundation.NSAffineTransform.swiftinterface")
380+
}
322381
}
323382

324383
private func assertSystemSwiftInterface(

0 commit comments

Comments
 (0)