mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-04 19:56:35 +02:00
The cloner and viewer code generators didn't handle named types
with basic underlying types (map/slice) that have their own Clone
or View methods. For example, a type like:
type Map map[string]any
func (m Map) Clone() Map { ... }
func (m Map) View() MapView { ... }
When used as a struct field, the cloner would descend into the
underlying map[string]any and fail because it can't clone the any
(interface{}) value type. Similarly, the viewer would try to create
a MapFnOf view and fail.
Fix the cloner to check for a Clone method on the named type
before falling through to the underlying type handling.
Fix the viewer to check for a View method on named map/slice types,
so the type author can provide a purpose-built safe view that
doesn't leak raw any values. Named map/slice types without a View
method fall through to normal handling, which correctly rejects
types like map[string]any as unsupported.
Updates tailscale/corp#39502 (needed by tailscale/corp#39594)
Change-Id: Iaef0192a221e02b4b8e409c99ef8398090327744
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
68 lines
1.7 KiB
Go
68 lines
1.7 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type SliceContainer,InterfaceContainer,MapWithPointers,DeeplyNestedMap,NamedMapContainer
|
|
|
|
// Package clonerex is an example package for the cloner tool.
|
|
package clonerex
|
|
|
|
type SliceContainer struct {
|
|
Slice []*int
|
|
}
|
|
|
|
// Cloneable is an interface with a Clone method.
|
|
type Cloneable interface {
|
|
Clone() Cloneable
|
|
}
|
|
|
|
// CloneableImpl is a concrete type that implements Cloneable.
|
|
type CloneableImpl struct {
|
|
Value int
|
|
}
|
|
|
|
func (c *CloneableImpl) Clone() Cloneable {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return &CloneableImpl{Value: c.Value}
|
|
}
|
|
|
|
// InterfaceContainer has a pointer to an interface field, which tests
|
|
// the special handling for interface types in the cloner.
|
|
type InterfaceContainer struct {
|
|
Interface Cloneable
|
|
}
|
|
|
|
type MapWithPointers struct {
|
|
Nested map[string]*int
|
|
WithCloneMethod map[string]*SliceContainer
|
|
CloneInterface map[string]Cloneable
|
|
}
|
|
|
|
// NamedMap is a named map type with its own Clone method.
|
|
// This tests that the cloner uses the type's Clone method
|
|
// rather than trying to descend into the map's value type.
|
|
type NamedMap map[string]any
|
|
|
|
func (m NamedMap) Clone() NamedMap {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
m2 := make(NamedMap, len(m))
|
|
for k, v := range m {
|
|
m2[k] = v
|
|
}
|
|
return m2
|
|
}
|
|
|
|
// NamedMapContainer has a field whose type is a named map with a Clone method.
|
|
type NamedMapContainer struct {
|
|
Attrs NamedMap
|
|
}
|
|
|
|
// DeeplyNestedMap tests arbitrary depth of map nesting (3+ levels)
|
|
type DeeplyNestedMap struct {
|
|
ThreeLevels map[string]map[string]map[string]int
|
|
FourLevels map[string]map[string]map[string]map[string]*SliceContainer
|
|
}
|