blob: d5ee82c58b20d8d526210f2d59f5a77ca4ff70d0 [file] [log] [blame]
// 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.
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.Ptr:
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)
}