Skip to content

getCompletionsAtPosition in API deadlocks when used with includeSymbol: true #64166

Description

@auvred

🔎 Search Terms

api, getCompletionsAtPosition, includeSymbol, deadlock

🕗 Version & Regression Information

This bug couldn't be reproduced in any published version because it can only be triggered if the error described in #64132 were fixed

⏯ Playground Link

No response

💻 Code

(note: the reproduction must be run on a #64133 branch, or on main after the PR is merged)

  1. Create repro dir
    mkdir /tmp/repro
    cd /tmp/repro
    npm init -y
    npm add typescript@7.0.2
    echo '{}' > tsconfig.json
  2. Create files
    // export.ts
    export const someValue = 1
    
    // index.ts
    const foo = {
        
    }
    
    // repro.mjs
    import path from 'node:path'
    import { API } from 'typescript/unstable/sync'
    
    const api = new API()
    const root = process.cwd()
    const file = path.join(root, 'index.ts')
    const tsconfigPath = path.join(root, 'tsconfig.json')
    const snapshot = api.updateSnapshot({ openProject: tsconfigPath })
    const project = snapshot.getProject(tsconfigPath)
    
    console.log(project.checker.getCompletionsAtPosition(file, 15, { includeSymbol:true }))
  3. Run repro
    node repro.mjs

🙁 Actual behavior

It hangs forever

🙂 Expected behavior

It returns completion items

Additional information about the issue

This happens because getCompletionsAtPosition uses a context with a checker API lifetime:

func (s *Session) handleGetCompletionsAtPosition(ctx context.Context, params *GetCompletionsAtPositionParams) (*CompletionInfoResponse, error) {
if params.IncludeSymbol {
ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeAPI)
}

Because of this, there is only one persistent checker is the in checkerpool.

The same goroutine first acquires the checker here

checker, done := l.GetProgram().GetTypeCheckerForFile(ctx, file)
defer done()

Image

Then it tries to acquire the same checker again here

ch, done := v.program.GetTypeChecker(ctx)
defer done()

Image

As a result, it silently deadlocks and hangs indefinitely. There is no panic because other goroutines are still active.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions