Skip to content

Commit

Permalink
util/codegen, cmd/cloner, cmd/viewer: update codegen.LookupMethod to …
Browse files Browse the repository at this point in the history
…support alias type nodes

Go 1.23 updates the go/types package to produce Alias type nodes for type aliases, unless disabled with gotypesalias=0.
This new default behavior breaks codegen.LookupMethod, which uses checked type assertions to types.Named and
types.Interface, as only named types and interfaces have methods.

In this PR, we update codegen.LookupMethod to perform method lookup on the right-hand side of the alias declaration
and clearly switch on the supported type nodes types. We also improve support for various edge cases, such as when an alias
is used as a type parameter constraint, and add tests for the LookupMethod function.

Additionally, we update cmd/viewer/tests to include types with aliases used in type fields and generic type constraints.

Updates tailscale#13224
Updates tailscale#12912

Signed-off-by: Nick Khyl <[email protected]>
  • Loading branch information
nickkhyl committed Aug 23, 2024
1 parent aa42ae9 commit a9dc6e0
Show file tree
Hide file tree
Showing 7 changed files with 393 additions and 24 deletions.
2 changes: 1 addition & 1 deletion cmd/cloner/cloner.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func main() {
it := codegen.NewImportTracker(pkg.Types)
buf := new(bytes.Buffer)
for _, typeName := range typeNames {
typ, ok := namedTypes[typeName]
typ, ok := namedTypes[typeName].(*types.Named)
if !ok {
log.Fatalf("could not find type %s", typeName)
}
Expand Down
19 changes: 18 additions & 1 deletion cmd/viewer/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"tailscale.com/types/views"
)

//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices,OnlyGetClone,StructWithEmbedded,GenericIntStruct,GenericNoPtrsStruct,GenericCloneableStruct,StructWithContainers --clone-only-type=OnlyGetClone
//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices,OnlyGetClone,StructWithEmbedded,GenericIntStruct,GenericNoPtrsStruct,GenericCloneableStruct,StructWithContainers,StructWithTypeAliasFields,GenericTypeAliasStruct --clone-only-type=OnlyGetClone

type StructWithoutPtrs struct {
Int int
Expand Down Expand Up @@ -202,3 +202,20 @@ type StructWithContainers struct {
CloneableMap MapContainer[int, *StructWithPtrs]
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
}

type (
StructWithPtrsAlias = StructWithPtrs
StructWithoutPtrsAlias = StructWithoutPtrs
)

type StructWithTypeAliasFields struct {
WithPtr StructWithPtrsAlias
WithoutPtr StructWithoutPtrsAlias
}

type integer = constraints.Integer

type GenericTypeAliasStruct[T integer, T2 views.ViewCloner[T2, V2], V2 views.StructView[T2]] struct {
NonCloneable T
Cloneable T2
}
38 changes: 38 additions & 0 deletions cmd/viewer/tests/tests_clone.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 113 additions & 1 deletion cmd/viewer/tests/tests_view.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/viewer/viewer.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ func main() {
if cloneOnlyType[typeName] {
continue
}
typ, ok := namedTypes[typeName]
typ, ok := namedTypes[typeName].(*types.Named)
if !ok {
log.Fatalf("could not find type %s", typeName)
}
Expand Down
36 changes: 23 additions & 13 deletions util/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
var flagCopyright = flag.Bool("copyright", true, "add Tailscale copyright to generated file headers")

// LoadTypes returns all named types in pkgName, keyed by their type name.
func LoadTypes(buildTags string, pkgName string) (*packages.Package, map[string]*types.Named, error) {
func LoadTypes(buildTags string, pkgName string) (*packages.Package, map[string]types.Type, error) {
cfg := &packages.Config{
Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedName,
Tests: buildTags == "test",
Expand Down Expand Up @@ -181,8 +181,8 @@ func writeFormatted(code []byte, path string) error {
}

// namedTypes returns all named types in pkg, keyed by their type name.
func namedTypes(pkg *packages.Package) map[string]*types.Named {
nt := make(map[string]*types.Named)
func namedTypes(pkg *packages.Package) map[string]types.Type {
nt := make(map[string]types.Type)
for _, file := range pkg.Syntax {
for _, d := range file.Decls {
decl, ok := d.(*ast.GenDecl)
Expand All @@ -198,11 +198,10 @@ func namedTypes(pkg *packages.Package) map[string]*types.Named {
if !ok {
continue
}
typ, ok := typeNameObj.Type().(*types.Named)
if !ok {
continue
switch typ := typeNameObj.Type(); typ.(type) {
case *types.Alias, *types.Named:
nt[spec.Name.Name] = typ
}
nt[spec.Name.Name] = typ
}
}
}
Expand Down Expand Up @@ -356,14 +355,25 @@ func FormatTypeParams(params *types.TypeParamList, it *ImportTracker) (constrain

// LookupMethod returns the method with the specified name in t, or nil if the method does not exist.
func LookupMethod(t types.Type, name string) *types.Func {
if t, ok := t.(*types.Named); ok {
for i := 0; i < t.NumMethods(); i++ {
if method := t.Method(i); method.Name() == name {
return method
switch t := t.(type) {
case *types.Alias:
return LookupMethod(t.Rhs(), name)
case *types.TypeParam:
return LookupMethod(t.Constraint(), name)
case *types.Pointer:
return LookupMethod(t.Elem(), name)
case *types.Named:
switch u := t.Underlying().(type) {
case *types.Interface:
return LookupMethod(u, name)
default:
for i := 0; i < t.NumMethods(); i++ {
if method := t.Method(i); method.Name() == name {
return method
}
}
}
}
if t, ok := t.Underlying().(*types.Interface); ok {
case *types.Interface:
for i := 0; i < t.NumMethods(); i++ {
if method := t.Method(i); method.Name() == name {
return method
Expand Down
Loading

0 comments on commit a9dc6e0

Please sign in to comment.