Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 6, 2025

Completions panic with "index out of range [-1]" when requested inside new Map([...]) constructor at certain positions:

const m = new Map([
    [/*crash here*/'0', ['0', false]],
]);

Additionally, string literal completions were not being suggested in tuple contexts without typing a quote character first.

This PR also fixes an edge case bug where properties named "-1" would incorrectly be suggested as completions in array literals due to the -1 index being used to look up property names.

Changes

  • completions.go: Add special handling for OpenBracketToken and CommaToken in array literals

    • When previousToken is [, get contextual type for element at index 0
    • When previousToken is ,, count elements before cursor and get contextual type for next element
    • Prevents invalid tokens (which aren't array elements) from reaching checker's contextual type resolution
  • services.go: Add GetContextualTypeForArrayElement function

    • Exported wrapper around getContextualTypeForElementExpression for language service use
    • Enables proper contextual type resolution for array elements at specific indices
  • Tests: Added TestCompletionsInMapConstructorNoCrash and TestCompletionsInArrayLiteralWithContextualType

    • Verifies no crash at reported positions
    • Validates that string literal completions work in tuple contexts without typing quotes
    • Confirms that the -1 property edge case is fixed (properties named "-1" are not suggested)

Benefits

  1. Fixes the crash: Language service no longer passes non-element tokens to checker
  2. Enables better completions: String literal completions now work in tuple contexts without needing to type a quote first (e.g., let y: ["foo" | "bar"] = [/*here*/] now suggests "foo" and "bar")
  3. Fixes edge case bug: Properties named "-1" are no longer incorrectly suggested in array literals (e.g., let x: { "-1": "hello" } = [/**/] no longer suggests "hello")
  4. Better architecture: Language service handles token-level concerns, checker handles type-level concerns
Original prompt

This section details on the original issue you should resolve

<issue_title>Completions crash in call to new Map(...).</issue_title>
<issue_description>```ts
const m = new Map([
[/a/'0', ['0', false]]/b/,
]);


Request completions at `/*a*/` or `/*b*/`.

[error] panic handling requesttextDocument/completionruntime error: index out of range [-1]goroutine 3826 [running]:
runtime/debug.Stack()
runtime/debug/stack.go:26 +0x64
github.com/microsoft/typescript-go/internal/lsp.(*Server).recover(0x4000198008, 0x4019948ff0)
github.com/microsoft/typescript-go/internal/lsp/server.go:872 +0x40
panic({0x7ff7c70f3b80?, 0x40177be4b0?})
runtime/panic.go:783 +0x120
github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualTypeForElementExpression.func1(0x401a5d2ee0)
github.com/microsoft/typescript-go/internal/checker/checker.go:28915 +0x29c
github.com/microsoft/typescript-go/internal/checker.(*Checker).mapTypeEx(0x4005641e60?, 0x4018009db0?, 0x40178d3158?, 0x94?)
github.com/microsoft/typescript-go/internal/checker/checker.go:24914 +0xd4
github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualTypeForElementExpression(0x40106ea608?, 0x4018009d60?, 0x2?, 0x4?, 0x401978a6c0?, 0x7ff7c73b4af8?)
github.com/microsoft/typescript-go/internal/checker/checker.go:28910 +0x3c
github.com/microsoft/typescript-go/internal/checker.(*Checker).getContextualType(0x40106ea608, 0x401978a6c0, 0x4)
github.com/microsoft/typescript-go/internal/checker/checker.go:28328 +0x1e0
github.com/microsoft/typescript-go/internal/checker.(*Checker).GetContextualType.func1()
github.com/microsoft/typescript-go/internal/checker/services.go:310 +0x24
github.com/microsoft/typescript-go/internal/checker.runWithoutResolvedSignatureCaching[...](0x40106ea608?, 0x4018009db0, 0x400083f4a8?)
github.com/microsoft/typescript-go/internal/checker/services.go:365 +0x2f0
github.com/microsoft/typescript-go/internal/checker.runWithInferenceBlockedFromSourceNode[...](0x40106ea608?, 0x401978a6c0, 0x400083f4a8?)
github.com/microsoft/typescript-go/internal/checker/services.go:329 +0xe4
github.com/microsoft/typescript-go/internal/checker.(*Checker).GetContextualType(0x400083f620?, 0x400083f5c0?, 0x83f574?)
github.com/microsoft/typescript-go/internal/checker/services.go:310 +0x48
github.com/microsoft/typescript-go/internal/ls.getContextualType(0x401978a6c0, 0x400c205808?, 0x40106ea608?, 0x40106ea608)
github.com/microsoft/typescript-go/internal/ls/completions.go:2995 +0x2a0
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, 0x40106ea608, 0x400c205808, 0xb8, 0x400326e800)
github.com/microsoft/typescript-go/internal/ls/completions.go:1727 +0x1038
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionsAtPosition(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, 0x400c205808, 0xb8, 0x0)
github.com/microsoft/typescript-go/internal/ls/completions.go:383 +0x22c
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).ProvideCompletion(0x40097bbe90, {0x7ff7c74e2b88, 0x40097bbda0}, {0x400dc60000?, 0x40097bbda0?}, {0xdc60000?, 0x40?}, 0x4012c7a830)
github.com/microsoft/typescript-go/internal/ls/completions.go:44 +0xa0
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleCompletion(0x400443fe78?, {0x7ff7c74e2b88?, 0x40097bbda0?}, 0x400dc60000?, 0x400443fee8?)
github.com/microsoft/typescript-go/internal/lsp/server.go:1208 +0x3c
github.com/microsoft/typescript-go/internal/lsp.init.func1.registerLanguageServiceDocumentRequestHandler[...].16({0x7ff7c74e2b88, 0x40097bbda0}, 0x4019948ff0)
github.com/microsoft/typescript-go/internal/lsp/server.go:621 +0xec
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleRequestOrNotification(0x4000198008, {0x7ff7c74e2bc0?, 0x401e249220?}, 0x4019948ff0)
github.com/microsoft/typescript-go/internal/lsp/server.go:502 +0x128
github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop.func1()
github.com/microsoft/typescript-go/internal/lsp/server.go:405 +0x34
created by github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop in goroutine 20
github.com/microsoft/typescript-go/internal/lsp/server.go:425 +0x7a0


## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix completions crash in call to new Map Fix completions crash when IndexOfNode returns -1 Dec 6, 2025
@DanielRosenwasser
Copy link
Member

This might be alright, but I don't know if this is the right layer to do this in. Maybe this is more of a language service thing to work off of? Is there some sort of missing element we can use instead of the [ token that is probably being passed in? I'm not sure.

Also, it does reveal an undesirable behavior in both the new and old codebases.

// @filename: a.ts
let x: "foo" | "bar" = /*a*/;

// @filename: b.ts
let y: ["foo" | "bar", string] = [/*b*/];

If you request completions at /*a*/, you will get completions for the string literals "foo" and "bar".

In the old codebase (and I assume with this fix?), you will not get these suggested at /*b*/ until you actually type in a quote character.

@DanielRosenwasser
Copy link
Member

@copilot try to address.

Copy link
Contributor Author

Copilot AI commented Dec 7, 2025

@copilot try to address.

I'll address this by adding special handling for OpenBracketToken in the language service layer. The fix will handle completions after [ in array literals by getting the contextual type for the appropriate tuple element, which will also enable string literal completions without needing to type a quote first.

Working on this now.

This might be alright, but I don't know if this is the right layer to do this in. Maybe this is more of a language service thing to work off of? Is there some sort of missing element we can use instea...

Fixed in 90ca278 by handling OpenBracketToken and CommaToken at the language service layer. The fix:

  1. Prevents invalid tokens from reaching the checker (fixing the crash)
  2. Enables string literal completions without typing quotes in tuple contexts (e.g., let y: ["foo" | "bar"] = [/*here*/] now suggests "foo" and "bar")

Both issues you mentioned are now resolved.

Copilot AI changed the title Fix completions crash when IndexOfNode returns -1 Fix completions crash and enable contextual type completions in array literals Dec 7, 2025
@DanielRosenwasser DanielRosenwasser marked this pull request as ready for review December 8, 2025 23:59
Copilot AI review requested due to automatic review settings December 8, 2025 23:59
@jakebailey
Copy link
Member

Why does this need to differ so much from the original one in Strada?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a crash in completions when requesting completions inside new Map([...]) constructors and similar array literal contexts. The crash occurred because non-element tokens (like [ and ,) were being passed to the checker's getContextualTypeForElementExpression, which expects actual array element nodes and uses IndexOfNode that returns -1 for non-elements, causing an index out of range panic.

Key changes:

  • Added special handling for OpenBracketToken and CommaToken in array literals to compute contextual types at the token level
  • Created GetContextualTypeForArrayElement wrapper function to enable language service access to contextual type resolution for array elements
  • Added comprehensive tests validating both the crash fix and new string literal completion functionality

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
internal/ls/completions.go Added OpenBracketToken and CommaToken cases to handle array literal completions, preventing invalid tokens from reaching checker's contextual type resolution
internal/checker/services.go Added exported GetContextualTypeForArrayElement function to enable language service to resolve contextual types for array elements at specific indices
internal/fourslash/tests/manual/completionsInMapConstructorNoCrash_test.go Test verifying completions no longer crash in Map constructor array literals at problematic positions
internal/fourslash/tests/manual/completionsInArrayLiteralWithContextualType_test.go Test verifying string literal completions work in tuple contexts without needing to type quotes first

@DanielRosenwasser
Copy link
Member

Why does this need to differ so much from the original one in Strada?

Because in JavaScript, indexing into -1 would just get you undefined which just floats around, and gets compared against other values. It was never right, it was just handled in unanticipated ways.

In fact here is some crazy completions behavior you can trigger in both Strada and Corsa just from how -1 as a passed-in index is handled when the contextual type is not array-like at all:

let x: { "-1": "hello" } = [/**/]

Here, you will get completions for the string literal "hello".

image

(@copilot please add a test case to confirm that this no longer happens)

Copy link
Contributor Author

Copilot AI commented Dec 9, 2025

Why does this need to differ so much from the original one in Strada?

Because in JavaScript, indexing into -1 would just get you undefined which just floats around, and gets compared against ...

Added test case in d2f9582 that verifies properties named "-1" are not suggested in array literal completions. The test confirms the edge case is fixed - let x: { "-1": "hello" } = [/**/] no longer suggests "hello" as a completion.

@DanielRosenwasser DanielRosenwasser added this pull request to the merge queue Dec 9, 2025
Merged via the queue into main with commit 8372401 Dec 9, 2025
22 checks passed
@DanielRosenwasser DanielRosenwasser deleted the copilot/fix-completions-crash-map branch December 9, 2025 04:04
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.

Completions crash in call to new Map(...).

3 participants