From f2d2641ae77cf258f6caf844e94708de4a70f36a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:42:48 +0000 Subject: [PATCH 1/4] Initial plan From 166145bc69d83055b3f9571bb93eb0c061428efb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:57:54 +0000 Subject: [PATCH 2/4] Add failing test for Go to Definition with getter returning callable interface Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- ...tionGetterReturnsCallableInterface_test.go | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 internal/fourslash/tests/goToDefinitionGetterReturnsCallableInterface_test.go diff --git a/internal/fourslash/tests/goToDefinitionGetterReturnsCallableInterface_test.go b/internal/fourslash/tests/goToDefinitionGetterReturnsCallableInterface_test.go new file mode 100644 index 0000000000..f555f50d1e --- /dev/null +++ b/internal/fourslash/tests/goToDefinitionGetterReturnsCallableInterface_test.go @@ -0,0 +1,53 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestGoToDefinitionGetterReturnsCallableInterface(t *testing.T) { + t.Parallel() + + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: type.d.ts +export interface Disposable { + dispose(): void; +} + +export interface [|Event|] { + [|(|] + listener: (e: T) => any, + thisArgs?: any, + disposables?: Disposable[] + [|)|]: Disposable; +} + +export interface TextDocumentChangeEvent { + document: T; +} + +export declare class TextDocuments< + T extends { + uri: string; + } +> { + [|get onDidChangeContent()|]: Event>; +} + +export interface TextDocument { + uri: string; +} + +// @Filename: index.ts +import { TextDocument, TextDocuments } from "./type"; + +var documents: TextDocuments; + +documents!.[|onDid/*1*/ChangeContent|]() +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyBaselineGoToDefinition(t, true, "1") +} From 65053fbef462d7bdc668faab34de78e29efbac6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 02:58:50 +0000 Subject: [PATCH 3/4] Add baseline for failing test Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- ...terReturnsCallableInterface.baseline.jsonc | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc diff --git a/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc b/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc new file mode 100644 index 0000000000..4b7bd89d21 --- /dev/null +++ b/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc @@ -0,0 +1,24 @@ +// === goToDefinition === +// === /type.d.ts === +// export interface Disposable { +// dispose(): void; +// } +// +// export interface Event { +// [|( +// listener: (e: T) => any, +// thisArgs?: any, +// disposables?: Disposable[] +// ): Disposable;|] +// } +// +// export interface TextDocumentChangeEvent { +// --- (line: 14) skipped --- + +// === /index.ts === +// import { TextDocument, TextDocuments } from "./type"; +// +// var documents: TextDocuments; +// +// documents!.[|onDid/*GOTO DEF*/ChangeContent|]() +// \ No newline at end of file From 4d75c07d962104487e56583368a73a5bdaa68662 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:02:42 +0000 Subject: [PATCH 4/4] Fix Go to Definition to include getter when property returns callable interface Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/definition.go | 9 ++++++--- ...nitionGetterReturnsCallableInterface.baseline.jsonc | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/internal/ls/definition.go b/internal/ls/definition.go index e6ec8fa50e..4b9b671c1f 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -59,9 +59,12 @@ func (l *LanguageService) ProvideDefinition( declarations := getDeclarationsFromLocation(c, node) calledDeclaration := tryGetSignatureDeclaration(c, node) if calledDeclaration != nil { - // If we can resolve a call signature, remove all function-like declarations and add that signature. - nonFunctionDeclarations := core.Filter(slices.Clip(declarations), func(node *ast.Node) bool { return !ast.IsFunctionLike(node) }) - declarations = append(nonFunctionDeclarations, calledDeclaration) + // If we can resolve a call signature, remove all function-like declarations (except accessors) and add that signature. + // Accessors are kept because they show where the callable value comes from. + nonFunctionOrAccessorDeclarations := core.Filter(slices.Clip(declarations), func(node *ast.Node) bool { + return !ast.IsFunctionLike(node) || ast.IsAccessor(node) + }) + declarations = append(nonFunctionOrAccessorDeclarations, calledDeclaration) } return l.createLocationsFromDeclarations(originSelectionRange, clientSupportsLink, declarations), nil } diff --git a/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc b/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc index 4b7bd89d21..6f8bfb3575 100644 --- a/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/goToDefinition/goToDefinitionGetterReturnsCallableInterface.baseline.jsonc @@ -15,6 +15,16 @@ // export interface TextDocumentChangeEvent { // --- (line: 14) skipped --- +// --- (line: 18) skipped --- +// uri: string; +// } +// > { +// <|get [|onDidChangeContent|](): Event>;|> +// } +// +// export interface TextDocument { +// --- (line: 26) skipped --- + // === /index.ts === // import { TextDocument, TextDocuments } from "./type"; //