cmd/cloner, cmd/viewer: handle named map/slice types with Clone/View methods

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>
This commit is contained in:
Brad Fitzpatrick
2026-04-05 22:56:53 +00:00
committed by Brad Fitzpatrick
parent 5a899e406d
commit 86f42ea87b
10 changed files with 440 additions and 6 deletions
+34
View File
@@ -563,3 +563,37 @@ func (src *StructWithMapOfViews) Clone() *StructWithMapOfViews {
var _StructWithMapOfViewsCloneNeedsRegeneration = StructWithMapOfViews(struct {
MapOfViews map[string]StructWithoutPtrsView
}{})
// Clone makes a deep copy of StructWithNamedMap.
// The result aliases no memory with the original.
func (src *StructWithNamedMap) Clone() *StructWithNamedMap {
if src == nil {
return nil
}
dst := new(StructWithNamedMap)
*dst = *src
dst.Attrs = src.Attrs.Clone()
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _StructWithNamedMapCloneNeedsRegeneration = StructWithNamedMap(struct {
Attrs NamedMap
}{})
// Clone makes a deep copy of StructWithNamedSlice.
// The result aliases no memory with the original.
func (src *StructWithNamedSlice) Clone() *StructWithNamedSlice {
if src == nil {
return nil
}
dst := new(StructWithNamedSlice)
*dst = *src
dst.Items = src.Items.Clone()
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _StructWithNamedSliceCloneNeedsRegeneration = StructWithNamedSlice(struct {
Items NamedSlice
}{})