From febb0d9c2568af32d7e6607a30c8014cb1f516ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:59:49 +0000 Subject: [PATCH 01/10] Initial plan From bd5851814f6f2b98426d7159b6369d0f09d0fcf1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:32:48 +0000 Subject: [PATCH 02/10] Add check to prevent renaming standard library symbols Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../manual/renameStandardLibrary_test.go | 32 +++++++++++++ internal/ls/findallreferences.go | 46 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 internal/fourslash/tests/manual/renameStandardLibrary_test.go diff --git a/internal/fourslash/tests/manual/renameStandardLibrary_test.go b/internal/fourslash/tests/manual/renameStandardLibrary_test.go new file mode 100644 index 0000000000..2aac623587 --- /dev/null +++ b/internal/fourslash/tests/manual/renameStandardLibrary_test.go @@ -0,0 +1,32 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestRenameStandardLibrary(t *testing.T) { + t.Parallel() + + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = ` +// Test that standard library symbols cannot be renamed +/*1*/setTimeout(() => {}, 100); +/*2*/console.log("test"); +const arr = [1, 2, 3]; +arr./*3*/push(4); +const str = "test"; +str./*4*/substring(0, 1); +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + + // Try to rename standard library functions - should fail + markers := []string{"1", "2", "3", "4"} + for _, marker := range markers { + f.GoToMarker(t, marker) + f.VerifyRenameFailed(t, nil /*preferences*/) + } +} diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 2e0c8fa9e1..8b7ab52224 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -586,6 +586,17 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr return node, nil, false } + // For rename operations, check if we're trying to rename a standard library symbol + if isRename { + checker, done := program.GetTypeChecker(ctx) + symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) + done() + if symbol != nil && isDefinedInLibraryFile(program, symbol) { + // Disallow rename for elements that are defined in the standard TypeScript library + return node, nil, false + } + } + var options refOptions if !isRename { options.use = referenceUseReferences @@ -594,7 +605,14 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr options.useAliasesForRename = true } - return node, l.getReferencedSymbolsForNode(ctx, position, node, program, program.GetSourceFiles(), options, nil), true + symbolsAndEntries := l.getReferencedSymbolsForNode(ctx, position, node, program, program.GetSourceFiles(), options, nil) + + // When renaming, check if the result is nil (e.g., due to other errors) + if isRename && symbolsAndEntries == nil { + return node, nil, false + } + + return node, symbolsAndEntries, true } func (l *LanguageService) ProvideReferencesFromSymbolAndEntries(ctx context.Context, params *lsproto.ReferenceParams, originalNode *ast.Node, symbolsAndEntries []*SymbolAndEntries) (lsproto.ReferencesResponse, error) { @@ -659,6 +677,11 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, return lsproto.WorkspaceEditOrNull{}, nil } + // If symbolsAndEntries is nil (e.g., due to attempting to rename a standard library symbol), return null + if symbolsAndEntries == nil { + return lsproto.WorkspaceEditOrNull{}, nil + } + program := l.GetProgram() entries := core.FlatMap(symbolsAndEntries, func(s *SymbolAndEntries) []*ReferenceEntry { return s.references }) changes := make(map[lsproto.DocumentUri][]*lsproto.TextEdit) @@ -944,6 +967,27 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return l.mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget) } +// isDefinedInLibraryFile checks if a symbol is defined ONLY in the standard TypeScript library. +// Returns true only if ALL declarations are in library files. +func isDefinedInLibraryFile(program *compiler.Program, symbol *ast.Symbol) bool { + if symbol.Declarations == nil || len(symbol.Declarations) == 0 { + return false + } + // Check if ALL declarations are in library files + for _, declaration := range symbol.Declarations { + sourceFile := ast.GetSourceFileOfNode(declaration) + if sourceFile == nil { + return false + } + if !program.IsSourceFileDefaultLibrary(sourceFile.Path()) || !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionDts) { + // Found a non-library declaration, so it's not purely a library symbol + return false + } + } + // All declarations are in library files + return true +} + func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx context.Context, symbol *ast.Symbol, program *compiler.Program, sourceFiles []*ast.SourceFile, checker *checker.Checker, options refOptions, sourceFilesSet *collections.Set[string]) []*SymbolAndEntries { moduleSourceFileName := "" if symbol == nil || !((symbol.Flags&ast.SymbolFlagsModule != 0) && len(symbol.Declarations) != 0) { From eb502d7c431fea571050a890139ca718a981ca4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:35:29 +0000 Subject: [PATCH 03/10] Update baseline and apply formatting fixes Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 4 +- ...gElementInitializerExternal.baseline.jsonc | 152 +++-------------- ...entInitializerExternal.baseline.jsonc.diff | 153 +++--------------- 3 files changed, 52 insertions(+), 257 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 8b7ab52224..9110897b2f 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -606,12 +606,12 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr } symbolsAndEntries := l.getReferencedSymbolsForNode(ctx, position, node, program, program.GetSourceFiles(), options, nil) - + // When renaming, check if the result is nil (e.g., due to other errors) if isRename && symbolsAndEntries == nil { return node, nil, false } - + return node, symbolsAndEntries, true } diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc index e0664feded..c4d5ed9a5b 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc @@ -13,28 +13,11 @@ // const external = true; // // function f({ -// lvl1 = /*RENAME*/[|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] +// lvl1 = /*RENAME*/external, +// nested: { lvl2 = external}, +// oldName: newName = external // }) {} -// -// const { -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] -// } = obj; - -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// +// --- (line: 8) skipped --- @@ -43,28 +26,12 @@ // const external = true; // // function f({ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = /*RENAME*/[|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] +// lvl1 = external, +// nested: { lvl2 = /*RENAME*/external}, +// oldName: newName = external // }) {} // -// const { -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] -// } = obj; - -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// +// --- (line: 9) skipped --- @@ -73,115 +40,46 @@ // const external = true; // // function f({ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = /*RENAME*/[|externalRENAME|] +// lvl1 = external, +// nested: { lvl2 = external}, +// oldName: newName = /*RENAME*/external // }) {} // // const { -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] -// } = obj; - -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// +// --- (line: 10) skipped --- // === findRenameLocations === // === /renameBindingElementInitializerExternal.ts === -// const external = true; -// -// function f({ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] +// --- (line: 6) skipped --- // }) {} // // const { -// lvl1 = /*RENAME*/[|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] +// lvl1 = /*RENAME*/external, +// nested: { lvl2 = external}, +// oldName: newName = external // } = obj; -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// - // === findRenameLocations === // === /renameBindingElementInitializerExternal.ts === -// const external = true; -// -// function f({ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] -// }) {} +// --- (line: 7) skipped --- // // const { -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = /*RENAME*/[|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] +// lvl1 = external, +// nested: { lvl2 = /*RENAME*/external}, +// oldName: newName = external // } = obj; -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// - // === findRenameLocations === // === /renameBindingElementInitializerExternal.ts === -// const external = true; -// -// function f({ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = [|externalRENAME|] -// }) {} -// +// --- (line: 8) skipped --- // const { -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = /*RENAME*/[|externalRENAME|] -// } = obj; - -// === bundled:///libs/lib.dom.d.ts === -// --- (line: --) skipped --- -// * -// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -// */ -// declare var [|externalRENAME|]: External; -// /** -// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -// * -// --- (line: --) skipped --- -// \ No newline at end of file +// lvl1 = external, +// nested: { lvl2 = external}, +// oldName: newName = /*RENAME*/external +// } = obj; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc.diff index a557cecec2..75d783ee7a 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc.diff +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/renameBindingElementInitializerExternal.baseline.jsonc.diff @@ -121,7 +121,6 @@ -// lvl1 = [|externalRENAME|], -// nested: { lvl2 = [|externalRENAME|]}, -// oldName: newName = /*RENAME*/[|externalRENAME|] --// } = obj; +// lvl1 = external, +// --- (line: 5) skipped --- + @@ -132,28 +131,11 @@ +// const external = true; +// +// function f({ -+// lvl1 = /*RENAME*/[|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] ++// lvl1 = /*RENAME*/external, ++// nested: { lvl2 = external}, ++// oldName: newName = external +// }) {} -+// -+// const { -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] -+// } = obj; -+ -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// ++// --- (line: 8) skipped --- + + + @@ -162,28 +144,12 @@ +// const external = true; +// +// function f({ -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = /*RENAME*/[|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] ++// lvl1 = external, ++// nested: { lvl2 = /*RENAME*/external}, ++// oldName: newName = external +// }) {} +// -+// const { -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] -+// } = obj; -+ -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// ++// --- (line: 9) skipped --- + + + @@ -192,115 +158,46 @@ +// const external = true; +// +// function f({ -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = /*RENAME*/[|externalRENAME|] ++// lvl1 = external, ++// nested: { lvl2 = external}, ++// oldName: newName = /*RENAME*/external +// }) {} +// +// const { -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] -+// } = obj; -+ -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// ++// --- (line: 10) skipped --- + + + +// === findRenameLocations === +// === /renameBindingElementInitializerExternal.ts === -+// const external = true; -+// -+// function f({ -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] ++// --- (line: 6) skipped --- +// }) {} +// +// const { -+// lvl1 = /*RENAME*/[|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] ++// lvl1 = /*RENAME*/external, ++// nested: { lvl2 = external}, ++// oldName: newName = external +// } = obj; + -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// -+ + + +// === findRenameLocations === +// === /renameBindingElementInitializerExternal.ts === -+// const external = true; -+// -+// function f({ -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] -+// }) {} ++// --- (line: 7) skipped --- +// +// const { -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = /*RENAME*/[|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] ++// lvl1 = external, ++// nested: { lvl2 = /*RENAME*/external}, ++// oldName: newName = external +// } = obj; + -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// -+ + + +// === findRenameLocations === +// === /renameBindingElementInitializerExternal.ts === -+// const external = true; -+// -+// function f({ -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = [|externalRENAME|] -+// }) {} -+// ++// --- (line: 8) skipped --- +// const { -+// lvl1 = [|externalRENAME|], -+// nested: { lvl2 = [|externalRENAME|]}, -+// oldName: newName = /*RENAME*/[|externalRENAME|] -+// } = obj; -+ -+// === bundled:///libs/lib.dom.d.ts === -+// --- (line: --) skipped --- -+// * -+// * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/external) -+// */ -+// declare var [|externalRENAME|]: External; -+// /** -+// * The **`Window.frameElement`** property returns the element (such as iframe or object) in which the window is embedded. -+// * -+// --- (line: --) skipped --- -+// \ No newline at end of file ++// lvl1 = external, ++// nested: { lvl2 = external}, ++// oldName: newName = /*RENAME*/external + // } = obj; \ No newline at end of file From 5a5be0564eb4c4f359237890948b0c9e732d3a16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:38:50 +0000 Subject: [PATCH 04/10] Improve comments based on code review feedback Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 9110897b2f..1c6739f2f6 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -607,7 +607,7 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr symbolsAndEntries := l.getReferencedSymbolsForNode(ctx, position, node, program, program.GetSourceFiles(), options, nil) - // When renaming, check if the result is nil (e.g., due to other errors) + // When renaming, if symbolsAndEntries is nil (e.g., from blocking a library symbol rename), return false if isRename && symbolsAndEntries == nil { return node, nil, false } @@ -673,11 +673,12 @@ func (l *LanguageService) getImplementationReferenceEntries(ctx context.Context, } func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, params *lsproto.RenameParams, originalNode *ast.Node, symbolsAndEntries []*SymbolAndEntries) (lsproto.WorkspaceEditOrNull, error) { + // Early return if the node is not an identifier (can't be renamed) if originalNode.Kind != ast.KindIdentifier { return lsproto.WorkspaceEditOrNull{}, nil } - // If symbolsAndEntries is nil (e.g., due to attempting to rename a standard library symbol), return null + // Early return if symbolsAndEntries is nil (e.g., rename was blocked for a standard library symbol) if symbolsAndEntries == nil { return lsproto.WorkspaceEditOrNull{}, nil } From f6734f9e60baf403fe630c2771108cdc88fb34f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:30:52 +0000 Subject: [PATCH 05/10] Refactor to match TypeScript implementation using core.Some Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 1c6739f2f6..8e199798bf 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -591,9 +591,13 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr checker, done := program.GetTypeChecker(ctx) symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) done() - if symbol != nil && isDefinedInLibraryFile(program, symbol) { - // Disallow rename for elements that are defined in the standard TypeScript library - return node, nil, false + if symbol != nil { + // Disallow rename for elements that are defined in the standard TypeScript library. + if symbol.Declarations != nil && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { + return isDefinedInLibraryFile(program, declaration) + }) { + return node, nil, false + } } } @@ -968,25 +972,9 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return l.mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget) } -// isDefinedInLibraryFile checks if a symbol is defined ONLY in the standard TypeScript library. -// Returns true only if ALL declarations are in library files. -func isDefinedInLibraryFile(program *compiler.Program, symbol *ast.Symbol) bool { - if symbol.Declarations == nil || len(symbol.Declarations) == 0 { - return false - } - // Check if ALL declarations are in library files - for _, declaration := range symbol.Declarations { - sourceFile := ast.GetSourceFileOfNode(declaration) - if sourceFile == nil { - return false - } - if !program.IsSourceFileDefaultLibrary(sourceFile.Path()) || !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionDts) { - // Found a non-library declaration, so it's not purely a library symbol - return false - } - } - // All declarations are in library files - return true +func isDefinedInLibraryFile(program *compiler.Program, declaration *ast.Node) bool { + sourceFile := ast.GetSourceFileOfNode(declaration) + return sourceFile != nil && program.IsSourceFileDefaultLibrary(sourceFile.Path()) && tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionDts) } func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx context.Context, symbol *ast.Symbol, program *compiler.Program, sourceFiles []*ast.SourceFile, checker *checker.Checker, options refOptions, sourceFilesSet *collections.Set[string]) []*SymbolAndEntries { From e44795023358f9f26e177a6218fa45c593e3834d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:56:14 +0000 Subject: [PATCH 06/10] Remove nil check and handle empty declarations correctly Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 8e199798bf..2b6d06a0f1 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -592,11 +592,15 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) done() if symbol != nil { - // Disallow rename for elements that are defined in the standard TypeScript library. - if symbol.Declarations != nil && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { - return isDefinedInLibraryFile(program, declaration) - }) { - return node, nil, false + // Only allow a symbol to be renamed if it actually has at least one declaration. + declarations := symbol.Declarations + if len(declarations) > 0 { + // Disallow rename for elements that are defined in the standard TypeScript library. + if core.Some(declarations, func(declaration *ast.Node) bool { + return isDefinedInLibraryFile(program, declaration) + }) { + return node, nil, false + } } } } From 5746f95ce6789c996dfa3bd7b5f09e539c9dff29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:12:50 +0000 Subject: [PATCH 07/10] Remove empty declarations check - not needed for rename operation Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 2b6d06a0f1..abc0a4e588 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -592,15 +592,12 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) done() if symbol != nil { - // Only allow a symbol to be renamed if it actually has at least one declaration. declarations := symbol.Declarations - if len(declarations) > 0 { - // Disallow rename for elements that are defined in the standard TypeScript library. - if core.Some(declarations, func(declaration *ast.Node) bool { - return isDefinedInLibraryFile(program, declaration) - }) { - return node, nil, false - } + // Disallow rename for elements that are defined in the standard TypeScript library. + if core.Some(declarations, func(declaration *ast.Node) bool { + return isDefinedInLibraryFile(program, declaration) + }) { + return node, nil, false } } } From 9795c2b880012ac3a98f7c26d5970d89af888d4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:24:26 +0000 Subject: [PATCH 08/10] Add empty declarations check matching TypeScript semantics Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 5 +++++ .../jsdocTypedefTagRename01.baseline.jsonc | 2 +- .../jsdocTypedefTagRename01.baseline.jsonc.diff | 11 +++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index abc0a4e588..b8765f20d6 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -592,7 +592,12 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) done() if symbol != nil { + // Only allow a symbol to be renamed if it actually has at least one declaration. declarations := symbol.Declarations + if len(declarations) == 0 { + return node, nil, false + } + // Disallow rename for elements that are defined in the standard TypeScript library. if core.Some(declarations, func(declaration *ast.Node) bool { return isDefinedInLibraryFile(program, declaration) diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc index 382e55f562..055844e8b8 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc @@ -29,5 +29,5 @@ // // NumberLike = 10; // -// /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ +// /** @type {/*RENAME*/NumberLike} */ // var numberLike; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff index 8a16026273..11c6c9ccac 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff @@ -37,10 +37,13 @@ -// // /** @typedef {(string | number)} */ -// var [|NumberLikeRENAME|]; -+// var NumberLike; - // +-// -// [|NumberLikeRENAME|] = 10; +-// +-// /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ ++// var NumberLike; ++// +// NumberLike = 10; - // - // /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ ++// ++// /** @type {/*RENAME*/NumberLike} */ // var numberLike; \ No newline at end of file From 9ce1695fe4839c1ff8da95c8746bc914ddb9732f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:37:40 +0000 Subject: [PATCH 09/10] Move empty declarations check to getReferencedSymbolsForNode to fix JSDoc Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index b8765f20d6..a6c873c5ea 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -592,12 +592,7 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) done() if symbol != nil { - // Only allow a symbol to be renamed if it actually has at least one declaration. declarations := symbol.Declarations - if len(declarations) == 0 { - return node, nil, false - } - // Disallow rename for elements that are defined in the standard TypeScript library. if core.Some(declarations, func(declaration *ast.Node) bool { return isDefinedInLibraryFile(program, declaration) @@ -966,6 +961,11 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return l.getReferencedSymbolsForModule(ctx, program, symbol.Parent, false /*excludeImportTypeOfExportEquals*/, sourceFiles, sourceFilesSet) } + // Only allow a symbol to be renamed if it actually has at least one declaration. + if options.use == referenceUseRename && len(symbol.Declarations) == 0 { + return nil + } + moduleReferences := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, symbol, program, sourceFiles, checker, options, sourceFilesSet) // !!! cancellationToken if moduleReferences != nil && symbol.Flags&ast.SymbolFlagsTransient == 0 { return moduleReferences From 864359d2b5f9381829723622c12343add0464b0a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:54:07 +0000 Subject: [PATCH 10/10] Revert baseline files - JSDoc issue is pre-existing Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 5 ----- .../jsdocTypedefTagRename01.baseline.jsonc | 2 +- .../jsdocTypedefTagRename01.baseline.jsonc.diff | 11 ++++------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index a6c873c5ea..abc0a4e588 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -961,11 +961,6 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return l.getReferencedSymbolsForModule(ctx, program, symbol.Parent, false /*excludeImportTypeOfExportEquals*/, sourceFiles, sourceFilesSet) } - // Only allow a symbol to be renamed if it actually has at least one declaration. - if options.use == referenceUseRename && len(symbol.Declarations) == 0 { - return nil - } - moduleReferences := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, symbol, program, sourceFiles, checker, options, sourceFilesSet) // !!! cancellationToken if moduleReferences != nil && symbol.Flags&ast.SymbolFlagsTransient == 0 { return moduleReferences diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc index 055844e8b8..382e55f562 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc @@ -29,5 +29,5 @@ // // NumberLike = 10; // -// /** @type {/*RENAME*/NumberLike} */ +// /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ // var numberLike; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff index 11c6c9ccac..8a16026273 100644 --- a/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff +++ b/testdata/baselines/reference/submodule/fourslash/findRenameLocations/jsdocTypedefTagRename01.baseline.jsonc.diff @@ -37,13 +37,10 @@ -// // /** @typedef {(string | number)} */ -// var [|NumberLikeRENAME|]; --// --// [|NumberLikeRENAME|] = 10; --// --// /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ +// var NumberLike; -+// + // +-// [|NumberLikeRENAME|] = 10; +// NumberLike = 10; -+// -+// /** @type {/*RENAME*/NumberLike} */ + // + // /** @type {/*RENAME*/[|NumberLikeRENAME|]} */ // var numberLike; \ No newline at end of file