gopls: fix out of bounds bug in extract

There was a bug where the code would try to access an out of bounds
index when trimming extra space and comments in extract. This change
also adds a regtest for extract.

Fixes golang/go#55271

Change-Id: I7da716a6a68a9678011abf15def47acdea0b33fe
Reviewed-on: https://go-review.googlesource.com/c/tools/+/432135
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Suzy Mueller 2022-09-20 14:59:24 -04:00
parent 16b974289f
commit 2f04713366
2 changed files with 76 additions and 2 deletions

View File

@ -676,7 +676,7 @@ func adjustRangeForCommentsAndWhiteSpace(rng span.Range, tok *token.File, conten
prevStart = start
// If start is within a comment, move start to the end
// of the comment group.
if file.Comments[startComment].Pos() <= start && start < file.Comments[startComment].End() {
if startComment < len(file.Comments) && file.Comments[startComment].Pos() <= start && start < file.Comments[startComment].End() {
start = file.Comments[startComment].End()
startComment++
}
@ -697,11 +697,16 @@ func adjustRangeForCommentsAndWhiteSpace(rng span.Range, tok *token.File, conten
// Find the index for the first comment that ends after the range end.
return file.Comments[i].End() >= rng.End
})
// Search will return n if not found, so we need to adjust if there are no
// comments that would match.
if endComment == len(file.Comments) {
endComment = -1
}
for prevEnd != end {
prevEnd = end
// If end is within a comment, move end to the start
// of the comment group.
if file.Comments[endComment].Pos() < end && end <= file.Comments[endComment].End() {
if endComment >= 0 && file.Comments[endComment].Pos() < end && end <= file.Comments[endComment].End() {
end = file.Comments[endComment].Pos()
endComment--
}

View File

@ -0,0 +1,69 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package misc
import (
"testing"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
"golang.org/x/tools/gopls/internal/lsp/tests/compare"
"golang.org/x/tools/gopls/internal/lsp/protocol"
)
func TestExtractFunction(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
-- main.go --
package main
func Foo() int {
a := 5
return a
}
`
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
start := env.RegexpSearch("main.go", "a := 5").ToProtocolPosition()
end := env.RegexpSearch("main.go", "return a").ToProtocolPosition()
actions, err := env.Editor.CodeAction(env.Ctx, "main.go", &protocol.Range{Start: start, End: end}, nil)
if err != nil {
t.Fatal(err)
}
// Find the extract function code action.
var extractFunc *protocol.CodeAction
for _, action := range actions {
if action.Kind == protocol.RefactorExtract && action.Title == "Extract function" {
extractFunc = &action
break
}
}
if extractFunc == nil {
t.Fatal("could not find extract function action")
}
env.ApplyCodeAction(*extractFunc)
want := `package main
func Foo() int {
a := newFunction()
return a
}
func newFunction() int {
a := 5
return a
}
`
if got := env.Editor.BufferText("main.go"); got != want {
t.Fatalf("TestFillStruct failed:\n%s", compare.Text(want, got))
}
})
}