jpsim / jpsim/SourceKitten

Off-by-one bug in generic inherited type.

Open
#658 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Swift
Stars
2.4k
Forks
237
PR merge metrics
No merged PRs in 30d

Description

Hey, I'm maintaining Cuckoo and recently a bug surfaced (though it was present all this time, just under our radar) where SourceKitten includes the last > of the generic inherited type.

I've been using SourceKitten 0.23.2 until now; though upgrading to 0.29.0 didn't help, unfortunately.

Xcode 11.5, though it's the same result on 10.2.1.

Some examples:

struct gg<G: T<Int>> {}  // "key.inheritedTypes" is "T<Int>>"
func bg<T: G<Void>>()    // "key.inheritedTypes" is "G<Void>>"

This applies for any class, struct, or function.

I've tried tracking this bug down inside the project (master) with the intention of filing a PR, though the source code gets increasingly complex for my iOS brain. I found that at Request.swift:88, the string is already incorrect and I'm unsure whether it's a SourceKitten bug or SourceKit provides incorrect numbers.

I've created a general structure test that should go green when this is resolved:

func testGenericInheritedType() throws {
        let structure = try Structure(file: File(contents: "class Foo<T, U: V<T>> {}"))
        let expected: NSDictionary = [
            "key.substructure": [
                [
                    "key.kind": "source.lang.swift.decl.class",
                    "key.accessibility": "source.lang.swift.accessibility.internal",
                    "key.offset": 0,
                    "key.nameoffset": 6,
                    "key.namelength": 3,
                    "key.bodyoffset": 23,
                    "key.bodylength": 0,
                    "key.length": 24,
                    "key.name": "Foo",
                    "key.substructure": [
                        [
                            "key.kind": "source.lang.swift.decl.generic_type_param",
                            "key.length": 1,
                            "key.name": "T",
                            "key.namelength": 1,
                            "key.nameoffset": 10,
                            "key.offset": 10,
                        ],
                        [
                            "key.elements": [
                                [
                                    "key.kind": "source.lang.swift.structure.elem.typeref",
                                    "key.length": 4,
                                    "key.offset": 16,
                                ]
                            ],
                            "key.inheritedtypes": [
                                [
                                    "key.name": "V<T>",
                                ]
                            ],
                            "key.kind": "source.lang.swift.decl.generic_type_param",
                            "key.length": 7,
                            "key.name": "U",
                            "key.namelength": 1,
                            "key.nameoffset": 13,
                            "key.offset": 13,
                      ]
                    ]
                ]
            ],
            "key.offset": 0,
            "key.diagnostic_stage": "source.diagnostic.stage.swift.parse",
            "key.length": 24,
        ]
        XCTAssertEqual(toNSDictionary(structure.dictionary), expected, "should generate expected structure")
    }

Though I'm not sure about the specific offsets, so I wouldn't trust me on this.

In Cuckoo I added some simple protection against this, but it's pretty fragile (it just checks for >> in the last parameter), so I wanted to get your feedback on the complexity of this issue and whether we should rather compensate on our side rather than waiting for a fix.

Oh and I tried looking for this issue, so forgive me if I overlooked a duplicate, feel free to close this if it's known and/or unfixable. If I forgot to disclose some important info, let me know! 🙂

One last thing, this bug can be sidestepped by adding a space in front of the last >. So <T: G<Void> > actually returns the correct inherited type of just G<Void>.

EDIT: One actually important thing might be, this issue only arises when the inheritance type is generic as well, for example <T: G> correctly returns just G as the inherited type.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at Request.swift:88, where the issue reports that the inherited type is already incorrect, and inspect the supplied Structure test for the expected key.inheritedtypes value. Compare behavior for generic and non-generic inherited types, including the spacing workaround. Done means generic inherited types no longer include the final extra > while the provided test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.