]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/_mkmalloc: add a copy of cloneNode
authorMichael Matloob <matloob@golang.org>
Mon, 17 Mar 2025 15:45:52 +0000 (11:45 -0400)
committerMichael Matloob <matloob@google.com>
Tue, 16 Sep 2025 20:13:54 +0000 (13:13 -0700)
cloneNode is defined in golang.org/x/tools/internal/astutil. Make a copy
of it so we can easily clone AST nodes.

Change-Id: I6a6a6964132e663e64faa00fa6037cf6e8d4cbc6
Reviewed-on: https://go-review.googlesource.com/c/go/+/703396
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Matloob <matloob@google.com>
src/runtime/_mkmalloc/astutil/clone.go [new file with mode: 0644]

diff --git a/src/runtime/_mkmalloc/astutil/clone.go b/src/runtime/_mkmalloc/astutil/clone.go
new file mode 100644 (file)
index 0000000..16ea716
--- /dev/null
@@ -0,0 +1,73 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is a copy of golang.org/x/tools/internal/astutil/clone.go
+
+package astutil
+
+import (
+       "go/ast"
+       "reflect"
+)
+
+// CloneNode returns a deep copy of a Node.
+// It omits pointers to ast.{Scope,Object} variables.
+func CloneNode[T ast.Node](n T) T {
+       return cloneNode(n).(T)
+}
+
+func cloneNode(n ast.Node) ast.Node {
+       var clone func(x reflect.Value) reflect.Value
+       set := func(dst, src reflect.Value) {
+               src = clone(src)
+               if src.IsValid() {
+                       dst.Set(src)
+               }
+       }
+       clone = func(x reflect.Value) reflect.Value {
+               switch x.Kind() {
+               case reflect.Pointer:
+                       if x.IsNil() {
+                               return x
+                       }
+                       // Skip fields of types potentially involved in cycles.
+                       switch x.Interface().(type) {
+                       case *ast.Object, *ast.Scope:
+                               return reflect.Zero(x.Type())
+                       }
+                       y := reflect.New(x.Type().Elem())
+                       set(y.Elem(), x.Elem())
+                       return y
+
+               case reflect.Struct:
+                       y := reflect.New(x.Type()).Elem()
+                       for i := 0; i < x.Type().NumField(); i++ {
+                               set(y.Field(i), x.Field(i))
+                       }
+                       return y
+
+               case reflect.Slice:
+                       if x.IsNil() {
+                               return x
+                       }
+                       y := reflect.MakeSlice(x.Type(), x.Len(), x.Cap())
+                       for i := 0; i < x.Len(); i++ {
+                               set(y.Index(i), x.Index(i))
+                       }
+                       return y
+
+               case reflect.Interface:
+                       y := reflect.New(x.Type()).Elem()
+                       set(y, x.Elem())
+                       return y
+
+               case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer:
+                       panic(x) // unreachable in AST
+
+               default:
+                       return x // bool, string, number
+               }
+       }
+       return clone(reflect.ValueOf(n)).Interface().(ast.Node)
+}