gopls/internal/golang/completion: avoid SEGV from double deslicing
Completing at the cursor in
func f[T ~[]int](x T) {var s string; _ = append(x, append(<cursor>x, x...)...)
causes an extra deslicing in expectedCallParamType which makes the
expected param type as both variadic and nil which produces []nil
as the inner append's type, which gets matched against s's type
for completion, which seems to cause the panic.
The fix is to avoid slicing if there's a ..., in which case a slice is
being passed directly, so the code doesn't need to deslice.
Fixes: golang/go#74564
Change-Id: I4771417438969ba494ac51283c7932bbf26cb117
Reviewed-on: https://go-review.googlesource.com/c/tools/+/809600
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Hongxiang Jiang <hxjiang@golang.org>
diff --git a/gopls/internal/golang/completion/builtin.go b/gopls/internal/golang/completion/builtin.go
index 68f773e..d465b84 100644
--- a/gopls/internal/golang/completion/builtin.go
+++ b/gopls/internal/golang/completion/builtin.go
@@ -91,10 +91,11 @@
break
}
- inf.objType = deslice(inf.objType)
-
- // Check if we are completing the variadic append() param.
- inf.variadic = exprIdx == 1 && len(call.Args) <= 2
+ if !call.Ellipsis.IsValid() {
+ inf.objType = deslice(inf.objType)
+ // Check if we are completing the variadic append() param. (TestIssue74564)
+ inf.variadic = exprIdx == 1 && len(call.Args) <= 2
+ }
// Penalize the first append() argument as a candidate. You
// don't normally append a slice to itself.
diff --git a/gopls/internal/golang/completion/completion.go b/gopls/internal/golang/completion/completion.go
index f9f53b7..ed146c0 100644
--- a/gopls/internal/golang/completion/completion.go
+++ b/gopls/internal/golang/completion/completion.go
@@ -2723,7 +2723,7 @@
inf.objType = sig.Params().At(exprIdx).Type()
}
- if sig.Variadic() && exprIdx >= (numParams-1) {
+ if sig.Variadic() && exprIdx >= (numParams-1) && !node.Ellipsis.IsValid() {
// If we are completing a variadic param, deslice the variadic type.
inf.objType = deslice(inf.objType)
// Record whether we are completing the initial variadic param.
diff --git a/gopls/internal/test/integration/completion/completion_test.go b/gopls/internal/test/integration/completion/completion_test.go
index 0fcd8a6..045b859 100644
--- a/gopls/internal/test/integration/completion/completion_test.go
+++ b/gopls/internal/test/integration/completion/completion_test.go
@@ -1697,3 +1697,25 @@
env.Completion(loc)
})
}
+
+func TestIssue74564(t *testing.T) {
+ const src = `
+-- go.mod --
+module mod.com
+go 1.22
+-- main.go --
+package main
+
+func f[T ~[]int](x T) {
+ var s string
+ _ = append(x, append(x, x...)...)
+}
+`
+ Run(t, src, func(t *testing.T, env *Env) {
+ env.OpenFile("main.go")
+ env.Await(env.DoneWithOpen())
+ // Place cursor inside the first 'x' of the inner append call.
+ loc := env.RegexpSearch("main.go", `append\(x, append\(()x,`)
+ env.Completion(loc)
+ })
+}