// 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) }