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

Unexpected behavior: embedded generic interface require their type arguments to be interfaces #113

Open
danvolchek opened this issue Jul 22, 2024 · 3 comments

Comments

@danvolchek
Copy link

danvolchek commented Jul 22, 2024

When using this file:

//go:generate minimock -i Foo

type Foo interface {
    Bar[Baz]
}

type Bar[T any] interface {
    DoBar(T)
}

type Baz struct {
    // fields
}

The resulting error is:

minimock: failed to parse interface declaration: Baz: embedded type is not an interface

Why does Baz need to be an interface? As a type parameter to Bar it doesn't matter what type it is - the generated code would be the same - right? Am I misunderstanding?

Thanks!

@danvolchek
Copy link
Author

danvolchek commented Jul 22, 2024

Seems like the error originates here:

https://github.com/hexdigest/gowrap/blob/2c275b2bff7eafa9ac70c46d97785c88f8ceffc1/generator/generator.go#L614-L638

var errNotAnInterface = errors.New("embedded type is not an interface")

func processIdent(i *ast.Ident, input targetProcessInput) (methodsList, error) {
	var embeddedInterface *ast.InterfaceType
	var genericsTypes genericTypes
	for _, t := range input.types {
		if t.Name.Name == i.Name {
			var ok bool
			embeddedInterface, ok = t.Type.(*ast.InterfaceType)
			if !ok {
				return nil, errors.Wrap(errNotAnInterface, t.Name.Name)
			}

			genericsTypes = buildGenericTypesFromSpec(t, input.types, input.typesPrefix)
			break
		}
	}

	if embeddedInterface == nil {
		return nil, nil
	}

	input.genericTypes = genericsTypes
	return processInterface(embeddedInterface, input)
}

This also fails with the same error:

//go:generate minimock -i Foo

type Foo interface {
    Baz
}

type Baz struct {
  // fields
}

Maybe the parser thinks that Baz is a type directly embedded inside Foo? That seems like a bug.

Even in that case, since Go.18, arbitrary types can be embedded into interfaces (https://go.dev/ref/spec#Go_1.18):

Interface types may embed arbitrary types (not just type names of interfaces) as well as union and ~T type elements.

Should I open an issue for gowrap?

@danvolchek
Copy link
Author

danvolchek commented Jul 22, 2024

Also, using an interface as the type argument rather than the struct generates code, but it's not correct.

The generated file has a bunch of references to *T, but T is undefined and should be the name of the type argument.

E.g. in funcDoBar func() tp1 *T T is supposed to Baz.

Seems like gowrap isn't handling embedded interfaces correctly.

@zcolleen
Copy link
Collaborator

zcolleen commented Sep 2, 2024

Hey @danvolchek thank for issue! Lets clarify few things.
The first example with

type Foo interface {
    Bar[Baz]
}

is a clear bug, i agree.
But second example im not sure about how to behave with such interfaces that are used as constraints, not as usual interface. Probably we should just skip generation for such interfaces.

The last point about E.g. in funcDoBar func() tp1 *T T is supposed to Baz. i didnt get, could you please provide an example of interface that is generated incorrectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants