package ast_test

import (
	"testing"

	"github.com/microsoft/typescript-go/internal/ast"
	"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
	"gotest.tools/v3/assert"
)

type NodeComparisonWorkItem struct {
	original *ast.Node
	copy     *ast.Node
}

func getChildren(node *ast.Node) []*ast.Node {
	children := []*ast.Node{}
	node.VisitEachChild(ast.NewNodeVisitor(func(node *ast.Node) *ast.Node {
		children = append(children, node)
		return node
	}, nil, ast.NodeVisitorHooks{}))
	return children
}

func TestDeepCloneNodeSanityCheck(t *testing.T) {
	t.Parallel()
	data := []struct {
		title string
		input string
		jsx   bool
	}{
		{title: "StringLiteral#1", input: `;"test"`},
		{title: "StringLiteral#2", input: `;'test'`},
		{title: "NumericLiteral", input: `0`},
		{title: "BigIntLiteral", input: `0n`},
		{title: "BooleanLiteral#1", input: `true`},
		{title: "BooleanLiteral#2", input: `false`},
		{title: "NoSubstitutionTemplateLiteral", input: "``"},
		{title: "RegularExpressionLiteral#1", input: `/a/`},
		{title: "RegularExpressionLiteral#2", input: `/a/g`},
		{title: "NullLiteral", input: `null`},
		{title: "ThisExpression", input: `this`},
		{title: "SuperExpression", input: `super()`},
		{title: "ImportExpression", input: `import()`},
		{title: "PropertyAccess#1", input: `a.b`},
		{title: "PropertyAccess#2", input: `a.#b`},
		{title: "PropertyAccess#3", input: `a?.b`},
		{title: "PropertyAccess#4", input: `a?.b.c`},
		{title: "PropertyAccess#5", input: `1..b`},
		{title: "PropertyAccess#6", input: `1.0.b`},
		{title: "PropertyAccess#7", input: `0x1.b`},
		{title: "PropertyAccess#8", input: `0b1.b`},
		{title: "PropertyAccess#9", input: `0o1.b`},
		{title: "PropertyAccess#10", input: `10e1.b`},
		{title: "PropertyAccess#11", input: `10E1.b`},
		{title: "ElementAccess#1", input: `a[b]`},
		{title: "ElementAccess#2", input: `a?.[b]`},
		{title: "ElementAccess#3", input: `a?.[b].c`},
		{title: "CallExpression#1", input: `a()`},
		{title: "CallExpression#2", input: `a<T>()`},
		{title: "CallExpression#3", input: `a(b)`},
		{title: "CallExpression#4", input: `a<T>(b)`},
		{title: "CallExpression#5", input: `a(b).c`},
		{title: "CallExpression#6", input: `a<T>(b).c`},
		{title: "CallExpression#7", input: `a?.(b)`},
		{title: "CallExpression#8", input: `a?.<T>(b)`},
		{title: "CallExpression#9", input: `a?.(b).c`},
		{title: "CallExpression#10", input: `a?.<T>(b).c`},
		{title: "CallExpression#11", input: `a<T, U>()`},
		{title: "CallExpression#12", input: `a<T,>()`},
		{title: "NewExpression#1", input: `new a`},
		{title: "NewExpression#2", input: `new a.b`},
		{title: "NewExpression#3", input: `new a()`},
		{title: "NewExpression#4", input: `new a.b()`},
		{title: "NewExpression#5", input: `new a<T>()`},
		{title: "NewExpression#6", input: `new a.b<T>()`},
		{title: "NewExpression#7", input: `new a(b)`},
		{title: "NewExpression#8", input: `new a.b(c)`},
		{title: "NewExpression#9", input: `new a<T>(b)`},
		{title: "NewExpression#10", input: `new a.b<T>(c)`},
		{title: "NewExpression#11", input: `new a(b).c`},
		{title: "NewExpression#12", input: `new a<T>(b).c`},
		{title: "TaggedTemplateExpression#1", input: "tag``"},
		{title: "TaggedTemplateExpression#2", input: "tag<T>``"},
		{title: "TypeAssertionExpression#1", input: `<T>a`},
		{title: "FunctionExpression#1", input: `(function(){})`},
		{title: "FunctionExpression#2", input: `(function f(){})`},
		{title: "FunctionExpression#3", input: `(function*f(){})`},
		{title: "FunctionExpression#4", input: `(async function f(){})`},
		{title: "FunctionExpression#5", input: `(async function*f(){})`},
		{title: "FunctionExpression#6", input: `(function<T>(){})`},
		{title: "FunctionExpression#7", input: `(function(a){})`},
		{title: "FunctionExpression#8", input: `(function():T{})`},
		{title: "ArrowFunction#1", input: `a=>{}`},
		{title: "ArrowFunction#2", input: `()=>{}`},
		{title: "ArrowFunction#3", input: `(a)=>{}`},
		{title: "ArrowFunction#4", input: `<T>(a)=>{}`},
		{title: "ArrowFunction#5", input: `async a=>{}`},
		{title: "ArrowFunction#6", input: `async()=>{}`},
		{title: "ArrowFunction#7", input: `async<T>()=>{}`},
		{title: "ArrowFunction#8", input: `():T=>{}`},
		{title: "ArrowFunction#9", input: `()=>a`},
		{title: "DeleteExpression", input: `delete a`},
		{title: "TypeOfExpression", input: `typeof a`},
		{title: "VoidExpression", input: `void a`},
		{title: "AwaitExpression", input: `await a`},
		{title: "PrefixUnaryExpression#1", input: `+a`},
		{title: "PrefixUnaryExpression#2", input: `++a`},
		{title: "PrefixUnaryExpression#3", input: `+ +a`},
		{title: "PrefixUnaryExpression#4", input: `+ ++a`},
		{title: "PrefixUnaryExpression#5", input: `-a`},
		{title: "PrefixUnaryExpression#6", input: `--a`},
		{title: "PrefixUnaryExpression#7", input: `- -a`},
		{title: "PrefixUnaryExpression#8", input: `- --a`},
		{title: "PrefixUnaryExpression#9", input: `+-a`},
		{title: "PrefixUnaryExpression#10", input: `+--a`},
		{title: "PrefixUnaryExpression#11", input: `-+a`},
		{title: "PrefixUnaryExpression#12", input: `-++a`},
		{title: "PrefixUnaryExpression#13", input: `~a`},
		{title: "PrefixUnaryExpression#14", input: `!a`},
		{title: "PostfixUnaryExpression#1", input: `a++`},
		{title: "PostfixUnaryExpression#2", input: `a--`},
		{title: "BinaryExpression#1", input: `a,b`},
		{title: "BinaryExpression#2", input: `a+b`},
		{title: "BinaryExpression#3", input: `a**b`},
		{title: "BinaryExpression#4", input: `a instanceof b`},
		{title: "BinaryExpression#5", input: `a in b`},
		{title: "ConditionalExpression", input: `a?b:c`},
		{title: "TemplateExpression#1", input: "`a${b}c`"},
		{title: "TemplateExpression#2", input: "`a${b}c${d}e`"},
		{title: "YieldExpression#1", input: `(function*() { yield })`},
		{title: "YieldExpression#2", input: `(function*() { yield a })`},
		{title: "YieldExpression#3", input: `(function*() { yield*a })`},
		{title: "SpreadElement", input: `[...a]`},
		{title: "ClassExpression#1", input: `(class {})`},
		{title: "ClassExpression#2", input: `(class a {})`},
		{title: "ClassExpression#3", input: `(class<T>{})`},
		{title: "ClassExpression#4", input: `(class a<T>{})`},
		{title: "ClassExpression#5", input: `(class extends b {})`},
		{title: "ClassExpression#6", input: `(class a extends b {})`},
		{title: "ClassExpression#7", input: `(class implements b {})`},
		{title: "ClassExpression#8", input: `(class a implements b {})`},
		{title: "ClassExpression#9", input: `(class implements b, c {})`},
		{title: "ClassExpression#10", input: `(class a implements b, c {})`},
		{title: "ClassExpression#11", input: `(class extends b implements c, d {})`},
		{title: "ClassExpression#12", input: `(class a extends b implements c, d {})`},
		{title: "ClassExpression#13", input: `(@a class {})`},
		{title: "OmittedExpression", input: `[,]`},
		{title: "ExpressionWithTypeArguments", input: `a<T>`},
		{title: "AsExpression", input: `a as T`},
		{title: "SatisfiesExpression", input: `a satisfies T`},
		{title: "NonNullExpression", input: `a!`},
		{title: "MetaProperty#1", input: `new.target`},
		{title: "MetaProperty#2", input: `import.meta`},
		{title: "ArrayLiteralExpression#1", input: `[]`},
		{title: "ArrayLiteralExpression#2", input: `[a]`},
		{title: "ArrayLiteralExpression#3", input: `[a,]`},
		{title: "ArrayLiteralExpression#4", input: `[,a]`},
		{title: "ArrayLiteralExpression#5", input: `[...a]`},
		{title: "ObjectLiteralExpression#1", input: `({})`},
		{title: "ObjectLiteralExpression#2", input: `({a,})`},
		{title: "ShorthandPropertyAssignment", input: `({a})`},
		{title: "PropertyAssignment", input: `({a:b})`},
		{title: "SpreadAssignment", input: `({...a})`},
		{title: "Block", input: `{}`},
		{title: "VariableStatement#1", input: `var a`},
		{title: "VariableStatement#2", input: `let a`},
		{title: "VariableStatement#3", input: `const a = b`},
		{title: "VariableStatement#4", input: `using a = b`},
		{title: "VariableStatement#5", input: `await using a = b`},
		{title: "EmptyStatement", input: `;`},
		{title: "IfStatement#1", input: `if(a);`},
		{title: "IfStatement#2", input: `if(a);else;`},
		{title: "IfStatement#3", input: `if(a);else{}`},
		{title: "IfStatement#4", input: `if(a);else if(b);`},
		{title: "IfStatement#5", input: `if(a);else if(b) {}`},
		{title: "IfStatement#6", input: `if(a) {}`},
		{title: "IfStatement#7", input: `if(a) {} else;`},
		{title: "IfStatement#8", input: `if(a) {} else {}`},
		{title: "IfStatement#9", input: `if(a) {} else if(b);`},
		{title: "IfStatement#10", input: `if(a) {} else if(b){}`},
		{title: "DoStatement#1", input: `do;while(a);`},
		{title: "DoStatement#2", input: `do {} while(a);`},
		{title: "WhileStatement#1", input: `while(a);`},
		{title: "WhileStatement#2", input: `while(a) {}`},
		{title: "ForStatement#1", input: `for(;;);`},
		{title: "ForStatement#2", input: `for(a;;);`},
		{title: "ForStatement#3", input: `for(var a;;);`},
		{title: "ForStatement#4", input: `for(;a;);`},
		{title: "ForStatement#5", input: `for(;;a);`},
		{title: "ForStatement#6", input: `for(;;){}`},
		{title: "ForInStatement#1", input: `for(a in b);`},
		{title: "ForInStatement#2", input: `for(var a in b);`},
		{title: "ForInStatement#3", input: `for(a in b){}`},
		{title: "ForOfStatement#1", input: `for(a of b);`},
		{title: "ForOfStatement#2", input: `for(var a of b);`},
		{title: "ForOfStatement#3", input: `for(a of b){}`},
		{title: "ForOfStatement#4", input: `for await(a of b);`},
		{title: "ForOfStatement#5", input: `for await(var a of b);`},
		{title: "ForOfStatement#6", input: `for await(a of b){}`},
		{title: "ContinueStatement#1", input: `continue`},
		{title: "ContinueStatement#2", input: `continue a`},
		{title: "BreakStatement#1", input: `break`},
		{title: "BreakStatement#2", input: `break a`},
		{title: "ReturnStatement#1", input: `return`},
		{title: "ReturnStatement#2", input: `return a`},
		{title: "WithStatement#1", input: `with(a);`},
		{title: "WithStatement#2", input: `with(a){}`},
		{title: "SwitchStatement", input: `switch (a) {}`},
		{title: "CaseClause#1", input: `switch (a) {case b:}`},
		{title: "CaseClause#2", input: `switch (a) {case b:;}`},
		{title: "DefaultClause#1", input: `switch (a) {default:}`},
		{title: "DefaultClause#2", input: `switch (a) {default:;}`},
		{title: "LabeledStatement", input: `a:;`},
		{title: "ThrowStatement", input: `throw a`},
		{title: "TryStatement#1", input: `try {} catch {}`},
		{title: "TryStatement#2", input: `try {} finally {}`},
		{title: "TryStatement#3", input: `try {} catch {} finally {}`},
		{title: "DebuggerStatement", input: `debugger`},
		{title: "FunctionDeclaration#1", input: `export default function(){}`},
		{title: "FunctionDeclaration#2", input: `function f(){}`},
		{title: "FunctionDeclaration#3", input: `function*f(){}`},
		{title: "FunctionDeclaration#4", input: `async function f(){}`},
		{title: "FunctionDeclaration#5", input: `async function*f(){}`},
		{title: "FunctionDeclaration#6", input: `function f<T>(){}`},
		{title: "FunctionDeclaration#7", input: `function f(a){}`},
		{title: "FunctionDeclaration#8", input: `function f():T{}`},
		{title: "FunctionDeclaration#9", input: `function f();`},
		{title: "ClassDeclaration#1", input: `class a {}`},
		{title: "ClassDeclaration#2", input: `class a<T>{}`},
		{title: "ClassDeclaration#3", input: `class a extends b {}`},
		{title: "ClassDeclaration#4", input: `class a implements b {}`},
		{title: "ClassDeclaration#5", input: `class a implements b, c {}`},
		{title: "ClassDeclaration#6", input: `class a extends b implements c, d {}`},
		{title: "ClassDeclaration#7", input: `export default class {}`},
		{title: "ClassDeclaration#8", input: `export default class<T>{}`},
		{title: "ClassDeclaration#9", input: `export default class extends b {}`},
		{title: "ClassDeclaration#10", input: `export default class implements b {}`},
		{title: "ClassDeclaration#11", input: `export default class implements b, c {}`},
		{title: "ClassDeclaration#12", input: `export default class extends b implements c, d {}`},
		{title: "ClassDeclaration#13", input: `@a class b {}`},
		{title: "ClassDeclaration#14", input: `@a export class b {}`},
		{title: "ClassDeclaration#15", input: `export @a class b {}`},
		{title: "InterfaceDeclaration#1", input: `interface a {}`},
		{title: "InterfaceDeclaration#2", input: `interface a<T>{}`},
		{title: "InterfaceDeclaration#3", input: `interface a extends b {}`},
		{title: "InterfaceDeclaration#4", input: `interface a extends b, c {}`},
		{title: "TypeAliasDeclaration#1", input: `type a = b`},
		{title: "TypeAliasDeclaration#2", input: `type a<T> = b`},
		{title: "EnumDeclaration#1", input: `enum a{}`},
		{title: "EnumDeclaration#2", input: `enum a{b}`},
		{title: "EnumDeclaration#3", input: `enum a{b=c}`},
		{title: "ModuleDeclaration#1", input: `module a{}`},
		{title: "ModuleDeclaration#2", input: `module a.b{}`},
		{title: "ModuleDeclaration#3", input: `module "a";`},
		{title: "ModuleDeclaration#4", input: `module "a"{}`},
		{title: "ModuleDeclaration#5", input: `namespace a{}`},
		{title: "ModuleDeclaration#6", input: `namespace a.b{}`},
		{title: "ModuleDeclaration#7", input: `global;`},
		{title: "ModuleDeclaration#8", input: `global{}`},
		{title: "ImportEqualsDeclaration#1", input: `import a = b`},
		{title: "ImportEqualsDeclaration#2", input: `import a = b.c`},
		{title: "ImportEqualsDeclaration#3", input: `import a = require("b")`},
		{title: "ImportEqualsDeclaration#4", input: `export import a = b`},
		{title: "ImportEqualsDeclaration#5", input: `export import a = require("b")`},
		{title: "ImportEqualsDeclaration#6", input: `import type a = b`},
		{title: "ImportEqualsDeclaration#7", input: `import type a = b.c`},
		{title: "ImportEqualsDeclaration#8", input: `import type a = require("b")`},
		{title: "ImportDeclaration#1", input: `import "a"`},
		{title: "ImportDeclaration#2", input: `import a from "b"`},
		{title: "ImportDeclaration#3", input: `import type a from "b"`},
		{title: "ImportDeclaration#4", input: `import * as a from "b"`},
		{title: "ImportDeclaration#5", input: `import type * as a from "b"`},
		{title: "ImportDeclaration#6", input: `import {} from "b"`},
		{title: "ImportDeclaration#7", input: `import type {} from "b"`},
		{title: "ImportDeclaration#8", input: `import { a } from "b"`},
		{title: "ImportDeclaration#9", input: `import type { a } from "b"`},
		{title: "ImportDeclaration#8", input: `import { a as b } from "c"`},
		{title: "ImportDeclaration#9", input: `import type { a as b } from "c"`},
		{title: "ImportDeclaration#10", input: `import { "a" as b } from "c"`},
		{title: "ImportDeclaration#11", input: `import type { "a" as b } from "c"`},
		{title: "ImportDeclaration#12", input: `import a, {} from "b"`},
		{title: "ImportDeclaration#13", input: `import a, * as b from "c"`},
		{title: "ImportDeclaration#14", input: `import {} from "a" with {}`},
		{title: "ImportDeclaration#15", input: `import {} from "a" with { b: "c" }`},
		{title: "ImportDeclaration#16", input: `import {} from "a" with { "b": "c" }`},
		{title: "ExportAssignment#1", input: `export = a`},
		{title: "ExportAssignment#2", input: `export default a`},
		{title: "NamespaceExportDeclaration", input: `export as namespace a`},
		{title: "ExportDeclaration#1", input: `export * from "a"`},
		{title: "ExportDeclaration#2", input: `export type * from "a"`},
		{title: "ExportDeclaration#3", input: `export * as a from "b"`},
		{title: "ExportDeclaration#4", input: `export type * as a from "b"`},
		{title: "ExportDeclaration#5", input: `export { } from "a"`},
		{title: "ExportDeclaration#6", input: `export type { } from "a"`},
		{title: "ExportDeclaration#7", input: `export { a } from "b"`},
		{title: "ExportDeclaration#8", input: `export { type a } from "b"`},
		{title: "ExportDeclaration#9", input: `export type { a } from "b"`},
		{title: "ExportDeclaration#10", input: `export { a as b } from "c"`},
		{title: "ExportDeclaration#11", input: `export { type a as b } from "c"`},
		{title: "ExportDeclaration#12", input: `export type { a as b } from "c"`},
		{title: "ExportDeclaration#13", input: `export { a as "b" } from "c"`},
		{title: "ExportDeclaration#14", input: `export { type a as "b" } from "c"`},
		{title: "ExportDeclaration#15", input: `export type { a as "b" } from "c"`},
		{title: "ExportDeclaration#16", input: `export { "a" } from "b"`},
		{title: "ExportDeclaration#17", input: `export { type "a" } from "b"`},
		{title: "ExportDeclaration#18", input: `export type { "a" } from "b"`},
		{title: "ExportDeclaration#19", input: `export { "a" as b } from "c"`},
		{title: "ExportDeclaration#20", input: `export { type "a" as b } from "c"`},
		{title: "ExportDeclaration#21", input: `export type { "a" as b } from "c"`},
		{title: "ExportDeclaration#22", input: `export { "a" as "b" } from "c"`},
		{title: "ExportDeclaration#23", input: `export { type "a" as "b" } from "c"`},
		{title: "ExportDeclaration#24", input: `export type { "a" as "b" } from "c"`},
		{title: "ExportDeclaration#25", input: `export { }`},
		{title: "ExportDeclaration#26", input: `export type { }`},
		{title: "ExportDeclaration#27", input: `export { a }`},
		{title: "ExportDeclaration#28", input: `export { type a }`},
		{title: "ExportDeclaration#29", input: `export type { a }`},
		{title: "ExportDeclaration#30", input: `export { a as b }`},
		{title: "ExportDeclaration#31", input: `export { type a as b }`},
		{title: "ExportDeclaration#32", input: `export type { a as b }`},
		{title: "ExportDeclaration#33", input: `export { a as "b" }`},
		{title: "ExportDeclaration#34", input: `export { type a as "b" }`},
		{title: "ExportDeclaration#35", input: `export type { a as "b" }`},
		{title: "ExportDeclaration#36", input: `export {} from "a" with {}`},
		{title: "ExportDeclaration#37", input: `export {} from "a" with { b: "c" }`},
		{title: "ExportDeclaration#38", input: `export {} from "a" with { "b": "c" }`},
		{title: "KeywordTypeNode#1", input: `type T = any`},
		{title: "KeywordTypeNode#2", input: `type T = unknown`},
		{title: "KeywordTypeNode#3", input: `type T = never`},
		{title: "KeywordTypeNode#4", input: `type T = void`},
		{title: "KeywordTypeNode#5", input: `type T = undefined`},
		{title: "KeywordTypeNode#6", input: `type T = null`},
		{title: "KeywordTypeNode#7", input: `type T = object`},
		{title: "KeywordTypeNode#8", input: `type T = string`},
		{title: "KeywordTypeNode#9", input: `type T = symbol`},
		{title: "KeywordTypeNode#10", input: `type T = number`},
		{title: "KeywordTypeNode#11", input: `type T = bigint`},
		{title: "KeywordTypeNode#12", input: `type T = boolean`},
		{title: "KeywordTypeNode#13", input: `type T = intrinsic`},
		{title: "TypePredicateNode#1", input: `function f(): asserts a`},
		{title: "TypePredicateNode#2", input: `function f(): asserts a is b`},
		{title: "TypePredicateNode#3", input: `function f(): asserts this`},
		{title: "TypePredicateNode#4", input: `function f(): asserts this is b`},
		{title: "TypeReferenceNode#1", input: `type T = a`},
		{title: "TypeReferenceNode#2", input: `type T = a.b`},
		{title: "TypeReferenceNode#3", input: `type T = a<U>`},
		{title: "TypeReferenceNode#4", input: `type T = a.b<U>`},
		{title: "FunctionTypeNode#1", input: `type T = () => a`},
		{title: "FunctionTypeNode#2", input: `type T = <T>() => a`},
		{title: "FunctionTypeNode#3", input: `type T = (a) => b`},
		{title: "ConstructorTypeNode#1", input: `type T = new () => a`},
		{title: "ConstructorTypeNode#2", input: `type T = new <T>() => a`},
		{title: "ConstructorTypeNode#3", input: `type T = new (a) => b`},
		{title: "ConstructorTypeNode#4", input: `type T = abstract new () => a`},
		{title: "TypeQueryNode#1", input: `type T = typeof a`},
		{title: "TypeQueryNode#2", input: `type T = typeof a.b`},
		{title: "TypeQueryNode#3", input: `type T = typeof a<U>`},
		{title: "TypeLiteralNode#1", input: `type T = {}`},
		{title: "TypeLiteralNode#2", input: `type T = {a}`},
		{title: "ArrayTypeNode", input: `type T = a[]`},
		{title: "TupleTypeNode#1", input: `type T = []`},
		{title: "TupleTypeNode#2", input: `type T = [a]`},
		{title: "TupleTypeNode#3", input: `type T = [a,]`},
		{title: "RestTypeNode", input: `type T = [...a]`},
		{title: "OptionalTypeNode", input: `type T = [a?]`},
		{title: "NamedTupleMember#1", input: `type T = [a: b]`},
		{title: "NamedTupleMember#2", input: `type T = [a?: b]`},
		{title: "NamedTupleMember#3", input: `type T = [...a: b]`},
		{title: "UnionTypeNode#1", input: `type T = a | b`},
		{title: "UnionTypeNode#2", input: `type T = a | b | c`},
		{title: "UnionTypeNode#3", input: `type T = | a | b`},
		{title: "IntersectionTypeNode#1", input: `type T = a & b`},
		{title: "IntersectionTypeNode#2", input: `type T = a & b & c`},
		{title: "IntersectionTypeNode#3", input: `type T = & a & b`},
		{title: "ConditionalTypeNode", input: `type T = a extends b ? c : d`},
		{title: "InferTypeNode#1", input: `type T = a extends infer b ? c : d`},
		{title: "InferTypeNode#2", input: `type T = a extends infer b extends c ? d : e`},
		{title: "ParenthesizedTypeNode", input: `type T = (U)`},
		{title: "ThisTypeNode", input: `type T = this`},
		{title: "TypeOperatorNode#1", input: `type T = keyof U`},
		{title: "TypeOperatorNode#2", input: `type T = readonly U[]`},
		{title: "TypeOperatorNode#3", input: `type T = unique symbol`},
		{title: "IndexedAccessTypeNode", input: `type T = a[b]`},
		{title: "MappedTypeNode#1", input: `type T = { [a in b]: c }`},
		{title: "MappedTypeNode#2", input: `type T = { [a in b as c]: d }`},
		{title: "MappedTypeNode#3", input: `type T = { readonly [a in b]: c }`},
		{title: "MappedTypeNode#4", input: `type T = { +readonly [a in b]: c }`},
		{title: "MappedTypeNode#5", input: `type T = { -readonly [a in b]: c }`},
		{title: "MappedTypeNode#6", input: `type T = { [a in b]?: c }`},
		{title: "MappedTypeNode#7", input: `type T = { [a in b]+?: c }`},
		{title: "MappedTypeNode#8", input: `type T = { [a in b]-?: c }`},
		{title: "MappedTypeNode#9", input: `type T = { [a in b]: c; d }`},
		{title: "LiteralTypeNode#1", input: `type T = null`},
		{title: "LiteralTypeNode#2", input: `type T = true`},
		{title: "LiteralTypeNode#3", input: `type T = false`},
		{title: "LiteralTypeNode#4", input: `type T = ""`},
		{title: "LiteralTypeNode#5", input: "type T = ''"},
		{title: "LiteralTypeNode#6", input: "type T = ``"},
		{title: "LiteralTypeNode#7", input: `type T = 0`},
		{title: "LiteralTypeNode#8", input: `type T = 0n`},
		{title: "LiteralTypeNode#9", input: `type T = -0`},
		{title: "LiteralTypeNode#10", input: `type T = -0n`},
		{title: "TemplateTypeNode#1", input: "type T = `a${b}c`"},
		{title: "TemplateTypeNode#2", input: "type T = `a${b}c${d}e`"},
		{title: "ImportTypeNode#1", input: `type T = import(a)`},
		{title: "ImportTypeNode#2", input: `type T = import(a).b`},
		{title: "ImportTypeNode#3", input: `type T = import(a).b<U>`},
		{title: "ImportTypeNode#4", input: `type T = typeof import(a)`},
		{title: "ImportTypeNode#5", input: `type T = typeof import(a).b`},
		{title: "ImportTypeNode#6", input: `type T = import(a, { with: { } })`},
		{title: "ImportTypeNode#6", input: `type T = import(a, { with: { b: "c" } })`},
		{title: "ImportTypeNode#7", input: `type T = import(a, { with: { "b": "c" } })`},
		{title: "PropertySignature#1", input: "interface I {a}"},
		{title: "PropertySignature#2", input: "interface I {readonly a}"},
		{title: "PropertySignature#3", input: "interface I {\"a\"}"},
		{title: "PropertySignature#4", input: "interface I {'a'}"},
		{title: "PropertySignature#5", input: "interface I {0}"},
		{title: "PropertySignature#6", input: "interface I {0n}"},
		{title: "PropertySignature#7", input: "interface I {[a]}"},
		{title: "PropertySignature#8", input: "interface I {a?}"},
		{title: "PropertySignature#9", input: "interface I {a: b}"},
		{title: "MethodSignature#1", input: "interface I {a()}"},
		{title: "MethodSignature#2", input: "interface I {\"a\"()}"},
		{title: "MethodSignature#3", input: "interface I {'a'()}"},
		{title: "MethodSignature#4", input: "interface I {0()}"},
		{title: "MethodSignature#5", input: "interface I {0n()}"},
		{title: "MethodSignature#6", input: "interface I {[a]()}"},
		{title: "MethodSignature#7", input: "interface I {a?()}"},
		{title: "MethodSignature#8", input: "interface I {a<T>()}"},
		{title: "MethodSignature#9", input: "interface I {a(): b}"},
		{title: "MethodSignature#10", input: "interface I {a(b): c}"},
		{title: "CallSignature#1", input: "interface I {()}"},
		{title: "CallSignature#2", input: "interface I {():a}"},
		{title: "CallSignature#3", input: "interface I {(p)}"},
		{title: "CallSignature#4", input: "interface I {<T>()}"},
		{title: "ConstructSignature#1", input: "interface I {new ()}"},
		{title: "ConstructSignature#2", input: "interface I {new ():a}"},
		{title: "ConstructSignature#3", input: "interface I {new (p)}"},
		{title: "ConstructSignature#4", input: "interface I {new <T>()}"},
		{title: "IndexSignatureDeclaration#1", input: "interface I {[a]}"},
		{title: "IndexSignatureDeclaration#2", input: "interface I {[a: b]}"},
		{title: "IndexSignatureDeclaration#3", input: "interface I {[a: b]: c}"},
		{title: "PropertyDeclaration#1", input: "class C {a}"},
		{title: "PropertyDeclaration#2", input: "class C {readonly a}"},
		{title: "PropertyDeclaration#3", input: "class C {static a}"},
		{title: "PropertyDeclaration#4", input: "class C {accessor a}"},
		{title: "PropertyDeclaration#5", input: "class C {\"a\"}"},
		{title: "PropertyDeclaration#6", input: "class C {'a'}"},
		{title: "PropertyDeclaration#7", input: "class C {0}"},
		{title: "PropertyDeclaration#8", input: "class C {0n}"},
		{title: "PropertyDeclaration#9", input: "class C {[a]}"},
		{title: "PropertyDeclaration#10", input: "class C {#a}"},
		{title: "PropertyDeclaration#11", input: "class C {a?}"},
		{title: "PropertyDeclaration#12", input: "class C {a!}"},
		{title: "PropertyDeclaration#13", input: "class C {a: b}"},
		{title: "PropertyDeclaration#14", input: "class C {a = b}"},
		{title: "PropertyDeclaration#15", input: "class C {@a b}"},
		{title: "MethodDeclaration#1", input: "class C {a()}"},
		{title: "MethodDeclaration#2", input: "class C {\"a\"()}"},
		{title: "MethodDeclaration#3", input: "class C {'a'()}"},
		{title: "MethodDeclaration#4", input: "class C {0()}"},
		{title: "MethodDeclaration#5", input: "class C {0n()}"},
		{title: "MethodDeclaration#6", input: "class C {[a]()}"},
		{title: "MethodDeclaration#7", input: "class C {#a()}"},
		{title: "MethodDeclaration#8", input: "class C {a?()}"},
		{title: "MethodDeclaration#9", input: "class C {a<T>()}"},
		{title: "MethodDeclaration#10", input: "class C {a(): b}"},
		{title: "MethodDeclaration#11", input: "class C {a(b): c}"},
		{title: "MethodDeclaration#12", input: "class C {a() {} }"},
		{title: "MethodDeclaration#13", input: "class C {@a b() {} }"},
		{title: "MethodDeclaration#14", input: "class C {static a() {} }"},
		{title: "MethodDeclaration#15", input: "class C {async a() {} }"},
		{title: "GetAccessorDeclaration#1", input: "class C {get a()}"},
		{title: "GetAccessorDeclaration#2", input: "class C {get \"a\"()}"},
		{title: "GetAccessorDeclaration#3", input: "class C {get 'a'()}"},
		{title: "GetAccessorDeclaration#4", input: "class C {get 0()}"},
		{title: "GetAccessorDeclaration#5", input: "class C {get 0n()}"},
		{title: "GetAccessorDeclaration#6", input: "class C {get [a]()}"},
		{title: "GetAccessorDeclaration#7", input: "class C {get #a()}"},
		{title: "GetAccessorDeclaration#8", input: "class C {get a(): b}"},
		{title: "GetAccessorDeclaration#9", input: "class C {get a(b): c}"},
		{title: "GetAccessorDeclaration#10", input: "class C {get a() {} }"},
		{title: "GetAccessorDeclaration#11", input: "class C {@a get b() {} }"},
		{title: "GetAccessorDeclaration#12", input: "class C {static get a() {} }"},
		{title: "SetAccessorDeclaration#1", input: "class C {set a()}"},
		{title: "SetAccessorDeclaration#2", input: "class C {set \"a\"()}"},
		{title: "SetAccessorDeclaration#3", input: "class C {set 'a'()}"},
		{title: "SetAccessorDeclaration#4", input: "class C {set 0()}"},
		{title: "SetAccessorDeclaration#5", input: "class C {set 0n()}"},
		{title: "SetAccessorDeclaration#6", input: "class C {set [a]()}"},
		{title: "SetAccessorDeclaration#7", input: "class C {set #a()}"},
		{title: "SetAccessorDeclaration#8", input: "class C {set a(): b}"},
		{title: "SetAccessorDeclaration#9", input: "class C {set a(b): c}"},
		{title: "SetAccessorDeclaration#10", input: "class C {set a() {} }"},
		{title: "SetAccessorDeclaration#11", input: "class C {@a set b() {} }"},
		{title: "SetAccessorDeclaration#12", input: "class C {static set a() {} }"},
		{title: "ConstructorDeclaration#1", input: "class C {constructor()}"},
		{title: "ConstructorDeclaration#2", input: "class C {constructor(): b}"},
		{title: "ConstructorDeclaration#3", input: "class C {constructor(b): c}"},
		{title: "ConstructorDeclaration#4", input: "class C {constructor() {} }"},
		{title: "ConstructorDeclaration#5", input: "class C {@a constructor() {} }"},
		{title: "ConstructorDeclaration#6", input: "class C {private constructor() {} }"},
		{title: "ClassStaticBlockDeclaration", input: "class C {static { }}"},
		{title: "SemicolonClassElement#1", input: "class C {;}"},
		{title: "ParameterDeclaration#1", input: "function f(a)"},
		{title: "ParameterDeclaration#2", input: "function f(a: b)"},
		{title: "ParameterDeclaration#3", input: "function f(a = b)"},
		{title: "ParameterDeclaration#4", input: "function f(a?)"},
		{title: "ParameterDeclaration#5", input: "function f(...a)"},
		{title: "ParameterDeclaration#6", input: "function f(this)"},
		{title: "ParameterDeclaration#7", input: "function f(a,)"},
		{title: "ObjectBindingPattern#1", input: "function f({})"},
		{title: "ObjectBindingPattern#2", input: "function f({a})"},
		{title: "ObjectBindingPattern#3", input: "function f({a = b})"},
		{title: "ObjectBindingPattern#4", input: "function f({a: b})"},
		{title: "ObjectBindingPattern#5", input: "function f({a: b = c})"},
		{title: "ObjectBindingPattern#6", input: "function f({\"a\": b})"},
		{title: "ObjectBindingPattern#7", input: "function f({'a': b})"},
		{title: "ObjectBindingPattern#8", input: "function f({0: b})"},
		{title: "ObjectBindingPattern#9", input: "function f({[a]: b})"},
		{title: "ObjectBindingPattern#10", input: "function f({...a})"},
		{title: "ObjectBindingPattern#11", input: "function f({a: {}})"},
		{title: "ObjectBindingPattern#12", input: "function f({a: []})"},
		{title: "ArrayBindingPattern#1", input: "function f([])"},
		{title: "ArrayBindingPattern#2", input: "function f([,])"},
		{title: "ArrayBindingPattern#3", input: "function f([a])"},
		{title: "ArrayBindingPattern#4", input: "function f([a, b])"},
		{title: "ArrayBindingPattern#5", input: "function f([a, , b])"},
		{title: "ArrayBindingPattern#6", input: "function f([a = b])"},
		{title: "ArrayBindingPattern#7", input: "function f([...a])"},
		{title: "ArrayBindingPattern#8", input: "function f([{}])"},
		{title: "ArrayBindingPattern#9", input: "function f([[]])"},
		{title: "TypeParameterDeclaration#1", input: "function f<T>();"},
		{title: "TypeParameterDeclaration#2", input: "function f<in T>();"},
		{title: "TypeParameterDeclaration#3", input: "function f<T extends U>();"},
		{title: "TypeParameterDeclaration#4", input: "function f<T = U>();"},
		{title: "TypeParameterDeclaration#5", input: "function f<T extends U = V>();"},
		{title: "TypeParameterDeclaration#6", input: "function f<T, U>();"},
		{title: "TypeParameterDeclaration#7", input: "function f<T,>();"},
		{title: "JsxElement1", input: "<a></a>"},
		{title: "JsxElement2", input: "<this></this>"},
		{title: "JsxElement3", input: "<a:b></a:b>"},
		{title: "JsxElement4", input: "<a.b></a.b>"},
		{title: "JsxElement5", input: "<a<b>></a>"},
		{title: "JsxElement6", input: "<a b></a>"},
		{title: "JsxElement7", input: "<a>b</a>"},
		{title: "JsxElement8", input: "<a>{b}</a>"},
		{title: "JsxElement9", input: "<a><b></b></a>"},
		{title: "JsxElement10", input: "<a><b /></a>"},
		{title: "JsxElement11", input: "<a><></></a>"},
		{title: "JsxSelfClosingElement1", input: "<a />"},
		{title: "JsxSelfClosingElement2", input: "<this />"},
		{title: "JsxSelfClosingElement3", input: "<a:b />"},
		{title: "JsxSelfClosingElement4", input: "<a.b />"},
		{title: "JsxSelfClosingElement5", input: "<a<b> />"},
		{title: "JsxSelfClosingElement6", input: "<a b/>"},
		{title: "JsxFragment1", input: "<></>"},
		{title: "JsxFragment2", input: "<>b</>"},
		{title: "JsxFragment3", input: "<>{b}</>"},
		{title: "JsxFragment4", input: "<><b></b></>"},
		{title: "JsxFragment5", input: "<><b /></>"},
		{title: "JsxFragment6", input: "<><></></>"},
		{title: "JsxAttribute1", input: "<a b/>"},
		{title: "JsxAttribute2", input: "<a b:c/>"},
		{title: "JsxAttribute3", input: "<a b=\"c\"/>"},
		{title: "JsxAttribute4", input: "<a b='c'/>"},
		{title: "JsxAttribute5", input: "<a b={c}/>"},
		{title: "JsxAttribute6", input: "<a b=<c></c>/>"},
		{title: "JsxAttribute7", input: "<a b=<c />/>"},
		{title: "JsxAttribute8", input: "<a b=<></>/>"},
		{title: "JsxSpreadAttribute", input: "<a {...b}/>"},
	}
	for _, rec := range data {
		t.Run("Clone "+rec.title, func(t *testing.T) {
			t.Parallel()

			factory := &ast.NodeFactory{}
			file := parsetestutil.ParseTypeScript(rec.input, false).AsNode()
			clone := factory.DeepCloneNode(file.AsNode()).AsNode()

			work := []NodeComparisonWorkItem{{file, clone}}

			for len(work) > 0 {
				nextWork := []NodeComparisonWorkItem{}
				for _, item := range work {
					assert.Assert(t, item.original != item.copy)
					originalChildren := getChildren(item.original)
					copyChildren := getChildren(item.copy)
					assert.Equal(t, len(originalChildren), len(copyChildren))
					for i, child := range originalChildren {
						nextWork = append(nextWork, NodeComparisonWorkItem{child, copyChildren[i]})
					}
				}
				work = nextWork
			}
		})
	}
}
