From 3856f25443986632ea99ebc9083582d848987416 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:42 +0000 Subject: [PATCH 1/6] Initial plan From d1f3da74d18e6fed5cb26cb84446668b03894848 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:23:56 +0000 Subject: [PATCH 2/6] Implement prepareRename LSP handler - Add nodeIsEligibleForRename function to check if a node can be renamed - Implement ProvidePrepareRename in language service - Add handlePrepareRename handler in LSP server - Update RenameProvider capability to indicate prepareProvider support - Handle keyword adjustments and eligibility checks Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 58 ++++++++++++++++++++++++++++++++ internal/ls/utilities.go | 42 +++++++++++++++++++++++ internal/lsp/server.go | 9 ++++- 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 2e0c8fa9e1..abd5d72562 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -679,6 +679,64 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, }, nil } +// ProvidePrepareRename provides information for rename preparation. +// This checks if the location is eligible for renaming and returns the range and placeholder. +func (l *LanguageService) ProvidePrepareRename(ctx context.Context, uri lsproto.DocumentUri, documentPosition lsproto.Position) (lsproto.PrepareRenameResponse, error) { + program, sourceFile := l.getProgramAndFile(uri) + position := int(l.converters.LineAndCharacterToPosition(sourceFile, documentPosition)) + + // Get the node at the position (can include keywords) + node := astnav.GetTouchingPropertyName(sourceFile, position) + + // Adjust the location for rename (e.g., from 'class' keyword to class name) + adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile) + + // Check if the adjusted node is eligible for rename + if !nodeIsEligibleForRename(adjustedNode) { + // Return null to indicate rename is not available + return lsproto.PrepareRenameResponse{}, nil + } + + // Get the symbol to validate this is renameable + checker, done := program.GetTypeChecker(ctx) + defer done() + symbol := checker.GetSymbolAtLocation(adjustedNode) + + // Check special cases like string literals and labels + if symbol == nil { + if ast.IsStringLiteralLike(adjustedNode) { + // For string literals, we could check contextual type, but for now + // we allow rename if nodeIsEligibleForRename already passed + textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) + lspRange := l.converters.ToLSPRange(sourceFile, textRange) + return lsproto.PrepareRenameResponse{ + Range: &lspRange, + }, nil + } else if ast.IsLabelName(adjustedNode) { + // Allow rename for labels + textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) + lspRange := l.converters.ToLSPRange(sourceFile, textRange) + return lsproto.PrepareRenameResponse{ + Range: &lspRange, + }, nil + } + // No symbol and not a special case - cannot rename + return lsproto.PrepareRenameResponse{}, nil + } + + // Check if symbol has declarations + if symbol.Declarations == nil || len(symbol.Declarations) == 0 { + return lsproto.PrepareRenameResponse{}, nil + } + + // Return the range for renaming + textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) + lspRange := l.converters.ToLSPRange(sourceFile, textRange) + return lsproto.PrepareRenameResponse{ + Range: &lspRange, + }, nil +} + func (l *LanguageService) getTextForRename(originalNode *ast.Node, entry *ReferenceEntry, newText string, checker *checker.Checker) string { if entry.kind != entryKindRange && (ast.IsIdentifier(originalNode) || ast.IsStringLiteralLike(originalNode)) { node := entry.node diff --git a/internal/ls/utilities.go b/internal/ls/utilities.go index 2778243905..e34838df6c 100644 --- a/internal/ls/utilities.go +++ b/internal/ls/utilities.go @@ -1121,6 +1121,48 @@ func getAdjustedLocationForExportDeclaration(node *ast.ExportDeclaration, forRen return nil } +// nodeIsEligibleForRename checks if a node can be renamed. +// This matches the TypeScript implementation in services/rename.ts +func nodeIsEligibleForRename(node *ast.Node) bool { + switch node.Kind { + case ast.KindIdentifier, + ast.KindPrivateIdentifier, + ast.KindStringLiteral, + ast.KindNoSubstitutionTemplateLiteral, + ast.KindThisKeyword: + return true + case ast.KindNumericLiteral: + // For numeric literals, check if they're used as property names + parent := node.Parent + if parent == nil { + return false + } + switch parent.Kind { + case ast.KindPropertyDeclaration, + ast.KindPropertySignature, + ast.KindPropertyAssignment, + ast.KindEnumMember, + ast.KindMethodDeclaration, + ast.KindMethodSignature, + ast.KindGetAccessor, + ast.KindSetAccessor, + ast.KindModuleDeclaration: + return parent.Name() == node + case ast.KindElementAccessExpression: + return parent.AsElementAccessExpression().ArgumentExpression == node + case ast.KindComputedPropertyName: + return true + case ast.KindLiteralType: + grandParent := parent.Parent + return grandParent != nil && grandParent.Kind == ast.KindIndexedAccessType + default: + return false + } + default: + return false + } +} + func symbolFlagsHaveMeaning(flags ast.SymbolFlags, meaning ast.SemanticMeaning) bool { if meaning == ast.SemanticMeaningAll { return true diff --git a/internal/lsp/server.go b/internal/lsp/server.go index 87d031fb11..b2edd2b2db 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -555,6 +555,7 @@ var handlers = sync.OnceValue(func() handlerMap { registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentReferencesInfo, (*Server).handleReferences, combineReferences) registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentRenameInfo, (*Server).handleRename, combineRenameResponse) + registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentPrepareRenameInfo, (*Server).handlePrepareRename) registerRequestHandler(handlers, lsproto.CallHierarchyIncomingCallsInfo, (*Server).handleCallHierarchyIncomingCalls) registerRequestHandler(handlers, lsproto.CallHierarchyOutgoingCallsInfo, (*Server).handleCallHierarchyOutgoingCalls) @@ -979,7 +980,9 @@ func (s *Server) handleInitialize(ctx context.Context, params *lsproto.Initializ Boolean: ptrTo(true), }, RenameProvider: &lsproto.BooleanOrRenameOptions{ - Boolean: ptrTo(true), + RenameOptions: &lsproto.RenameOptions{ + PrepareProvider: ptrTo(true), + }, }, DocumentHighlightProvider: &lsproto.BooleanOrDocumentHighlightOptions{ Boolean: ptrTo(true), @@ -1282,6 +1285,10 @@ func (s *Server) handleRename(ctx context.Context, ls *ls.LanguageService, param return ls.ProvideRenameFromSymbolAndEntries(ctx, params, originalNode, symbolAndEntries) } +func (s *Server) handlePrepareRename(ctx context.Context, ls *ls.LanguageService, params *lsproto.PrepareRenameParams) (lsproto.PrepareRenameResponse, error) { + return ls.ProvidePrepareRename(ctx, params.TextDocument.Uri, params.Position) +} + func combineRenameResponse(results iter.Seq[lsproto.RenameResponse]) lsproto.RenameResponse { combined := make(map[lsproto.DocumentUri][]*lsproto.TextEdit) seenChanges := make(map[lsproto.DocumentUri]*collections.Set[lsproto.Range]) From e3ff2e0031a964ee4e80448dc31074d8a8f7a88b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:29:57 +0000 Subject: [PATCH 3/6] Update rename eligibility check and add tests - Update ProvideSymbolsAndEntries to check node eligibility after adjustment - Add test for prepareRename on keywords - Format code Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../tests/manual/prepareRenameKeyword_test.go | 39 +++++++++++++++++++ internal/ls/findallreferences.go | 25 +++++++----- 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 internal/fourslash/tests/manual/prepareRenameKeyword_test.go diff --git a/internal/fourslash/tests/manual/prepareRenameKeyword_test.go b/internal/fourslash/tests/manual/prepareRenameKeyword_test.go new file mode 100644 index 0000000000..45c3310a81 --- /dev/null +++ b/internal/fourslash/tests/manual/prepareRenameKeyword_test.go @@ -0,0 +1,39 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestPrepareRenameKeyword(t *testing.T) { + t.Parallel() + + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = ` +class /*1*/Foo { + method() {} +} + +const x = /*2*/(1 + 2); + +function /*3*/bar() {} +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + + // Test 1: Rename on 'class' keyword should work (adjusts to class name) + // Note: We can't easily test the class keyword position in fourslash, + // but we can test that renaming on the class name works + f.GoToMarker(t, "1") + f.VerifyRenameSucceeded(t, nil /*preferences*/) + + // Test 2: Rename on paren should fail + f.GoToMarker(t, "2") + f.VerifyRenameFailed(t, nil /*preferences*/) + + // Test 3: Rename on 'function' keyword should work (adjusts to function name) + f.GoToMarker(t, "3") + f.VerifyRenameSucceeded(t, nil /*preferences*/) +} diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index abd5d72562..61d2ae054a 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -582,8 +582,15 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr position := int(l.converters.LineAndCharacterToPosition(sourceFile, documentPosition)) node := astnav.GetTouchingPropertyName(sourceFile, position) - if isRename && node.Kind != ast.KindIdentifier { - return node, nil, false + + // For rename, check eligibility after adjusting location + // Note: getReferencedSymbolsForNode will also call getAdjustedLocation, but we need + // to check eligibility here first to fail fast + if isRename { + adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile) + if !nodeIsEligibleForRename(adjustedNode) { + return adjustedNode, nil, false + } } var options refOptions @@ -684,24 +691,24 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, func (l *LanguageService) ProvidePrepareRename(ctx context.Context, uri lsproto.DocumentUri, documentPosition lsproto.Position) (lsproto.PrepareRenameResponse, error) { program, sourceFile := l.getProgramAndFile(uri) position := int(l.converters.LineAndCharacterToPosition(sourceFile, documentPosition)) - + // Get the node at the position (can include keywords) node := astnav.GetTouchingPropertyName(sourceFile, position) - + // Adjust the location for rename (e.g., from 'class' keyword to class name) adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile) - + // Check if the adjusted node is eligible for rename if !nodeIsEligibleForRename(adjustedNode) { // Return null to indicate rename is not available return lsproto.PrepareRenameResponse{}, nil } - + // Get the symbol to validate this is renameable checker, done := program.GetTypeChecker(ctx) defer done() symbol := checker.GetSymbolAtLocation(adjustedNode) - + // Check special cases like string literals and labels if symbol == nil { if ast.IsStringLiteralLike(adjustedNode) { @@ -723,12 +730,12 @@ func (l *LanguageService) ProvidePrepareRename(ctx context.Context, uri lsproto. // No symbol and not a special case - cannot rename return lsproto.PrepareRenameResponse{}, nil } - + // Check if symbol has declarations if symbol.Declarations == nil || len(symbol.Declarations) == 0 { return lsproto.PrepareRenameResponse{}, nil } - + // Return the range for renaming textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) lspRange := l.converters.ToLSPRange(sourceFile, textRange) From b7b67c40da7f1de79f8c0eeda9136f9bd02ceeec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 23:31:38 +0000 Subject: [PATCH 4/6] Simplify rename validation without prepareRename Remove prepareRename implementation and return error from handleRename instead. - Add nodeIsEligibleForRename function to check basic eligibility - Update ProvideSymbolsAndEntries to validate adjusted node - Return error from ProvideRenameFromSymbolAndEntries when no symbols found - Remove prepareRename handler and test Note: Awaiting feedback on approach - some tests need adjustment for the new validation logic. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../tests/manual/prepareRenameKeyword_test.go | 39 ---------- internal/ls/findallreferences.go | 74 +++---------------- internal/lsp/server.go | 9 +-- 3 files changed, 12 insertions(+), 110 deletions(-) delete mode 100644 internal/fourslash/tests/manual/prepareRenameKeyword_test.go diff --git a/internal/fourslash/tests/manual/prepareRenameKeyword_test.go b/internal/fourslash/tests/manual/prepareRenameKeyword_test.go deleted file mode 100644 index 45c3310a81..0000000000 --- a/internal/fourslash/tests/manual/prepareRenameKeyword_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package fourslash_test - -import ( - "testing" - - "github.com/microsoft/typescript-go/internal/fourslash" - "github.com/microsoft/typescript-go/internal/testutil" -) - -func TestPrepareRenameKeyword(t *testing.T) { - t.Parallel() - - defer testutil.RecoverAndFail(t, "Panic on fourslash test") - const content = ` -class /*1*/Foo { - method() {} -} - -const x = /*2*/(1 + 2); - -function /*3*/bar() {} -` - f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) - defer done() - - // Test 1: Rename on 'class' keyword should work (adjusts to class name) - // Note: We can't easily test the class keyword position in fourslash, - // but we can test that renaming on the class name works - f.GoToMarker(t, "1") - f.VerifyRenameSucceeded(t, nil /*preferences*/) - - // Test 2: Rename on paren should fail - f.GoToMarker(t, "2") - f.VerifyRenameFailed(t, nil /*preferences*/) - - // Test 3: Rename on 'function' keyword should work (adjusts to function name) - f.GoToMarker(t, "3") - f.VerifyRenameSucceeded(t, nil /*preferences*/) -} diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 61d2ae054a..344be6f703 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -16,6 +16,7 @@ import ( "github.com/microsoft/typescript-go/internal/compiler" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/debug" + "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/ls/lsconv" "github.com/microsoft/typescript-go/internal/lsp/lsproto" "github.com/microsoft/typescript-go/internal/scanner" @@ -583,11 +584,14 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr node := astnav.GetTouchingPropertyName(sourceFile, position) - // For rename, check eligibility after adjusting location - // Note: getReferencedSymbolsForNode will also call getAdjustedLocation, but we need - // to check eligibility here first to fail fast + // For rename, check if node could potentially be eligible after adjustment + // This is a preliminary check; full validation happens later if isRename { + // Apply location adjustment to handle keywords shifting to identifiers adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile) + // Only reject nodes that are definitely not renameable (like syntax tokens) + // Allow nodes that might be renameable (identifiers, literals, this, etc.) + // The detailed symbol validation will happen later in the flow if !nodeIsEligibleForRename(adjustedNode) { return adjustedNode, nil, false } @@ -662,8 +666,10 @@ 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) { - if originalNode.Kind != ast.KindIdentifier { - return lsproto.WorkspaceEditOrNull{}, nil + // Check if ProvideSymbolsAndEntries returned ok=false or no symbols found + // Return an error to inform the user that rename is not available + if symbolsAndEntries == nil || len(symbolsAndEntries) == 0 { + return lsproto.WorkspaceEditOrNull{}, fmt.Errorf("%s", diagnostics.You_cannot_rename_this_element) } program := l.GetProgram() @@ -686,64 +692,6 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, }, nil } -// ProvidePrepareRename provides information for rename preparation. -// This checks if the location is eligible for renaming and returns the range and placeholder. -func (l *LanguageService) ProvidePrepareRename(ctx context.Context, uri lsproto.DocumentUri, documentPosition lsproto.Position) (lsproto.PrepareRenameResponse, error) { - program, sourceFile := l.getProgramAndFile(uri) - position := int(l.converters.LineAndCharacterToPosition(sourceFile, documentPosition)) - - // Get the node at the position (can include keywords) - node := astnav.GetTouchingPropertyName(sourceFile, position) - - // Adjust the location for rename (e.g., from 'class' keyword to class name) - adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile) - - // Check if the adjusted node is eligible for rename - if !nodeIsEligibleForRename(adjustedNode) { - // Return null to indicate rename is not available - return lsproto.PrepareRenameResponse{}, nil - } - - // Get the symbol to validate this is renameable - checker, done := program.GetTypeChecker(ctx) - defer done() - symbol := checker.GetSymbolAtLocation(adjustedNode) - - // Check special cases like string literals and labels - if symbol == nil { - if ast.IsStringLiteralLike(adjustedNode) { - // For string literals, we could check contextual type, but for now - // we allow rename if nodeIsEligibleForRename already passed - textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) - lspRange := l.converters.ToLSPRange(sourceFile, textRange) - return lsproto.PrepareRenameResponse{ - Range: &lspRange, - }, nil - } else if ast.IsLabelName(adjustedNode) { - // Allow rename for labels - textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) - lspRange := l.converters.ToLSPRange(sourceFile, textRange) - return lsproto.PrepareRenameResponse{ - Range: &lspRange, - }, nil - } - // No symbol and not a special case - cannot rename - return lsproto.PrepareRenameResponse{}, nil - } - - // Check if symbol has declarations - if symbol.Declarations == nil || len(symbol.Declarations) == 0 { - return lsproto.PrepareRenameResponse{}, nil - } - - // Return the range for renaming - textRange := core.NewTextRange(adjustedNode.Pos(), adjustedNode.End()) - lspRange := l.converters.ToLSPRange(sourceFile, textRange) - return lsproto.PrepareRenameResponse{ - Range: &lspRange, - }, nil -} - func (l *LanguageService) getTextForRename(originalNode *ast.Node, entry *ReferenceEntry, newText string, checker *checker.Checker) string { if entry.kind != entryKindRange && (ast.IsIdentifier(originalNode) || ast.IsStringLiteralLike(originalNode)) { node := entry.node diff --git a/internal/lsp/server.go b/internal/lsp/server.go index b2edd2b2db..87d031fb11 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -555,7 +555,6 @@ var handlers = sync.OnceValue(func() handlerMap { registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentReferencesInfo, (*Server).handleReferences, combineReferences) registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentRenameInfo, (*Server).handleRename, combineRenameResponse) - registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentPrepareRenameInfo, (*Server).handlePrepareRename) registerRequestHandler(handlers, lsproto.CallHierarchyIncomingCallsInfo, (*Server).handleCallHierarchyIncomingCalls) registerRequestHandler(handlers, lsproto.CallHierarchyOutgoingCallsInfo, (*Server).handleCallHierarchyOutgoingCalls) @@ -980,9 +979,7 @@ func (s *Server) handleInitialize(ctx context.Context, params *lsproto.Initializ Boolean: ptrTo(true), }, RenameProvider: &lsproto.BooleanOrRenameOptions{ - RenameOptions: &lsproto.RenameOptions{ - PrepareProvider: ptrTo(true), - }, + Boolean: ptrTo(true), }, DocumentHighlightProvider: &lsproto.BooleanOrDocumentHighlightOptions{ Boolean: ptrTo(true), @@ -1285,10 +1282,6 @@ func (s *Server) handleRename(ctx context.Context, ls *ls.LanguageService, param return ls.ProvideRenameFromSymbolAndEntries(ctx, params, originalNode, symbolAndEntries) } -func (s *Server) handlePrepareRename(ctx context.Context, ls *ls.LanguageService, params *lsproto.PrepareRenameParams) (lsproto.PrepareRenameResponse, error) { - return ls.ProvidePrepareRename(ctx, params.TextDocument.Uri, params.Position) -} - func combineRenameResponse(results iter.Seq[lsproto.RenameResponse]) lsproto.RenameResponse { combined := make(map[lsproto.DocumentUri][]*lsproto.TextEdit) seenChanges := make(map[lsproto.DocumentUri]*collections.Set[lsproto.Range]) From d8a16fb73c83198f8641b212b6e8ccf7540e32e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 23:47:12 +0000 Subject: [PATCH 5/6] Localize diagnostic message properly Use locale.FromContext(ctx) and .Localize() to properly localize the "You cannot rename this element" error message instead of just converting to string. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 344be6f703..809403b958 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -17,6 +17,7 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/debug" "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/locale" "github.com/microsoft/typescript-go/internal/ls/lsconv" "github.com/microsoft/typescript-go/internal/lsp/lsproto" "github.com/microsoft/typescript-go/internal/scanner" @@ -669,7 +670,7 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, // Check if ProvideSymbolsAndEntries returned ok=false or no symbols found // Return an error to inform the user that rename is not available if symbolsAndEntries == nil || len(symbolsAndEntries) == 0 { - return lsproto.WorkspaceEditOrNull{}, fmt.Errorf("%s", diagnostics.You_cannot_rename_this_element) + return lsproto.WorkspaceEditOrNull{}, fmt.Errorf(diagnostics.You_cannot_rename_this_element.Localize(locale.FromContext(ctx))) } program := l.GetProgram() From 3b02540fbe7b2fc40b81e8eceec98708f2493c93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:07:22 +0000 Subject: [PATCH 6/6] Fix format string bug in error creation Use errors.New() instead of fmt.Errorf() when creating error from localized message to avoid treating the message as a format string. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/findallreferences.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 809403b958..8d9f4875ca 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -3,6 +3,7 @@ package ls import ( "cmp" "context" + "errors" "fmt" "slices" "strings" @@ -670,7 +671,7 @@ func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, // Check if ProvideSymbolsAndEntries returned ok=false or no symbols found // Return an error to inform the user that rename is not available if symbolsAndEntries == nil || len(symbolsAndEntries) == 0 { - return lsproto.WorkspaceEditOrNull{}, fmt.Errorf(diagnostics.You_cannot_rename_this_element.Localize(locale.FromContext(ctx))) + return lsproto.WorkspaceEditOrNull{}, errors.New(diagnostics.You_cannot_rename_this_element.Localize(locale.FromContext(ctx))) } program := l.GetProgram()