go icon indicating copy to clipboard operation
go copied to clipboard

x/tools/gopls: incorrect varargs handling for add test action

Open IlyasYOY opened this issue 3 weeks ago • 6 comments

What version of Go are you using (go version)?

$ go version
go version go1.25.5 darwin/arm64

Does this issue reproduce with the latest release?

Yes, I checked with: golang.org/x/tools/gopls v0.20.0

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE='on'
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/ilyasyoy/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/ilyasyoy/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/f2/s841xw414zqfscfkvn76cfv40000gn/T/go-build1666804876=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/ilyasyoy/Projects/IlyasYOY/experiments/varargs-generate-test-gopls/go.mod'
GOMODCACHE='/Users/ilyasyoy/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/ilyasyoy/go'
GOPRIVATE=''
GOPROXY='direct'
GOROOT='/opt/homebrew/Cellar/go/1.25.5/libexec'
GOSUMDB='off'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/ilyasyoy/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.25.5/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.25.5'
GOWORK=''
PKG_CONFIG='pkg-config'
uname -v: Darwin Kernel Version 25.1.0: Mon Oct 20 19:34:03 PDT 2025; root:xnu-12377.41.6~2/RELEASE_ARM64_T8112
ProductName:		macOS
ProductVersion:		26.1
BuildVersion:		25B78
lldb --version: lldb-1703.0.234.3
Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1)

What did you do?

  • created a file with the code:
package varargsgeneratetestgopls

import (
	"fmt"
	"strings"
)

func VarArgsFunc(args ...string) string {
	joined := strings.Join(args, ", ")
	return fmt.Sprintf("VarArgsFunc: %s", joined)
}
  • called the add test action.
Image

What did you expect to see?


func TestVarArgsFunc(t *testing.T) {
	tests := []struct {
		name string // description of this test case
		// Named input parameters for target function.
		args []string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := varargsgeneratetestgopls.VarArgsFunc(tt.args...) 
			// TODO: update the condition below to compare got with tt.want.
			if true {
				t.Errorf("VarArgsFunc() = %v, want %v", got, tt.want)
			}
		})
	}
}

What did you see instead?

I've got the test with syntax error:

package varargsgeneratetestgopls_test

import (
	"testing"

	"github.com/IlyasYOY/varargs-generate-test-gopls"
)

func TestVarArgsFunc(t *testing.T) {
	tests := []struct {
		name string // description of this test case
		// Named input parameters for target function.
		args []string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := varargsgeneratetestgopls.VarArgsFunc(tt.args) // <- error here, must be: tt.args... 
			// TODO: update the condition below to compare got with tt.want.
			if true {
				t.Errorf("VarArgsFunc() = %v, want %v", got, tt.want)
			}
		})
	}
}

IlyasYOY avatar Dec 03 '25 19:12 IlyasYOY

I'd be glad to provide the fix for this case. Maybe there are caveats that I need to know in advance, or I can proceed straight to solving the issue?

IlyasYOY avatar Dec 03 '25 19:12 IlyasYOY

cc: @h9jiang. See golang/vscode-go#1594 for original feature issue.

adonovan avatar Dec 04 '25 04:12 adonovan

Thanks for reporting this, I did no catch this in the first place. Really appreciate it!

Most of the code generation is defined in addtest.go.

Particularly, we go through the function signature and figure out each input parameter's type at line 489. While going through the input parameters, we realize the input parameter is type []string.

So we need to handle it as a special case. We need to figure out whether the last input parameter is a purely []string or a []string which is actually sig.Variadic(). The piece of information is not available when we traverse through the input parameters but available from function signature level.

To make this work, we need to have a CL to read the info Variadic, add a special value to the function

type function struct {
	Name    string
	Args    []field
	Results []field
+      isVariadic bool
}

When generating the function call

			{{- /* Input parameters. */ -}}
			(
				{{- range $index, $arg := .Func.Args}}
				{{- if ne $index 0}}, {{end}}
				{{- if .Name}}tt.{{.Name}}{{else}}{{.Value}}{{end}}
				{{- end -}}
+                               // insert "..." if the function is variadic
			)

To make it more comprehensive, you should also do the same for the constructor.

Thank you so much. If you are interested, feel free to draft a CL to us and we will review!

h9jiang avatar Dec 05 '25 01:12 h9jiang

@h9jiang Hello! Thank you for the valuable advice. I prepared the changes and I'll open a PR soon.

UPD. Opened up a PR: https://github.com/golang/tools/pull/609


A little bit off‑topic here. As a heavy user of TDD I am a big fan of black‑box testing and I really appreciate that gopls encourages it.

I have a Draft PR in gotests that implements it: https://github.com/cweill/gotests/pull/199. I've been missing it a lot.

IlyasYOY avatar Dec 05 '25 17:12 IlyasYOY

It is not my original idea but I hope this would be shared here in case it helps.

Writing test for un-exported functions increase the test coverages but sometimes is not convenient for contributions. Imagine a contributor notice a bug and is willing to fix it for you, a single line of change could result in 10+ small test case changes.

Test only the exported function or test only the E2E behavior would encourage such contribution. gopls will prefer generating test function using foo_test package as much as possible.

h9jiang avatar Dec 06 '25 01:12 h9jiang

Change https://go.dev/cl/727280 mentions this issue: gopls/internal/golang: add support for variadic functions and constructors in generated tests

gopherbot avatar Dec 07 '25 15:12 gopherbot