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

Fix Go 1.23 types.Alias handling #808

Merged
merged 8 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ func (g *Generator) renderType(ctx context.Context, typ types.Type) string {
args = append(args, g.renderType(ctx, arg))
}
return fmt.Sprintf("%s[%s]", name, strings.Join(args, ","))
case *types.Alias:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually surprised that CI/CD is not failing in the go1.21 test. This didn't exist until 1.22 so technically this won't compile if someone tries to build with 1.21.

If I'm understanding that correctly, we will need to make sure people understand. But I'd also like to figure out why CICD didn't fail.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, go will download at least 1.22 because of our go.mod

$ /opt/homebrew/opt/go@1.21/bin/go version
go: downloading go1.22.0 (darwin/arm64)
go version go1.22.0 darwin/arm64

Also 1.21 is not supported anymore anyways, so we can probably remove it from the matrix and figure out how to get 1.23 to work in the test matrix.

Warning: go@1.21 has been deprecated because it is not supported upstream! It will be disabled on 2025-08-16.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a CI test with go 1.23 enabled, but it requires the lastest golangci-lint, but installing it forces the library's go.mod to go 1.23.

return g.renderTypeAlias(ctx, t)
case *types.TypeParam:
if t.Constraint() != nil {
name := t.Obj().Name()
Expand Down
12 changes: 12 additions & 0 deletions pkg/generator_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !go1.23

package pkg

import (
"context"
"go/types"
)

func (g *Generator) renderTypeAlias(ctx context.Context, t *types.Alias) string {
return g.getPackageScopedType(ctx, t.Obj())
}
23 changes: 23 additions & 0 deletions pkg/generator_alias_go123.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build go1.23

package pkg

import (
"context"
"fmt"
"go/types"
"strings"
)

func (g *Generator) renderTypeAlias(ctx context.Context, t *types.Alias) string {
name := g.getPackageScopedType(ctx, t.Obj())
if t.TypeArgs() == nil || t.TypeArgs().Len() == 0 {
return name
}
args := make([]string, 0, t.TypeArgs().Len())
for i := 0; i < t.TypeArgs().Len(); i++ {
arg := t.TypeArgs().At(i)
args = append(args, g.renderType(ctx, arg))
}
return fmt.Sprintf("%s[%s]", name, strings.Join(args, ","))
}
115 changes: 115 additions & 0 deletions pkg/generator_go123_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//go:build go1.23

package pkg

import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"regexp"
)

func (s *GeneratorSuite) TestReplaceTypePackagePrologueGo123() {
if !isTypeAliasEnabled() {
// "go 1.22" in go.mod makes gotypesalias=0 even when compiling with Go 1.23.
// Remove this when upgrading to Go 1.23 in go.mod.
return
}

expected := `package mocks

import baz "github.com/vektra/mockery/v2/pkg/fixtures/example_project/baz"
import mock "github.com/stretchr/testify/mock"

`
generator := NewGenerator(
s.ctx,
GeneratorConfig{InPackage: false},
s.getInterfaceFromFile("example_project/baz/foo.go", "Foo"),
pkg,
)

s.checkPrologueGeneration(generator, expected)
}

func (s *GeneratorSuite) TestReplaceTypePackageGo123() {
if !isTypeAliasEnabled() {
// "go 1.22" in go.mod makes gotypesalias=0 even when compiling with Go 1.23.
// Remove this when upgrading to Go 1.23 in go.mod.
return
}

cfg := GeneratorConfig{InPackage: false}

s.checkGenerationRegexWithConfig("example_project/baz/foo.go", "Foo", cfg, []regexpExpected{
// func (_m *Foo) GetBaz() (*baz.Baz, error)
{true, regexp.MustCompile(`func \([^\)]+\) GetBaz\(\) \(\*baz\.Baz`)},
// func (_m *Foo) GetBaz() (*foo.InternalBaz, error)
{false, regexp.MustCompile(`func \([^\)]+\) GetBaz\(\) \(\*foo\.InternalBaz`)},
})
}

func (s *GeneratorSuite) TestReplaceTypePackageMultiplePrologueGo123() {
if !isTypeAliasEnabled() {
// "go 1.22" in go.mod makes gotypesalias=0 even when compiling with Go 1.23.
// Remove this when upgrading to Go 1.23 in go.mod.
return
}

expected := `package mocks

import mock "github.com/stretchr/testify/mock"
import replace_type "github.com/vektra/mockery/v2/pkg/fixtures/example_project/replace_type"
import rt1 "github.com/vektra/mockery/v2/pkg/fixtures/example_project/replace_type/rti/rt1"
import rt2 "github.com/vektra/mockery/v2/pkg/fixtures/example_project/replace_type/rti/rt2"

`
generator := NewGenerator(
s.ctx,
GeneratorConfig{InPackage: false},
s.getInterfaceFromFile("example_project/replace_type/rt.go", "RType"),
pkg,
)

s.checkPrologueGeneration(generator, expected)
}

func (s *GeneratorSuite) TestReplaceTypePackageMultipleGo123() {
if !isTypeAliasEnabled() {
// "go 1.22" in go.mod makes gotypesalias=0 even when compiling with Go 1.23.
// Remove this when upgrading to Go 1.23 in go.mod.
return
}

cfg := GeneratorConfig{InPackage: false}

s.checkGenerationRegexWithConfig("example_project/replace_type/rt.go", "RType", cfg, []regexpExpected{
// func (_m *RType) Replace1(f rt1.RType1)
{true, regexp.MustCompile(`func \([^\)]+\) Replace1\(f rt1\.RType1`)},
// func (_m *RType) Replace2(f rt2.RType2)
{true, regexp.MustCompile(`func \([^\)]+\) Replace2\(f rt2\.RType2`)},
})
}

// isTypeAliasEnabled reports whether [NewAlias] should create [types.Alias] types.
//
// This function is expensive! Call it sparingly.
// source: /go/1.23.0/libexec/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go
func isTypeAliasEnabled() bool {
// The only reliable way to compute the answer is to invoke go/types.
// We don't parse the GODEBUG environment variable, because
// (a) it's tricky to do so in a manner that is consistent
// with the godebug package; in particular, a simple
// substring check is not good enough. The value is a
// rightmost-wins list of options. But more importantly:
// (b) it is impossible to detect changes to the effective
// setting caused by os.Setenv("GODEBUG"), as happens in
// many tests. Therefore any attempt to cache the result
// is just incorrect.
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
return enabled
}
Loading