sourcekit-lsp icon indicating copy to clipboard operation
sourcekit-lsp copied to clipboard

Jump-to-definition to `init` of a C struct does not work

Open ahoppen opened this issue 1 year ago • 4 comments

When doing jump to definition on the timespec initializer in the following (jump-to-definition on tv_spec), it doesn’t jump anywhere, probably because the C struct doesn’t have an initializer.

import Darwin

func test() {
  _ = timespec(tv_sec: 0, tv_nsec: 0)
}

rdar://126382075

ahoppen avatar May 13 '24 16:05 ahoppen

I have come up with a partial solution to this bug,

  1. Check if the primary entry in the sourcekitd response from CursorInfoRequest has key.kind: source.lang.swift.ref.function.constructor, key.decl_lang: source.lang.swift but neither key.filepath nor key.groupname. If I get it correctly, this indicates a reference to a nil constructor of a C struct.
  2. Get the TokenSyntax from the syntax tree at the position where CursorInfoRequest was called. Find the initializer parent FunctionCallExprSyntax of that TokenSyntax and check the type of FunctionCallExprSyntax.calledExpression.
  3. If calledExpression is MemberAccessExprSyntax: to be implemented
  4. If calledExpression is DeclReferenceExprSyntax, make a CursorInfoRequest on this token's position.
  5. Check the value of key.kind of the response dictionary. 5.1. If key.kind: source.lang.swift.ref.typealias: get the TokenSyntax from the syntax tree at the position of calledExpression, get its parent TypeAliasDeclSyntax and make a CursorInfoRequest on the position of TypeAliasDeclSyntax.initializer.value. Back to 5. 5.2. else: I just return the result here. In the future I should check if key.kind is only a reference but not the declaration itself and search further.

However I've encountered difficulties to get sourcekitd search beyond the local file. CursorInfoRequest on TS in main.swift returned an error response.

   let project = try await SwiftPMTestProject(
      files: [
        "Sources/MySwiftLibrary/ta.swift":
          """
        import Darwin
        typealias TS = timespec
        """,
        "Sources/MySwiftLibrary/main.swift":
          """
        var a = TS()
        a = TS(2️⃣tv_sec: 0, tv_nsec: 0)
        
        struct A {}
        extension A {
          init(a: Int) {}
        }
        String(cString: "123")
        """,
      ],
      manifest: """
        let package = Package(
          name: "MyLibrary",
          targets: [
            .target(name: "MyLibrary"),
            .target(name: "MySwiftLibrary", dependencies: ["MyLibrary"])
          ]
        )
        """,
      enableBackgroundIndexing: true
    )

    _ = try project.openDocument("ta.swift")
    let (uri, positions) = try project.openDocument("main.swift")

AppAppWorks avatar Jul 04 '24 21:07 AppAppWorks

This is probably more a bug on the sourcekitd side and we should fix there, rather than trying to workaround it here. What I expect to be happening is that sourcekitd should return a cursor info response with modulename + is_system + usr set and then that should be used to jump to the generated interface.

From a quick test that is the case, it's just that the initializer is giving Darwin as the module rather than Darwin.POSIX.sys.types._timespec:

{
    key.kind: source.lang.swift.ref.struct,
    key.name: "timespec",
    key.usr: "c:@S@timespec",
    key.typename: "timespec.Type",
    key.modulename: "Darwin.POSIX.sys.types._timespec",
    key.annotated_decl: "<Declaration>struct timespec</Declaration>",
    key.is_system: true,
    key.decl_lang: source.lang.objc,
    ...
    key.secondary_symbols: [
      {
        key.kind: source.lang.swift.ref.function.constructor,
        key.name: "init(tv_sec:tv_nsec:)",
        key.usr: "s:So8timespecV6tv_sec0B5_nsecABSi_Sitcfc",
        key.typename: "(timespec.Type) -> (Int, Int) -> timespec",
        key.modulename: "Darwin",
        ...
        key.is_system: true,
        ...
        key.decl_lang: source.lang.swift,
      }
    ],
    ...
  }

bnbarham avatar Jul 05 '24 18:07 bnbarham

@bnbarham thanks for your insight! I've spotted the same thing when looking into the source code, but I thought that was the correct behaviour of sourcekitd and it was our responsibility to make sense of it.

AppAppWorks avatar Jul 05 '24 18:07 AppAppWorks

but I thought that was the correct behaviour of sourcekitd and it was our responsibility to make sense of it.

Heh, fair enough :) But no, it's definitely a bug on the sourcekitd side - I imagine we just don't handle the synthesized initializer correctly.

bnbarham avatar Jul 05 '24 18:07 bnbarham