Skip to content

Conversation

@Kila2
Copy link
Contributor

@Kila2 Kila2 commented Dec 31, 2025

Motivation

When working with third-party binary frameworks (XCFrameworks) or libraries that are not fully indexed by IndexStoreDB, "Jump to Definition" often fails silently or returns no results. However, sourcekitd often possesses knowledge that these symbols belong to a specific module (via the systemModule property in SymbolDetails), even if the specific USR location isn't in the index.

Modification

Modified definitionLocations in SourceKitLSPServer.swift.

When indexBasedDefinition is performed:

  1. If the IndexStore is unavailable, or
  2. If the IndexStore lookup returns 0 occurrences for the specific USR,

The logic now checks if symbol.systemModule is present. If it is, it calls definitionInInterface (wrapping source.request.editor.open.interface), allowing the editor to jump to the generated Swift Interface file (.swiftinterface / swiftmodule) instead of failing.

Result

Users can now jump to definition for symbols in pre-compiled binary frameworks even without index data, provided the module is visible to the compiler.

…efinition

Currently, `indexBasedDefinition` relies heavily on IndexStoreDB. If a symbol
belongs to a binary framework or a library that hasn't been indexed (but has
module info provided by sourcekitd), the definition request fails or returns
empty results.

This change adds a fallback mechanism in `definitionLocations`. When no
occurrences are found in the index, we check if `systemModule` information
is available on the symbol. If so, we trigger `definitionInInterface` to
generate the textual interface (via `editor.open.interface`) and return that
location.

This improves navigation for binary dependencies (XCFrameworks) and SDKs
partially covered by the index.
Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have an example where this produces results where it previously doesn’t. A test case that wraps such an example would be ideal. If you have an example that requires a more complicated setup, I can help you reduce it to a test case.

@Kila2
Copy link
Contributor Author

Kila2 commented Jan 4, 2026

Simplified Test Case Proposal

I realized this can be tested in a much simpler way without relying on binary frameworks: by disabling background indexing.

When background indexing is disabled (or when indexing has not run yet), IndexStoreDB does not contain cross-module symbol data, which causes the current implementation to fail.

Proposed Scenario

  1. Create a workspace with two Swift modules (e.g. Lib and Executable, where Executable imports Lib).
  2. Configure SourceKitLSPOptions to disable background indexing (e.g. index: .init(enable: false)).
  3. In Executable, attempt to jump to the definition of a symbol imported from Lib.

Current Behavior

The request fails (or returns no results) because indexBasedDefinition finds no occurrences in an empty or incomplete index.

Behavior with This PR

The request should succeed by falling back to generating the module interface for Lib.
Even without an index, sourcekitd still provides module information for the symbol (via the systemModule field), allowing the fallback logic to work correctly.

This demonstrates that the fallback path behaves as expected when the index is unavailable.

Do you think this approach fits well with the existing test suite structure?

@ahoppen
Copy link
Member

ahoppen commented Jan 5, 2026

I just tried replicating that setup you described and I don’t think we hit your fallback case that way. We need to generate the swiftmodule for Lib. Generating it through a build will also generate .swiftsourceinfo, which the cursor info uses to provide a source location. If this fallback is intended for xcframeworks, I think the best test case would be to generate an xcframework in a test case and then trying to jump to a definition within it. Would you like to try and create a test case based on my example below. If not, instructions of how to test this locally would be great and I can help you shape it into a SourceKit-LSP test case.

My test case
func testJumpToSwiftInterfaceIfIndexLookupFails() async throws {
  let project = try await SwiftPMTestProject(
    files: [
      "Lib/Lib.swift": """
      public func 1️⃣abc() {}
      """,
      "Executable/Executable.swift": """
      import Lib

      func test() {
        2️⃣abc()
      }
      """,
    ],
    manifest: """
      let package = Package(
        name: "MyLibrary",
        targets: [
          .target(name: "Lib"),
          .executableTarget(name: "Executable", dependencies: ["Lib"])
        ]
      )
      """
  )
  try await SwiftPMTestProject.build(at: project.scratchDirectory, extraArguments: ["--disable-index-store"])

  let (uri, positions) = try project.openDocument("Executable.swift")
  let definitions = try await project.testClient.send(
    DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["2️⃣"])
  )
  XCTAssertEqual((definitions?.locations), [try project.location(from: "1️⃣", to: "1️⃣", in: "Lib.swift")])
}

@Kila2
Copy link
Contributor Author

Kila2 commented Jan 5, 2026

Good catch — a normal build still emits .swiftsourceinfo, so the fallback never triggers.
I updated the test to also pass -Xswiftc -avoid-emit-module-source-info along with --disable-index-store, which forces the Swift interface fallback path. With that change the test reproduces the issue and validates the fix.

@ahoppen
Copy link
Member

ahoppen commented Jan 5, 2026

Nice idea. Could you adjust the test case I proposed accordingly and add it to your PR?

@Kila2
Copy link
Contributor Author

Kila2 commented Jan 6, 2026

Sure — I’ve updated the test case accordingly and added it to the PR.

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two nitpicks, otherwise looks good to me. Thank you!

let project = try await SwiftPMTestProject(
files: [
"Lib/Lib.swift": """
public func 1️⃣abc() {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don’t need the 1 position marker anymore and 2 can then become 1 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks for the review!

)
"""
)
try await SwiftPMTestProject.build(at: project.scratchDirectory, extraArguments: ["--disable-index-store", "-Xswiftc", "-avoid-emit-module-source-info"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you run swift-format on your changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Let’s get this in. Thanks for your contribution, @Kila2 ❤️

@ahoppen
Copy link
Member

ahoppen commented Jan 6, 2026

@swift-ci Please test

@ahoppen
Copy link
Member

ahoppen commented Jan 6, 2026

@swift-ci Please test Windows

1 similar comment
@ahoppen
Copy link
Member

ahoppen commented Jan 6, 2026

@swift-ci Please test Windows

@ahoppen ahoppen enabled auto-merge January 6, 2026 19:11
auto-merge was automatically disabled January 8, 2026 11:16

Head branch was pushed to by a user without write access

@ahoppen
Copy link
Member

ahoppen commented Jan 8, 2026

@swift-ci Please test

@ahoppen ahoppen enabled auto-merge January 8, 2026 13:02
@ahoppen ahoppen disabled auto-merge January 8, 2026 13:02
@ahoppen ahoppen enabled auto-merge January 8, 2026 13:02
@ahoppen
Copy link
Member

ahoppen commented Jan 8, 2026

@swift-ci Please test Windows

@ahoppen ahoppen merged commit 69c864b into swiftlang:main Jan 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants