Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variadic Function With Receiver fails when no argument is passed #1485

Closed
taliesins opened this issue Oct 31, 2022 · 1 comment · Fixed by #1516
Closed

Variadic Function With Receiver fails when no argument is passed #1485

taliesins opened this issue Oct 31, 2022 · 1 comment · Fixed by #1516
Labels
area/core bug Something isn't working

Comments

@taliesins
Copy link

taliesins commented Oct 31, 2022

The following program sample.go triggers an unexpected result

package int

import (
	"fmt"
	"testing"
)

type Logger struct {
}

func (*Logger) WriteWithReceiver(name string, opts ...Option) {
	messageWritten := false
	for _, opt := range opts {
		if !messageWritten {
			messageWritten = true
			opt(name)
		}
	}
}

func WriteWithoutReceiver(name string, opts ...Option) {
	messageWritten := false
	for _, opt := range opts {
		if !messageWritten {
			messageWritten = true
			opt(name)
		}
	}
}

type Option func(name string)

func WithOption() Option {
	return func(name string) {
		fmt.Println(name)
	}
}

func TestVariadicFunctionWithReceiver(t *testing.T) {
	logger := &Logger{}

	//////////// fails here //////////////////
	//This is a relatively popular method
	logger.WriteWithReceiver("WriteWithReceiver - Nothing passed at all fails")
	fmt.Println("WriteWithReceiver - Nothing passed at all fails") //Enumerate empty args will not output a message
	//////////// fails here //////////////////

	logger.WriteWithReceiver("WriteWithReceiver - WithOption() works", WithOption())
	logger.WriteWithReceiver("WriteWithReceiver - WithOption(), WithOption() works", WithOption(), WithOption())
	oneOption := []Option{WithOption()}
	logger.WriteWithReceiver("WriteWithReceiver - []Option{WithOption()} works", oneOption...)
	twoOption := []Option{WithOption(), WithOption()}
	logger.WriteWithReceiver("WriteWithReceiver - []Option{WithOption(), WithOption()} works", twoOption...)
	emptyOption := []Option{}
	logger.WriteWithReceiver("WriteWithReceiver - []Option{} works", emptyOption...)
	fmt.Println("WriteWithReceiver - []Option{} works") //Enumerate empty args will not output a message
	var nilOption []Option
	logger.WriteWithReceiver("WriteWithReceiver - var nilOption []Option works", nilOption...)
	fmt.Println("WriteWithReceiver - var nilOption []Option works") //Enumerate empty args will not output a message
}

func TestVariadicFunctionWithoutReceiver(t *testing.T) {

	WriteWithoutReceiver("WriteWithoutReceiver - WithOption() works", WithOption())
	WriteWithoutReceiver("WriteWithoutReceiver - WithOption(), WithOption() works", WithOption(), WithOption())
	oneOption := []Option{WithOption()}
	WriteWithoutReceiver("WriteWithoutReceiver - []Option{WithOption()} works", oneOption...)
	twoOption := []Option{WithOption(), WithOption()}
	WriteWithoutReceiver("WriteWithoutReceiver - []Option{WithOption(), WithOption()} works", twoOption...)
	emptyOption := []Option{}
	WriteWithoutReceiver("WriteWithoutReceiver - []Option{} works", emptyOption...)
	fmt.Println("WriteWithoutReceiver - []Option{} works") //Enumerate empty args will not output a message
	var nilOption []Option
	WriteWithoutReceiver("WriteWithoutReceiver - var nilOption []Option works", nilOption...)
	fmt.Println("WriteWithoutReceiver - var nilOption []Option works") //Enumerate empty args will not output a message
	WriteWithoutReceiver("WriteWithoutReceiver - Nothing passed at all works")
	fmt.Println("WriteWithoutReceiver - Nothing passed at all works") //Enumerate empty args will not output a message
}

Expected result

C:\Users\taliesin.sisson\go\src\github.com\taliesins\traefik-plugin-oidc>yaegi test -v github.com/taliesins/traefik-plugin-oidc/int
=== RUN   TestVariadicFunctionWithReceiver
WriteWithReceiver - Nothing passed at all fails
WriteWithReceiver - WithOption() works
WriteWithReceiver - WithOption(), WithOption() works
WriteWithReceiver - []Option{WithOption()} works
WriteWithReceiver - []Option{WithOption(), WithOption()} works
WriteWithReceiver - []Option{} works
WriteWithReceiver - var nilOption []Option works
--- PASS: TestVariadicFunctionWithReceiver (0.00s)
=== RUN   TestVariadicFunctionWithoutReceiver
WriteWithoutReceiver - WithOption() works
WriteWithoutReceiver - WithOption(), WithOption() works
WriteWithoutReceiver - []Option{WithOption()} works
WriteWithoutReceiver - []Option{WithOption(), WithOption()} works
WriteWithoutReceiver - []Option{} works
WriteWithoutReceiver - var nilOption []Option works
WriteWithoutReceiver - Nothing passed at all works
--- PASS: TestVariadicFunctionWithoutReceiver (0.00s)
PASS

Got

C:\Users\taliesin.sisson\go\src\github.com\taliesins\traefik-plugin-oidc>yaegi test -v github.com/taliesins/traefik-plugin-oidc/int
=== RUN   TestVariadicFunctionWithReceiver
C:\Users\taliesin.sisson\go\src\github.com\taliesins\traefik-plugin-oidc\int\do_test.go:40:13: panic
--- FAIL: TestVariadicFunctionWithReceiver (0.00s)
panic: reflect: Call using *struct {} as type string [recovered]
        panic: reflect: Call using *struct {} as type string [recovered]
        panic: reflect: Call using *struct {} as type string

goroutine 7 [running]:
testing.tRunner.func1.2({0x12c2be0, 0xc0004184f0})
        C:/Program Files/Go/src/testing/testing.go:1396 +0x24e
testing.tRunner.func1()
        C:/Program Files/Go/src/testing/testing.go:1399 +0x39f
panic({0x12c2be0, 0xc0004184f0})
        C:/Program Files/Go/src/runtime/panic.go:884 +0x212
github.com/traefik/yaegi/interp.runCfg.func1()
        C:/Users/taliesin.sisson/go/pkg/mod/github.com/traefik/yaegi@v0.14.3/interp/run.go:192 +0x148
panic({0x12c2be0, 0xc0004184f0})
        C:/Program Files/Go/src/runtime/panic.go:884 +0x212
reflect.Value.call({0xc00008ab40?, 0xc00041b2c0?, 0x970e5f?}, {0x13eb30e, 0x4}, {0xc00041b2f0, 0x2, 0x0?})
        C:/Program Files/Go/src/reflect/value.go:440 +0x1abf
reflect.Value.Call({0xc00008ab40?, 0xc00041b2c0?, 0x9b5bb2?}, {0xc00041b2f0?, 0x13e9360?, 0x1?})
        C:/Program Files/Go/src/reflect/value.go:368 +0xbc
github.com/traefik/yaegi/interp.call.func9.2({0xc00041b2f0?, 0xc00041b2c0?, 0x5?})
        C:/Users/taliesin.sisson/go/pkg/mod/github.com/traefik/yaegi@v0.14.3/interp/run.go:1288 +0x3c
github.com/traefik/yaegi/interp.call.func9(0xc0000cc8f0)
        C:/Users/taliesin.sisson/go/pkg/mod/github.com/traefik/yaegi@v0.14.3/interp/run.go:1303 +0x122f
github.com/traefik/yaegi/interp.runCfg(0xc0003eb440, 0xc0000cc8f0, 0x0?, 0x13d5d40?)
        C:/Users/taliesin.sisson/go/pkg/mod/github.com/traefik/yaegi@v0.14.3/interp/run.go:200 +0x29d
github.com/traefik/yaegi/interp.genFunctionWrapper.func1.1({0xc000009650, 0x1, 0x1?})
        C:/Users/taliesin.sisson/go/pkg/mod/github.com/traefik/yaegi@v0.14.3/interp/run.go:1002 +0x4a5
testing.tRunner(0xc0001a31e0, 0xc00041b050)
        C:/Program Files/Go/src/testing/testing.go:1446 +0x10b
created by testing.(*T).Run
        C:/Program Files/Go/src/testing/testing.go:1493 +0x35f

C:\Users\taliesin.sisson\go\src\github.com\taliesins\traefik-plugin-oidc>

Yaegi Version

v0.14.3

Additional Notes

It is quite a popular use case in structured logging, to make use of a variadic function with a method receiver e.g.

func (log *Logger) Debug(msg string, fields ...encoder.Field) {
...
}
@taliesins
Copy link
Author

@mvertes I think this is something you could possibly fix easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/core bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants