mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 11:35:35 +00:00
a9dc6e07ad
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 #13224 Updates #12912 Signed-off-by: Nick Khyl <nickk@tailscale.com>
222 lines
5.9 KiB
Go
222 lines
5.9 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package tests serves a list of tests for tailscale.com/cmd/viewer.
|
|
package tests
|
|
|
|
import (
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"golang.org/x/exp/constraints"
|
|
"tailscale.com/types/ptr"
|
|
"tailscale.com/types/views"
|
|
)
|
|
|
|
//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
|
|
Pfx netip.Prefix
|
|
}
|
|
|
|
type Map struct {
|
|
Int map[string]int
|
|
SliceInt map[string][]int
|
|
StructPtrWithPtr map[string]*StructWithPtrs
|
|
StructPtrWithoutPtr map[string]*StructWithoutPtrs
|
|
StructWithoutPtr map[string]StructWithoutPtrs
|
|
SlicesWithPtrs map[string][]*StructWithPtrs
|
|
SlicesWithoutPtrs map[string][]*StructWithoutPtrs
|
|
StructWithoutPtrKey map[StructWithoutPtrs]int `json:"-"`
|
|
StructWithPtr map[string]StructWithPtrs
|
|
|
|
// Unsupported views.
|
|
SliceIntPtr map[string][]*int
|
|
PointerKey map[*string]int `json:"-"`
|
|
StructWithPtrKey map[StructWithPtrs]int `json:"-"`
|
|
}
|
|
|
|
type StructWithPtrs struct {
|
|
Value *StructWithoutPtrs
|
|
Int *int
|
|
|
|
NoCloneValue *StructWithoutPtrs `codegen:"noclone"`
|
|
}
|
|
|
|
func (v *StructWithPtrs) String() string { return fmt.Sprintf("%v", v.Int) }
|
|
|
|
func (v *StructWithPtrs) Equal(v2 *StructWithPtrs) bool {
|
|
return v.Value == v2.Value
|
|
}
|
|
|
|
type StructWithSlices struct {
|
|
Values []StructWithoutPtrs
|
|
ValuePointers []*StructWithoutPtrs
|
|
StructPointers []*StructWithPtrs
|
|
|
|
Slice []string
|
|
Prefixes []netip.Prefix
|
|
Data []byte
|
|
|
|
// Unsupported views.
|
|
Structs []StructWithPtrs
|
|
Ints []*int
|
|
}
|
|
|
|
type OnlyGetClone struct {
|
|
SinViewerPorFavor bool
|
|
}
|
|
|
|
type StructWithEmbedded struct {
|
|
A *StructWithPtrs
|
|
StructWithSlices
|
|
}
|
|
|
|
type GenericIntStruct[T constraints.Integer] struct {
|
|
Value T
|
|
Pointer *T
|
|
Slice []T
|
|
Map map[string]T
|
|
|
|
// Unsupported views.
|
|
PtrSlice []*T
|
|
PtrKeyMap map[*T]string `json:"-"`
|
|
PtrValueMap map[string]*T
|
|
SliceMap map[string][]T
|
|
}
|
|
|
|
type BasicType interface {
|
|
~bool | constraints.Integer | constraints.Float | constraints.Complex | ~string
|
|
}
|
|
|
|
type GenericNoPtrsStruct[T StructWithoutPtrs | netip.Prefix | BasicType] struct {
|
|
Value T
|
|
Pointer *T
|
|
Slice []T
|
|
Map map[string]T
|
|
|
|
// Unsupported views.
|
|
PtrSlice []*T
|
|
PtrKeyMap map[*T]string `json:"-"`
|
|
PtrValueMap map[string]*T
|
|
SliceMap map[string][]T
|
|
}
|
|
|
|
type GenericCloneableStruct[T views.ViewCloner[T, V], V views.StructView[T]] struct {
|
|
Value T
|
|
Slice []T
|
|
Map map[string]T
|
|
|
|
// Unsupported views.
|
|
Pointer *T
|
|
PtrSlice []*T
|
|
PtrKeyMap map[*T]string `json:"-"`
|
|
PtrValueMap map[string]*T
|
|
SliceMap map[string][]T
|
|
}
|
|
|
|
// Container is a pre-defined container type, such as a collection, an optional
|
|
// value or a generic wrapper.
|
|
type Container[T any] struct {
|
|
Item T
|
|
}
|
|
|
|
func (c *Container[T]) Clone() *Container[T] {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
if cloner, ok := any(c.Item).(views.Cloner[T]); ok {
|
|
return &Container[T]{cloner.Clone()}
|
|
}
|
|
if !views.ContainsPointers[T]() {
|
|
return ptr.To(*c)
|
|
}
|
|
panic(fmt.Errorf("%T contains pointers, but is not cloneable", c.Item))
|
|
}
|
|
|
|
// ContainerView is a pre-defined readonly view of a Container[T].
|
|
type ContainerView[T views.ViewCloner[T, V], V views.StructView[T]] struct {
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
// character that looks pointy like a pointer.
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
// to callers. You must not let callers be able to mutate it.
|
|
ж *Container[T]
|
|
}
|
|
|
|
func (cv ContainerView[T, V]) Item() V {
|
|
return cv.ж.Item.View()
|
|
}
|
|
|
|
func ContainerViewOf[T views.ViewCloner[T, V], V views.StructView[T]](c *Container[T]) ContainerView[T, V] {
|
|
return ContainerView[T, V]{c}
|
|
}
|
|
|
|
// MapContainer is a predefined map-like container type.
|
|
// Unlike [Container], it has two type parameters, where the value
|
|
// is the second parameter.
|
|
type MapContainer[K comparable, V views.Cloner[V]] struct {
|
|
Items map[K]V
|
|
}
|
|
|
|
func (c *MapContainer[K, V]) Clone() *MapContainer[K, V] {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
var m map[K]V
|
|
if c.Items != nil {
|
|
m = make(map[K]V, len(c.Items))
|
|
for i := range m {
|
|
m[i] = c.Items[i].Clone()
|
|
}
|
|
}
|
|
return &MapContainer[K, V]{m}
|
|
}
|
|
|
|
// MapContainerView is a pre-defined readonly view of a [MapContainer][K, T].
|
|
type MapContainerView[K comparable, T views.ViewCloner[T, V], V views.StructView[T]] struct {
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
// character that looks pointy like a pointer.
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
// to callers. You must not let callers be able to mutate it.
|
|
ж *MapContainer[K, T]
|
|
}
|
|
|
|
func (cv MapContainerView[K, T, V]) Items() views.MapFn[K, T, V] {
|
|
return views.MapFnOf(cv.ж.Items, func(t T) V { return t.View() })
|
|
}
|
|
|
|
func MapContainerViewOf[K comparable, T views.ViewCloner[T, V], V views.StructView[T]](c *MapContainer[K, T]) MapContainerView[K, T, V] {
|
|
return MapContainerView[K, T, V]{c}
|
|
}
|
|
|
|
type GenericBasicStruct[T BasicType] struct {
|
|
Value T
|
|
}
|
|
|
|
type StructWithContainers struct {
|
|
IntContainer Container[int]
|
|
CloneableContainer Container[*StructWithPtrs]
|
|
BasicGenericContainer Container[GenericBasicStruct[int]]
|
|
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
|
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
|
|
}
|