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..abc0a4e588 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -586,6 +586,22 @@ 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 { + declarations := symbol.Declarations + // 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 + } + } + } + var options refOptions if !isRename { options.use = referenceUseReferences @@ -594,7 +610,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, if symbolsAndEntries is nil (e.g., from blocking a library symbol rename), return false + 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) { @@ -655,10 +678,16 @@ 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 } + // Early return if symbolsAndEntries is nil (e.g., rename was blocked for a standard library symbol) + 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 +973,11 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return l.mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget) } +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 { moduleSourceFileName := "" if symbol == nil || !((symbol.Flags&ast.SymbolFlagsModule != 0) && len(symbol.Declarations) != 0) { 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