package ast

import (
	"fmt"
	"iter"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/microsoft/typescript-go/internal/collections"
	"github.com/microsoft/typescript-go/internal/core"
	"github.com/microsoft/typescript-go/internal/stringutil"
	"github.com/microsoft/typescript-go/internal/tspath"
	"github.com/zeebo/xxh3"
)

// parseJSDocForNode is the package-level function for lazily parsing JSDoc.
// It is set by the parser package via init().
var parseJSDocForNode func(*SourceFile, *Node) []*Node

// SetParseJSDocForNode registers the lazy JSDoc parse function. Called from parser's init().
func SetParseJSDocForNode(fn func(*SourceFile, *Node) []*Node) {
	parseJSDocForNode = fn
}

// Visitor

type Visitor func(*Node) bool

func visit(v Visitor, node *Node) bool {
	if node != nil {
		return v(node)
	}
	return false
}

func visitNodes(v Visitor, nodes []*Node) bool {
	for _, node := range nodes { //nolint:modernize
		if v(node) {
			return true
		}
	}
	return false
}

func visitNodeList(v Visitor, nodeList *NodeList) bool {
	if nodeList != nil {
		return visitNodes(v, nodeList.Nodes)
	}
	return false
}

func visitModifiers(v Visitor, modifiers *ModifierList) bool {
	if modifiers != nil {
		return visitNodes(v, modifiers.Nodes)
	}
	return false
}

type NodeFactoryHooks struct {
	OnCreate func(node *Node)                 // Hooks the creation of a node.
	OnUpdate func(node *Node, original *Node) // Hooks the updating of a node.
	OnClone  func(node *Node, original *Node) // Hooks the cloning of a node.
}

type NodeFactoryCoercible interface {
	AsNodeFactory() *NodeFactory
}

func NewNodeFactory(hooks NodeFactoryHooks) *NodeFactory {
	return &NodeFactory{hooks: hooks}
}

func newNode(kind Kind, data nodeData, hooks NodeFactoryHooks) *Node {
	n := data.AsNode()
	n.Loc = core.UndefinedTextRange()
	n.Kind = kind
	n.data = data
	if hooks.OnCreate != nil {
		hooks.OnCreate(n)
	}
	return n
}

func (f *NodeFactory) newNode(kind Kind, data nodeData) *Node {
	f.nodeCount++
	return newNode(kind, data, f.hooks)
}

func (f *NodeFactory) NodeCount() int {
	return f.nodeCount
}

func (f *NodeFactory) TextCount() int {
	return f.textCount
}

func (f *NodeFactory) AsNodeFactory() *NodeFactory {
	return f
}

func updateNode(updated *Node, original *Node, hooks NodeFactoryHooks) *Node {
	if updated != original {
		updated.Flags = original.Flags
		updated.Loc = original.Loc
		if hooks.OnUpdate != nil {
			hooks.OnUpdate(updated, original)
		}
	}
	return updated
}

func cloneNode(updated *Node, original *Node, hooks NodeFactoryHooks) *Node {
	updateNode(updated, original, hooks)
	if updated != original && hooks.OnClone != nil {
		hooks.OnClone(updated, original)
	}
	return updated
}

// NodeList

type NodeList struct {
	Loc   core.TextRange
	Nodes []*Node
}

func (f *NodeFactory) NewNodeList(nodes []*Node) *NodeList {
	list := f.nodeListArena.New()
	list.Loc = core.UndefinedTextRange()
	list.Nodes = nodes
	return list
}

func (list *NodeList) Pos() int { return list.Loc.Pos() }
func (list *NodeList) End() int { return list.Loc.End() }

func (list *NodeList) HasTrailingComma() bool {
	if len(list.Nodes) == 0 {
		return false
	}
	last := list.Nodes[len(list.Nodes)-1]
	return last.End() < list.End()
}

func (list *NodeList) Clone(f NodeFactoryCoercible) *NodeList {
	result := f.AsNodeFactory().NewNodeList(list.Nodes)
	result.Loc = list.Loc
	return result
}

// ModifierList

type ModifierList struct {
	NodeList
	ModifierFlags ModifierFlags
}

func (f *NodeFactory) NewModifierList(nodes []*Node) *ModifierList {
	list := f.modifierListArena.New()
	list.Loc = core.UndefinedTextRange()
	list.Nodes = nodes
	list.ModifierFlags = ModifiersToFlags(nodes)
	return list
}

func (list *ModifierList) Clone(f *NodeFactory) *ModifierList {
	res := f.modifierListArena.New()
	res.Loc = list.Loc
	res.Nodes = list.Nodes
	res.ModifierFlags = list.ModifierFlags
	return res
}

// AST Node
// Interface values stored in AST nodes are never typed nil values. Construction code must ensure that
// interface valued properties either store a true nil or a reference to a non-nil struct.

type Node struct {
	Kind   Kind
	Flags  NodeFlags
	Loc    core.TextRange
	id     atomic.Uint64
	Parent *Node
	data   nodeData
}

// Node accessors. Some accessors are implemented as methods on NodeData, others are implemented though
// type switches. Either approach is fine. Interface methods are likely more performant, but have higher
// code size costs because we have hundreds of implementations of the NodeData interface.

func (n *Node) AsNode() *Node { return n }
func (n *Node) Pos() int      { return n.Loc.Pos() }
func (n *Node) End() int      { return n.Loc.End() }
func (n *Node) IterChildren() iter.Seq[*Node] {
	// Implemented directly (rather than through the nodeData interface) so that the
	// returned iterator and the visitor closure it passes to ForEachChild do not
	// escape: an interface call is opaque to escape analysis. `true` stops a TS
	// visitor early, whereas `false` stops a Go iterator yield, so the result is
	// inverted.
	return func(yield func(*Node) bool) {
		n.ForEachChild(func(child *Node) bool {
			return !yield(child)
		})
	}
}
func (n *Node) Clone(f NodeFactoryCoercible) *Node        { return n.data.Clone(f) }
func (n *Node) VisitEachChild(v *NodeVisitor) *Node       { return n.data.VisitEachChild(v) }
func (n *Node) Name() *DeclarationName                    { return n.data.Name() }
func (n *Node) Modifiers() *ModifierList                  { return n.data.Modifiers() }
func (n *Node) FlowNodeData() *FlowNodeBase               { return n.data.FlowNodeData() }
func (n *Node) DeclarationData() *DeclarationBase         { return n.data.DeclarationData() }
func (n *Node) ExportableData() *ExportableBase           { return n.data.ExportableData() }
func (n *Node) LocalsContainerData() *LocalsContainerBase { return n.data.LocalsContainerData() }
func (n *Node) FunctionLikeData() *FunctionLikeBase       { return n.data.FunctionLikeData() }
func (n *Node) ParameterList() *ParameterList             { return n.data.FunctionLikeData().Parameters }
func (n *Node) Parameters() []*ParameterDeclarationNode   { return n.ParameterList().Nodes }
func (n *Node) ClassLikeData() *ClassLikeBase             { return n.data.ClassLikeData() }
func (n *Node) BodyData() *BodyBase                       { return n.data.BodyData() }
func (n *Node) SubtreeFacts() SubtreeFacts                { return n.data.SubtreeFacts() }
func (n *Node) propagateSubtreeFacts() SubtreeFacts       { return n.data.propagateSubtreeFacts() }
func (n *Node) LiteralLikeData() *LiteralLikeNodeBase     { return n.data.LiteralLikeData() }
func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase {
	return n.data.TemplateLiteralLikeData()
}
func (n *Node) KindString() string { return n.Kind.String() }
func (n *Node) KindValue() int16   { return int16(n.Kind) }
func (n *Node) Decorators() []*Node {
	if n.Modifiers() == nil {
		return nil
	}
	return core.Filter(n.Modifiers().Nodes, IsDecorator)
}

type MutableNode Node

func (n *Node) AsMutable() *MutableNode                     { return (*MutableNode)(n) }
func (n *MutableNode) SetModifiers(modifiers *ModifierList) { n.data.setModifiers(modifiers) }

func (n *Node) Symbol() *Symbol {
	data := n.DeclarationData()
	if data != nil {
		return data.Symbol
	}
	return nil
}

func (n *Node) LocalSymbol() *Symbol {
	data := n.ExportableData()
	if data != nil {
		return data.LocalSymbol
	}
	return nil
}

func (n *Node) Locals() SymbolTable {
	data := n.LocalsContainerData()
	if data != nil {
		return data.Locals
	}
	return nil
}

func (n *Node) Body() *Node {
	data := n.BodyData()
	if data != nil {
		return data.Body
	}
	return nil
}

func (n *Node) Text() string {
	switch n.Kind {
	case KindIdentifier:
		return n.AsIdentifier().Text
	case KindPrivateIdentifier:
		return n.AsPrivateIdentifier().Text
	case KindStringLiteral:
		return n.AsStringLiteral().Text
	case KindNumericLiteral:
		return n.AsNumericLiteral().Text
	case KindBigIntLiteral:
		return n.AsBigIntLiteral().Text
	case KindMetaProperty:
		return n.AsMetaProperty().Name().Text()
	case KindNoSubstitutionTemplateLiteral:
		return n.AsNoSubstitutionTemplateLiteral().Text
	case KindTemplateHead:
		return n.AsTemplateHead().Text
	case KindTemplateMiddle:
		return n.AsTemplateMiddle().Text
	case KindTemplateTail:
		return n.AsTemplateTail().Text
	case KindJsxNamespacedName:
		return n.AsJsxNamespacedName().Namespace.Text() + ":" + n.AsJsxNamespacedName().name.Text()
	case KindRegularExpressionLiteral:
		return n.AsRegularExpressionLiteral().Text
	case KindJSDocText:
		return strings.Join(n.AsJSDocText().text, "")
	case KindJSDocLink:
		return strings.Join(n.AsJSDocLink().text, "")
	case KindJSDocLinkCode:
		return strings.Join(n.AsJSDocLinkCode().text, "")
	case KindJSDocLinkPlain:
		return strings.Join(n.AsJSDocLinkPlain().text, "")
	}
	panic(fmt.Sprintf("Unhandled case in Node.Text: %T", n.data))
}

func (n *Node) Expression() *Node {
	switch n.Kind {
	case KindPropertyAccessExpression:
		return n.AsPropertyAccessExpression().Expression
	case KindElementAccessExpression:
		return n.AsElementAccessExpression().Expression
	case KindParenthesizedExpression:
		return n.AsParenthesizedExpression().Expression
	case KindCallExpression:
		return n.AsCallExpression().Expression
	case KindNewExpression:
		return n.AsNewExpression().Expression
	case KindExpressionWithTypeArguments:
		return n.AsExpressionWithTypeArguments().Expression
	case KindComputedPropertyName:
		return n.AsComputedPropertyName().Expression
	case KindNonNullExpression:
		return n.AsNonNullExpression().Expression
	case KindTypeAssertionExpression:
		return n.AsTypeAssertion().Expression
	case KindAsExpression:
		return n.AsAsExpression().Expression
	case KindSatisfiesExpression:
		return n.AsSatisfiesExpression().Expression
	case KindTypeOfExpression:
		return n.AsTypeOfExpression().Expression
	case KindSpreadAssignment:
		return n.AsSpreadAssignment().Expression
	case KindSpreadElement:
		return n.AsSpreadElement().Expression
	case KindTemplateSpan:
		return n.AsTemplateSpan().Expression
	case KindDeleteExpression:
		return n.AsDeleteExpression().Expression
	case KindVoidExpression:
		return n.AsVoidExpression().Expression
	case KindAwaitExpression:
		return n.AsAwaitExpression().Expression
	case KindYieldExpression:
		return n.AsYieldExpression().Expression
	case KindPartiallyEmittedExpression:
		return n.AsPartiallyEmittedExpression().Expression
	case KindIfStatement:
		return n.AsIfStatement().Expression
	case KindDoStatement:
		return n.AsDoStatement().Expression
	case KindWhileStatement:
		return n.AsWhileStatement().Expression
	case KindWithStatement:
		return n.AsWithStatement().Expression
	case KindForInStatement, KindForOfStatement:
		return n.AsForInOrOfStatement().Expression
	case KindSwitchStatement:
		return n.AsSwitchStatement().Expression
	case KindCaseClause:
		return n.AsCaseOrDefaultClause().Expression
	case KindExpressionStatement:
		return n.AsExpressionStatement().Expression
	case KindReturnStatement:
		return n.AsReturnStatement().Expression
	case KindThrowStatement:
		return n.AsThrowStatement().Expression
	case KindExternalModuleReference:
		return n.AsExternalModuleReference().Expression
	case KindExportAssignment:
		return n.AsExportAssignment().Expression
	case KindDecorator:
		return n.AsDecorator().Expression
	case KindJsxExpression:
		return n.AsJsxExpression().Expression
	case KindJsxSpreadAttribute:
		return n.AsJsxSpreadAttribute().Expression
	}
	panic("Unhandled case in Node.Expression: " + n.Kind.String())
}

func (n *Node) RawText() string {
	switch n.Kind {
	case KindTemplateHead:
		return n.AsTemplateHead().RawText
	case KindTemplateMiddle:
		return n.AsTemplateMiddle().RawText
	case KindTemplateTail:
		return n.AsTemplateTail().RawText
	}
	panic("Unhandled case in Node.RawText: " + n.Kind.String())
}

func (m *MutableNode) SetExpression(expr *Node) {
	n := (*Node)(m)
	switch n.Kind {
	case KindPropertyAccessExpression:
		n.AsPropertyAccessExpression().Expression = expr
	case KindElementAccessExpression:
		n.AsElementAccessExpression().Expression = expr
	case KindParenthesizedExpression:
		n.AsParenthesizedExpression().Expression = expr
	case KindCallExpression:
		n.AsCallExpression().Expression = expr
	case KindNewExpression:
		n.AsNewExpression().Expression = expr
	case KindExpressionWithTypeArguments:
		n.AsExpressionWithTypeArguments().Expression = expr
	case KindComputedPropertyName:
		n.AsComputedPropertyName().Expression = expr
	case KindNonNullExpression:
		n.AsNonNullExpression().Expression = expr
	case KindTypeAssertionExpression:
		n.AsTypeAssertion().Expression = expr
	case KindAsExpression:
		n.AsAsExpression().Expression = expr
	case KindSatisfiesExpression:
		n.AsSatisfiesExpression().Expression = expr
	case KindTypeOfExpression:
		n.AsTypeOfExpression().Expression = expr
	case KindSpreadAssignment:
		n.AsSpreadAssignment().Expression = expr
	case KindSpreadElement:
		n.AsSpreadElement().Expression = expr
	case KindTemplateSpan:
		n.AsTemplateSpan().Expression = expr
	case KindDeleteExpression:
		n.AsDeleteExpression().Expression = expr
	case KindVoidExpression:
		n.AsVoidExpression().Expression = expr
	case KindAwaitExpression:
		n.AsAwaitExpression().Expression = expr
	case KindYieldExpression:
		n.AsYieldExpression().Expression = expr
	case KindPartiallyEmittedExpression:
		n.AsPartiallyEmittedExpression().Expression = expr
	case KindIfStatement:
		n.AsIfStatement().Expression = expr
	case KindDoStatement:
		n.AsDoStatement().Expression = expr
	case KindWhileStatement:
		n.AsWhileStatement().Expression = expr
	case KindWithStatement:
		n.AsWithStatement().Expression = expr
	case KindForInStatement, KindForOfStatement:
		n.AsForInOrOfStatement().Expression = expr
	case KindSwitchStatement:
		n.AsSwitchStatement().Expression = expr
	case KindCaseClause:
		n.AsCaseOrDefaultClause().Expression = expr
	case KindExpressionStatement:
		n.AsExpressionStatement().Expression = expr
	case KindReturnStatement:
		n.AsReturnStatement().Expression = expr
	case KindThrowStatement:
		n.AsThrowStatement().Expression = expr
	case KindExternalModuleReference:
		n.AsExternalModuleReference().Expression = expr
	case KindExportAssignment:
		n.AsExportAssignment().Expression = expr
	case KindDecorator:
		n.AsDecorator().Expression = expr
	case KindJsxExpression:
		n.AsJsxExpression().Expression = expr
	case KindJsxSpreadAttribute:
		n.AsJsxSpreadAttribute().Expression = expr
	default:
		panic("Unhandled case in mutableNode.SetExpression: " + n.Kind.String())
	}
}

func (n *Node) ArgumentList() *NodeList {
	switch n.Kind {
	case KindCallExpression:
		return n.AsCallExpression().Arguments
	case KindNewExpression:
		return n.AsNewExpression().Arguments
	}
	panic("Unhandled case in Node.Arguments: " + n.Kind.String())
}

func (n *Node) Arguments() []*Node {
	list := n.ArgumentList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) TypeArgumentList() *NodeList {
	switch n.Kind {
	case KindCallExpression:
		return n.AsCallExpression().TypeArguments
	case KindNewExpression:
		return n.AsNewExpression().TypeArguments
	case KindTaggedTemplateExpression:
		return n.AsTaggedTemplateExpression().TypeArguments
	case KindTypeReference:
		return n.AsTypeReferenceNode().TypeArguments
	case KindExpressionWithTypeArguments:
		return n.AsExpressionWithTypeArguments().TypeArguments
	case KindImportType:
		return n.AsImportTypeNode().TypeArguments
	case KindTypeQuery:
		return n.AsTypeQueryNode().TypeArguments
	case KindJsxOpeningElement:
		return n.AsJsxOpeningElement().TypeArguments
	case KindJsxSelfClosingElement:
		return n.AsJsxSelfClosingElement().TypeArguments
	}
	panic("Unhandled case in Node.TypeArguments")
}

func (n *Node) TypeArguments() []*Node {
	list := n.TypeArgumentList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) TypeParameterList() *NodeList {
	switch n.Kind {
	case KindClassDeclaration:
		return n.AsClassDeclaration().TypeParameters
	case KindClassExpression:
		return n.AsClassExpression().TypeParameters
	case KindInterfaceDeclaration:
		return n.AsInterfaceDeclaration().TypeParameters
	case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration:
		return n.AsTypeAliasDeclaration().TypeParameters
	case KindJSDocTemplateTag:
		return n.AsJSDocTemplateTag().TypeParameters
	default:
		funcLike := n.FunctionLikeData()
		if funcLike != nil {
			return funcLike.TypeParameters
		}
	}
	panic("Unhandled case in Node.TypeParameterList")
}

func (n *Node) TypeParameters() []*Node {
	list := n.TypeParameterList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) MemberList() *NodeList {
	switch n.Kind {
	case KindClassDeclaration:
		return n.AsClassDeclaration().Members
	case KindClassExpression:
		return n.AsClassExpression().Members
	case KindInterfaceDeclaration:
		return n.AsInterfaceDeclaration().Members
	case KindEnumDeclaration:
		return n.AsEnumDeclaration().Members
	case KindTypeLiteral:
		return n.AsTypeLiteralNode().Members
	case KindMappedType:
		return n.AsMappedTypeNode().Members
	}
	panic("Unhandled case in Node.MemberList: " + n.Kind.String())
}

func (n *Node) Members() []*Node {
	list := n.MemberList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) StatementList() *NodeList {
	switch n.Kind {
	case KindSourceFile:
		return n.AsSourceFile().Statements
	case KindBlock:
		return n.AsBlock().Statements
	case KindModuleBlock:
		return n.AsModuleBlock().Statements
	case KindCaseClause, KindDefaultClause:
		return n.AsCaseOrDefaultClause().Statements
	}
	panic("Unhandled case in Node.StatementList: " + n.Kind.String())
}

func (n *Node) Statements() []*Node {
	list := n.StatementList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) CanHaveStatements() bool {
	switch n.Kind {
	case KindSourceFile, KindBlock, KindModuleBlock, KindCaseClause, KindDefaultClause:
		return true
	default:
		return false
	}
}

func (n *Node) ModifierFlags() ModifierFlags {
	modifiers := n.Modifiers()
	if modifiers != nil {
		return modifiers.ModifierFlags
	}
	return ModifierFlagsNone
}

func (n *Node) ModifierNodes() []*Node {
	modifiers := n.Modifiers()
	if modifiers != nil {
		return modifiers.Nodes
	}
	return nil
}

func (n *Node) Type() *Node {
	switch n.Kind {
	case KindVariableDeclaration:
		return n.AsVariableDeclaration().Type
	case KindParameter:
		return n.AsParameterDeclaration().Type
	case KindPropertySignature:
		return n.AsPropertySignatureDeclaration().Type
	case KindPropertyDeclaration:
		return n.AsPropertyDeclaration().Type
	case KindPropertyAssignment:
		return n.AsPropertyAssignment().Type
	case KindShorthandPropertyAssignment:
		return n.AsShorthandPropertyAssignment().Type
	case KindTypePredicate:
		return n.AsTypePredicateNode().Type
	case KindParenthesizedType:
		return n.AsParenthesizedTypeNode().Type
	case KindTypeOperator:
		return n.AsTypeOperatorNode().Type
	case KindMappedType:
		return n.AsMappedTypeNode().Type
	case KindTypeAssertionExpression:
		return n.AsTypeAssertion().Type
	case KindAsExpression:
		return n.AsAsExpression().Type
	case KindSatisfiesExpression:
		return n.AsSatisfiesExpression().Type
	case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration:
		return n.AsTypeAliasDeclaration().Type
	case KindNamedTupleMember:
		return n.AsNamedTupleMember().Type
	case KindOptionalType:
		return n.AsOptionalTypeNode().Type
	case KindRestType:
		return n.AsRestTypeNode().Type
	case KindTemplateLiteralTypeSpan:
		return n.AsTemplateLiteralTypeSpan().Type
	case KindJSDocTypeExpression:
		return n.AsJSDocTypeExpression().Type
	case KindJSDocParameterTag, KindJSDocPropertyTag:
		return n.AsJSDocParameterOrPropertyTag().TypeExpression
	case KindJSDocNullableType:
		return n.AsJSDocNullableType().Type
	case KindJSDocNonNullableType:
		return n.AsJSDocNonNullableType().Type
	case KindJSDocOptionalType:
		return n.AsJSDocOptionalType().Type
	case KindExportAssignment:
		return n.AsExportAssignment().Type
	case KindBinaryExpression:
		return n.AsBinaryExpression().Type
	default:
		if funcLike := n.FunctionLikeData(); funcLike != nil {
			return funcLike.Type
		}
	}
	return nil
}

func (m *MutableNode) SetType(t *Node) {
	n := (*Node)(m)
	switch m.Kind {
	case KindVariableDeclaration:
		n.AsVariableDeclaration().Type = t
	case KindParameter:
		n.AsParameterDeclaration().Type = t
	case KindPropertySignature:
		n.AsPropertySignatureDeclaration().Type = t
	case KindPropertyDeclaration:
		n.AsPropertyDeclaration().Type = t
	case KindPropertyAssignment:
		n.AsPropertyAssignment().Type = t
	case KindShorthandPropertyAssignment:
		n.AsShorthandPropertyAssignment().Type = t
	case KindTypePredicate:
		n.AsTypePredicateNode().Type = t
	case KindParenthesizedType:
		n.AsParenthesizedTypeNode().Type = t
	case KindTypeOperator:
		n.AsTypeOperatorNode().Type = t
	case KindMappedType:
		n.AsMappedTypeNode().Type = t
	case KindTypeAssertionExpression:
		n.AsTypeAssertion().Type = t
	case KindAsExpression:
		n.AsAsExpression().Type = t
	case KindSatisfiesExpression:
		n.AsSatisfiesExpression().Type = t
	case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration:
		n.AsTypeAliasDeclaration().Type = t
	case KindNamedTupleMember:
		n.AsNamedTupleMember().Type = t
	case KindOptionalType:
		n.AsOptionalTypeNode().Type = t
	case KindRestType:
		n.AsRestTypeNode().Type = t
	case KindTemplateLiteralTypeSpan:
		n.AsTemplateLiteralTypeSpan().Type = t
	case KindJSDocTypeExpression:
		n.AsJSDocTypeExpression().Type = t
	case KindJSDocParameterTag, KindJSDocPropertyTag:
		n.AsJSDocParameterOrPropertyTag().TypeExpression = t
	case KindJSDocNullableType:
		n.AsJSDocNullableType().Type = t
	case KindJSDocNonNullableType:
		n.AsJSDocNonNullableType().Type = t
	case KindJSDocOptionalType:
		n.AsJSDocOptionalType().Type = t
	case KindExportAssignment:
		n.AsExportAssignment().Type = t
	case KindBinaryExpression:
		n.AsBinaryExpression().Type = t
	default:
		if funcLike := n.FunctionLikeData(); funcLike != nil {
			funcLike.Type = t
		} else {
			panic("Unhandled case in mutableNode.SetType: " + n.Kind.String())
		}
	}
}

func (n *Node) Initializer() *Node {
	switch n.Kind {
	case KindVariableDeclaration:
		return n.AsVariableDeclaration().Initializer
	case KindParameter:
		return n.AsParameterDeclaration().Initializer
	case KindBindingElement:
		return n.AsBindingElement().Initializer
	case KindPropertyDeclaration:
		return n.AsPropertyDeclaration().Initializer
	case KindPropertySignature:
		return n.AsPropertySignatureDeclaration().Initializer
	case KindPropertyAssignment:
		return n.AsPropertyAssignment().Initializer
	case KindEnumMember:
		return n.AsEnumMember().Initializer
	case KindForStatement:
		return n.AsForStatement().Initializer
	case KindForInStatement, KindForOfStatement:
		return n.AsForInOrOfStatement().Initializer
	case KindJsxAttribute:
		return n.AsJsxAttribute().Initializer
	}
	panic("Unhandled case in Node.Initializer")
}

func (m *MutableNode) SetInitializer(initializer *Node) {
	n := (*Node)(m)
	switch n.Kind {
	case KindVariableDeclaration:
		n.AsVariableDeclaration().Initializer = initializer
	case KindParameter:
		n.AsParameterDeclaration().Initializer = initializer
	case KindBindingElement:
		n.AsBindingElement().Initializer = initializer
	case KindPropertyDeclaration:
		n.AsPropertyDeclaration().Initializer = initializer
	case KindPropertySignature:
		n.AsPropertySignatureDeclaration().Initializer = initializer
	case KindPropertyAssignment:
		n.AsPropertyAssignment().Initializer = initializer
	case KindEnumMember:
		n.AsEnumMember().Initializer = initializer
	case KindForStatement:
		n.AsForStatement().Initializer = initializer
	case KindForInStatement, KindForOfStatement:
		n.AsForInOrOfStatement().Initializer = initializer
	case KindJsxAttribute:
		n.AsJsxAttribute().Initializer = initializer
	default:
		panic("Unhandled case in mutableNode.SetInitializer")
	}
}

func (n *Node) TagName() *Node {
	switch n.Kind {
	case KindJsxOpeningElement:
		return n.AsJsxOpeningElement().TagName
	case KindJsxClosingElement:
		return n.AsJsxClosingElement().TagName
	case KindJsxSelfClosingElement:
		return n.AsJsxSelfClosingElement().TagName
	case KindJSDocUnknownTag:
		return n.AsJSDocUnknownTag().TagName
	case KindJSDocAugmentsTag:
		return n.AsJSDocAugmentsTag().TagName
	case KindJSDocImplementsTag:
		return n.AsJSDocImplementsTag().TagName
	case KindJSDocDeprecatedTag:
		return n.AsJSDocDeprecatedTag().TagName
	case KindJSDocPublicTag:
		return n.AsJSDocPublicTag().TagName
	case KindJSDocPrivateTag:
		return n.AsJSDocPrivateTag().TagName
	case KindJSDocProtectedTag:
		return n.AsJSDocProtectedTag().TagName
	case KindJSDocReadonlyTag:
		return n.AsJSDocReadonlyTag().TagName
	case KindJSDocOverrideTag:
		return n.AsJSDocOverrideTag().TagName
	case KindJSDocCallbackTag:
		return n.AsJSDocCallbackTag().TagName
	case KindJSDocOverloadTag:
		return n.AsJSDocOverloadTag().TagName
	case KindJSDocParameterTag, KindJSDocPropertyTag:
		return n.AsJSDocParameterOrPropertyTag().TagName
	case KindJSDocReturnTag:
		return n.AsJSDocReturnTag().TagName
	case KindJSDocThisTag:
		return n.AsJSDocThisTag().TagName
	case KindJSDocTypeTag:
		return n.AsJSDocTypeTag().TagName
	case KindJSDocTemplateTag:
		return n.AsJSDocTemplateTag().TagName
	case KindJSDocTypedefTag:
		return n.AsJSDocTypedefTag().TagName
	case KindJSDocSeeTag:
		return n.AsJSDocSeeTag().TagName
	case KindJSDocSatisfiesTag:
		return n.AsJSDocSatisfiesTag().TagName
	case KindJSDocThrowsTag:
		return n.AsJSDocThrowsTag().TagName
	case KindJSDocImportTag:
		return n.AsJSDocImportTag().TagName
	}
	panic("Unhandled case in Node.TagName: " + n.Kind.String())
}

func (n *Node) PropertyName() *Node {
	switch n.Kind {
	case KindImportSpecifier:
		return n.AsImportSpecifier().PropertyName
	case KindExportSpecifier:
		return n.AsExportSpecifier().PropertyName
	case KindBindingElement:
		return n.AsBindingElement().PropertyName
	}
	return nil
}

func (n *Node) PropertyNameOrName() *Node {
	name := n.PropertyName()
	if name == nil {
		name = n.Name()
	}
	return name
}

func (n *Node) IsTypeOnly() bool {
	switch n.Kind {
	case KindImportEqualsDeclaration:
		return n.AsImportEqualsDeclaration().IsTypeOnly
	case KindImportSpecifier:
		return n.AsImportSpecifier().IsTypeOnly
	case KindImportClause:
		return n.AsImportClause().PhaseModifier == KindTypeKeyword
	case KindExportDeclaration:
		return n.AsExportDeclaration().IsTypeOnly
	case KindExportSpecifier:
		return n.AsExportSpecifier().IsTypeOnly
	}
	return false
}

// If updating this function, also update `hasComment`.
func (n *Node) CommentList() *NodeList {
	switch n.Kind {
	case KindJSDoc:
		return n.AsJSDoc().Comment
	case KindJSDocUnknownTag:
		return n.AsJSDocUnknownTag().Comment
	case KindJSDocAugmentsTag:
		return n.AsJSDocAugmentsTag().Comment
	case KindJSDocImplementsTag:
		return n.AsJSDocImplementsTag().Comment
	case KindJSDocDeprecatedTag:
		return n.AsJSDocDeprecatedTag().Comment
	case KindJSDocPublicTag:
		return n.AsJSDocPublicTag().Comment
	case KindJSDocPrivateTag:
		return n.AsJSDocPrivateTag().Comment
	case KindJSDocProtectedTag:
		return n.AsJSDocProtectedTag().Comment
	case KindJSDocReadonlyTag:
		return n.AsJSDocReadonlyTag().Comment
	case KindJSDocOverrideTag:
		return n.AsJSDocOverrideTag().Comment
	case KindJSDocCallbackTag:
		return n.AsJSDocCallbackTag().Comment
	case KindJSDocOverloadTag:
		return n.AsJSDocOverloadTag().Comment
	case KindJSDocParameterTag, KindJSDocPropertyTag:
		return n.AsJSDocParameterOrPropertyTag().Comment
	case KindJSDocReturnTag:
		return n.AsJSDocReturnTag().Comment
	case KindJSDocThisTag:
		return n.AsJSDocThisTag().Comment
	case KindJSDocTypeTag:
		return n.AsJSDocTypeTag().Comment
	case KindJSDocTemplateTag:
		return n.AsJSDocTemplateTag().Comment
	case KindJSDocTypedefTag:
		return n.AsJSDocTypedefTag().Comment
	case KindJSDocSeeTag:
		return n.AsJSDocSeeTag().Comment
	case KindJSDocSatisfiesTag:
		return n.AsJSDocSatisfiesTag().Comment
	case KindJSDocThrowsTag:
		return n.AsJSDocThrowsTag().Comment
	case KindJSDocImportTag:
		return n.AsJSDocImportTag().Comment
	}
	panic("Unhandled case in Node.CommentList: " + n.Kind.String())
}

func (n *Node) Comments() []*Node {
	list := n.CommentList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) Label() *Node {
	switch n.Kind {
	case KindLabeledStatement:
		return n.AsLabeledStatement().Label
	case KindBreakStatement:
		return n.AsBreakStatement().Label
	case KindContinueStatement:
		return n.AsContinueStatement().Label
	}
	panic("Unhandled case in Node.Label: " + n.Kind.String())
}

func (n *Node) Attributes() *Node {
	switch n.Kind {
	case KindJsxOpeningElement:
		return n.AsJsxOpeningElement().Attributes
	case KindJsxSelfClosingElement:
		return n.AsJsxSelfClosingElement().Attributes
	}
	panic("Unhandled case in Node.Attributes: " + n.Kind.String())
}

func (n *Node) Children() *NodeList {
	switch n.Kind {
	case KindJsxElement:
		return n.AsJsxElement().Children
	case KindJsxFragment:
		return n.AsJsxFragment().Children
	}
	panic("Unhandled case in Node.Children: " + n.Kind.String())
}

func (n *Node) ModuleSpecifier() *Expression {
	switch n.Kind {
	case KindImportDeclaration, KindJSImportDeclaration:
		return n.AsImportDeclaration().ModuleSpecifier
	case KindExportDeclaration:
		return n.AsExportDeclaration().ModuleSpecifier
	case KindJSDocImportTag:
		return n.AsJSDocImportTag().ModuleSpecifier
	}
	panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String())
}

func (n *Node) ImportClause() *Node {
	switch n.Kind {
	case KindImportDeclaration, KindJSImportDeclaration:
		return n.AsImportDeclaration().ImportClause
	case KindJSDocImportTag:
		return n.AsJSDocImportTag().ImportClause
	}
	panic("Unhandled case in Node.ImportClause: " + n.Kind.String())
}

func (n *Node) Statement() *Statement {
	switch n.Kind {
	case KindDoStatement:
		return n.AsDoStatement().Statement
	case KindWhileStatement:
		return n.AsWhileStatement().Statement
	case KindForStatement:
		return n.AsForStatement().Statement
	case KindForInStatement, KindForOfStatement:
		return n.AsForInOrOfStatement().Statement
	case KindWithStatement:
		return n.AsWithStatement().Statement
	case KindLabeledStatement:
		return n.AsLabeledStatement().Statement
	}
	panic("Unhandled case in Node.Statement: " + n.Kind.String())
}

func (n *Node) PropertyList() *NodeList {
	switch n.Kind {
	case KindObjectLiteralExpression:
		return n.AsObjectLiteralExpression().Properties
	case KindJsxAttributes:
		return n.AsJsxAttributes().Properties
	}
	panic("Unhandled case in Node.PropertyList: " + n.Kind.String())
}

func (n *Node) Properties() []*Node {
	list := n.PropertyList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) ElementList() *NodeList {
	switch n.Kind {
	case KindNamedImports:
		return n.AsNamedImports().Elements
	case KindNamedExports:
		return n.AsNamedExports().Elements
	case KindObjectBindingPattern, KindArrayBindingPattern:
		return n.AsBindingPattern().Elements
	case KindArrayLiteralExpression:
		return n.AsArrayLiteralExpression().Elements
	case KindTupleType:
		return n.AsTupleTypeNode().Elements
	}
	panic("Unhandled case in Node.ElementList: " + n.Kind.String())
}

func (n *Node) Elements() []*Node {
	list := n.ElementList()
	if list != nil {
		return list.Nodes
	}
	return nil
}

func (n *Node) PostfixToken() *Node {
	switch n.Kind {
	case KindMethodDeclaration:
		return n.AsMethodDeclaration().PostfixToken
	case KindShorthandPropertyAssignment:
		return n.AsShorthandPropertyAssignment().PostfixToken
	case KindMethodSignature:
		return n.AsMethodSignatureDeclaration().PostfixToken
	case KindPropertySignature:
		return n.AsPropertySignatureDeclaration().PostfixToken
	case KindPropertyAssignment:
		return n.AsPropertyAssignment().PostfixToken
	case KindPropertyDeclaration:
		return n.AsPropertyDeclaration().PostfixToken
	case KindEnumMember:
		return n.AsEnumMember().PostfixToken
	case KindGetAccessor:
		return n.AsGetAccessorDeclaration().PostfixToken
	case KindSetAccessor:
		return n.AsSetAccessorDeclaration().PostfixToken
	}
	return nil
}

func (n *Node) QuestionToken() *TokenNode {
	switch n.Kind {
	case KindParameter:
		return n.AsParameterDeclaration().QuestionToken
	case KindConditionalExpression:
		return n.AsConditionalExpression().QuestionToken
	case KindMappedType:
		return n.AsMappedTypeNode().QuestionToken
	case KindNamedTupleMember:
		return n.AsNamedTupleMember().QuestionToken
	}
	postfix := n.PostfixToken()
	if postfix != nil && postfix.Kind == KindQuestionToken {
		return postfix
	}
	return nil
}

func (n *Node) QuestionDotToken() *Node {
	switch n.Kind {
	case KindElementAccessExpression:
		return n.AsElementAccessExpression().QuestionDotToken
	case KindPropertyAccessExpression:
		return n.AsPropertyAccessExpression().QuestionDotToken
	case KindCallExpression:
		return n.AsCallExpression().QuestionDotToken
	case KindTaggedTemplateExpression:
		return n.AsTaggedTemplateExpression().QuestionDotToken
	}
	panic("Unhandled case in Node.QuestionDotToken: " + n.Kind.String())
}

func (n *Node) TypeExpression() *Node {
	switch n.Kind {
	case KindJSDocParameterTag, KindJSDocPropertyTag:
		return n.AsJSDocParameterOrPropertyTag().TypeExpression
	case KindJSDocReturnTag:
		return n.AsJSDocReturnTag().TypeExpression
	case KindJSDocTypeTag:
		return n.AsJSDocTypeTag().TypeExpression
	case KindJSDocTypedefTag:
		return n.AsJSDocTypedefTag().TypeExpression
	case KindJSDocCallbackTag:
		return n.AsJSDocCallbackTag().TypeExpression
	case KindJSDocSatisfiesTag:
		return n.AsJSDocSatisfiesTag().TypeExpression
	case KindJSDocThrowsTag:
		return n.AsJSDocThrowsTag().TypeExpression
	}
	panic("Unhandled case in Node.TypeExpression: " + n.Kind.String())
}

func (n *Node) ClassName() *Node {
	switch n.Kind {
	case KindJSDocAugmentsTag:
		return n.AsJSDocAugmentsTag().ClassName
	case KindJSDocImplementsTag:
		return n.AsJSDocImplementsTag().ClassName
	}
	panic("Unhandled case in Node.ClassName: " + n.Kind.String())
}

// Determines if `n` contains `descendant` by walking up the `Parent` pointers from `descendant`. This method panics if
// `descendant` or one of its ancestors is not parented except when that node is a `SourceFile`.
func (n *Node) Contains(descendant *Node) bool {
	for descendant != nil {
		if descendant == n {
			return true
		}
		parent := descendant.Parent
		if parent == nil && !IsSourceFile(descendant) {
			panic("descendant is not parented")
		}
		descendant = parent
	}
	return false
}

// Node casts

func (n *Node) AsFlowSwitchClauseData() *FlowSwitchClauseData {
	return n.data.(*FlowSwitchClauseData)
}

func (n *Node) AsFlowReduceLabelData() *FlowReduceLabelData {
	return n.data.(*FlowReduceLabelData)
}

// NodeData

type nodeData interface {
	AsNode() *Node
	ForEachChild(v Visitor) bool
	VisitEachChild(v *NodeVisitor) *Node
	Clone(v NodeFactoryCoercible) *Node
	Name() *DeclarationName
	Modifiers() *ModifierList
	setModifiers(modifiers *ModifierList)
	FlowNodeData() *FlowNodeBase
	DeclarationData() *DeclarationBase
	ExportableData() *ExportableBase
	LocalsContainerData() *LocalsContainerBase
	FunctionLikeData() *FunctionLikeBase
	ClassLikeData() *ClassLikeBase
	BodyData() *BodyBase
	LiteralLikeData() *LiteralLikeNodeBase
	TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase
	SubtreeFacts() SubtreeFacts
	computeSubtreeFacts() SubtreeFacts
	subtreeFactsWorker(self nodeData) SubtreeFacts
	propagateSubtreeFacts() SubtreeFacts
}

// NodeDefault

type NodeDefault struct {
	Node
}

func (node *NodeDefault) AsNode() *Node               { return &node.Node }
func (node *NodeDefault) ForEachChild(v Visitor) bool { return false }

func (node *NodeDefault) VisitEachChild(v *NodeVisitor) *Node                   { return node.AsNode() }
func (node *NodeDefault) Clone(v NodeFactoryCoercible) *Node                    { return nil }
func (node *NodeDefault) Name() *DeclarationName                                { return nil }
func (node *NodeDefault) Modifiers() *ModifierList                              { return nil }
func (node *NodeDefault) setModifiers(modifiers *ModifierList)                  {}
func (node *NodeDefault) FlowNodeData() *FlowNodeBase                           { return nil }
func (node *NodeDefault) DeclarationData() *DeclarationBase                     { return nil }
func (node *NodeDefault) ExportableData() *ExportableBase                       { return nil }
func (node *NodeDefault) LocalsContainerData() *LocalsContainerBase             { return nil }
func (node *NodeDefault) FunctionLikeData() *FunctionLikeBase                   { return nil }
func (node *NodeDefault) ClassLikeData() *ClassLikeBase                         { return nil }
func (node *NodeDefault) BodyData() *BodyBase                                   { return nil }
func (node *NodeDefault) LiteralLikeData() *LiteralLikeNodeBase                 { return nil }
func (node *NodeDefault) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { return nil }
func (node *NodeDefault) SubtreeFacts() SubtreeFacts {
	return node.data.subtreeFactsWorker(node.data)
}

func (node *NodeDefault) subtreeFactsWorker(self nodeData) SubtreeFacts {
	// To avoid excessive conditional checks, the default implementation of subtreeFactsWorker directly invokes
	// computeSubtreeFacts. More complex nodes should implement CompositeNodeBase, which overrides this
	// method to cache the result. `self` is passed along to ensure we lookup `computeSubtreeFacts` on the
	// correct type, as `CompositeNodeBase` does not, itself, inherit from `Node`.
	return self.computeSubtreeFacts()
}

func (node *NodeDefault) computeSubtreeFacts() SubtreeFacts {
	return SubtreeFactsNone
}

func (node *NodeDefault) propagateSubtreeFacts() SubtreeFacts {
	return node.data.SubtreeFacts() & ^SubtreeExclusionsNode
}

// NodeBase

type NodeBase struct {
	NodeDefault
}

// Aliases for Node unions not covered by ast_generated.go

type (
	NamedMember                 = Node // Node with NamedMemberBase
	AnyValidImportOrReExport    = Node // (ImportDeclaration | ExportDeclaration | JSDocImportTag) & { moduleSpecifier: StringLiteral } | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral }} | RequireOrImportCall | ValidImportTypeNode
	ValidImportTypeNode         = Node // ImportType & { argument: LiteralTypeNode & { literal: StringLiteral } }
	TypeOnlyImportDeclaration   = Node // ImportClause | ImportEqualsDeclaration | ImportSpecifier | NamespaceImport with isTypeOnly: true
	StringLiteralLike           = Node // StringLiteral | NoSubstitutionTemplateLiteral
	ObjectLiteralLike           = Node // ObjectLiteralExpression | ObjectBindingPattern
	AnyImportOrRequireStatement = Node // AnyImportSyntax | RequireVariableStatement
)

func IsWriteOnlyAccess(node *Node) bool {
	return accessKind(node) == AccessKindWrite
}

func IsWriteAccess(node *Node) bool {
	return accessKind(node) != AccessKindRead
}

func IsWriteAccessForReference(node *Node) bool {
	decl := GetDeclarationFromName(node)
	return (decl != nil && declarationIsWriteAccess(decl)) || node.Kind == KindDefaultKeyword || IsWriteAccess(node)
}

func GetDeclarationFromName(name *Node) *Declaration {
	if name == nil || name.Parent == nil {
		return nil
	}
	parent := name.Parent
	switch name.Kind {
	case KindStringLiteral, KindNoSubstitutionTemplateLiteral, KindNumericLiteral:
		if IsComputedPropertyName(parent) {
			return parent.Parent
		}
		fallthrough
	case KindIdentifier:
		if IsDeclaration(parent) {
			if parent.Name() == name {
				return parent
			}
			return nil
		}
		if IsQualifiedName(parent) {
			tag := parent.Parent
			if IsJSDocParameterTag(tag) && tag.Name() == parent {
				return tag
			}
			return nil
		}
		binExp := parent.Parent
		if IsBinaryExpression(binExp) && GetAssignmentDeclarationKind(binExp) != JSDeclarationKindNone {
			// (binExp.left as BindableStaticNameExpression).symbol || binExp.symbol
			leftHasSymbol := false
			if binExp.AsBinaryExpression().Left != nil && binExp.AsBinaryExpression().Left.Symbol() != nil {
				leftHasSymbol = true
			}
			if leftHasSymbol || binExp.Symbol() != nil {
				if GetNameOfDeclaration(binExp.AsNode()) == name {
					return binExp.AsNode()
				}
			}
		}
	case KindPrivateIdentifier:
		if IsDeclaration(parent) && parent.Name() == name {
			return parent
		}
	}
	return nil
}

func declarationIsWriteAccess(decl *Node) bool {
	if decl == nil {
		return false
	}
	// Consider anything in an ambient declaration to be a write access since it may be coming from JS.
	if decl.Flags&NodeFlagsAmbient != 0 {
		return true
	}

	switch decl.Kind {
	case KindBinaryExpression,
		KindBindingElement,
		KindClassDeclaration,
		KindClassExpression,
		KindDefaultKeyword,
		KindEnumDeclaration,
		KindEnumMember,
		KindExportSpecifier,
		KindImportClause, // default import
		KindImportEqualsDeclaration,
		KindImportSpecifier,
		KindInterfaceDeclaration,
		KindJSDocCallbackTag,
		KindJSDocTypedefTag,
		KindJsxAttribute,
		KindModuleDeclaration,
		KindNamespaceExportDeclaration,
		KindNamespaceImport,
		KindNamespaceExport,
		KindParameter,
		KindShorthandPropertyAssignment,
		KindTypeAliasDeclaration,
		KindJSTypeAliasDeclaration,
		KindTypeParameter:
		return true

	case KindPropertyAssignment:
		// In `({ x: y } = 0);`, `x` is not a write access.
		return !IsArrayLiteralOrObjectLiteralDestructuringPattern(decl.Parent)

	case KindFunctionDeclaration, KindFunctionExpression, KindConstructor, KindMethodDeclaration, KindGetAccessor, KindSetAccessor:
		// functions considered write if they provide a value (have a body)
		switch decl.Kind {
		case KindFunctionDeclaration:
			return decl.AsFunctionDeclaration().Body != nil
		case KindFunctionExpression:
			return decl.AsFunctionExpression().Body != nil
		case KindConstructor:
			// constructor node stores body on the parent? treat same as others
			return decl.AsConstructorDeclaration().Body != nil
		case KindMethodDeclaration:
			return decl.AsMethodDeclaration().Body != nil
		case KindGetAccessor:
			return decl.AsGetAccessorDeclaration().Body != nil
		case KindSetAccessor:
			return decl.AsSetAccessorDeclaration().Body != nil
		}
		return false

	case KindVariableDeclaration, KindPropertyDeclaration:
		// variable/property write if initializer present or is in catch clause
		var hasInit bool
		switch decl.Kind {
		case KindVariableDeclaration:
			hasInit = decl.AsVariableDeclaration().Initializer != nil
		case KindPropertyDeclaration:
			hasInit = decl.AsPropertyDeclaration().Initializer != nil
		}
		return hasInit || IsCatchClause(decl.Parent)

	case KindMethodSignature, KindPropertySignature, KindJSDocPropertyTag, KindJSDocParameterTag:
		return false

	default:
		// preserve TS behavior: crash on unexpected kinds
		panic("Unhandled case in declarationIsWriteAccess")
	}
}

func IsArrayLiteralOrObjectLiteralDestructuringPattern(node *Node) bool {
	if !(IsArrayLiteralExpression(node) || IsObjectLiteralExpression(node)) {
		return false
	}
	parent := node.Parent
	// [a,b,c] from:
	// [a, b, c] = someExpression;
	if IsBinaryExpression(parent) && parent.AsBinaryExpression().Left == node && parent.AsBinaryExpression().OperatorToken.Kind == KindEqualsToken {
		return true
	}
	// [a, b, c] from:
	// for([a, b, c] of expression)
	if IsForOfStatement(parent) && parent.Initializer() == node {
		return true
	}
	// {x, a: {a, b, c} } = someExpression
	if IsPropertyAssignment(parent) {
		return IsArrayLiteralOrObjectLiteralDestructuringPattern(parent.Parent)
	}
	// [a, b, c] of
	// [x, [a, b, c] ] = someExpression
	return IsArrayLiteralOrObjectLiteralDestructuringPattern(parent)
}

func accessKind(node *Node) AccessKind {
	parent := node.Parent
	if parent == nil {
		return AccessKindRead
	}
	switch parent.Kind {
	case KindParenthesizedExpression:
		return accessKind(parent)
	case KindPrefixUnaryExpression:
		operator := parent.AsPrefixUnaryExpression().Operator
		if operator == KindPlusPlusToken || operator == KindMinusMinusToken {
			return AccessKindReadWrite
		}
		return AccessKindRead
	case KindPostfixUnaryExpression:
		operator := parent.AsPostfixUnaryExpression().Operator
		if operator == KindPlusPlusToken || operator == KindMinusMinusToken {
			return AccessKindReadWrite
		}
		return AccessKindRead
	case KindBinaryExpression:
		if parent.AsBinaryExpression().Left == node {
			operator := parent.AsBinaryExpression().OperatorToken
			if IsAssignmentOperator(operator.Kind) {
				if operator.Kind == KindEqualsToken {
					return AccessKindWrite
				}
				return AccessKindReadWrite
			}
		}
		return AccessKindRead
	case KindPropertyAccessExpression:
		if parent.AsPropertyAccessExpression().Name() != node {
			return AccessKindRead
		}
		return accessKind(parent)
	case KindPropertyAssignment:
		parentAccess := accessKind(parent.Parent)
		// In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write.
		if node == parent.AsPropertyAssignment().Name() {
			return reverseAccessKind(parentAccess)
		}
		return parentAccess
	case KindShorthandPropertyAssignment:
		// Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals.
		if node == parent.AsShorthandPropertyAssignment().ObjectAssignmentInitializer {
			return AccessKindRead
		}
		return accessKind(parent.Parent)
	case KindArrayLiteralExpression:
		return accessKind(parent)
	case KindForInStatement, KindForOfStatement:
		if node == parent.AsForInOrOfStatement().Initializer {
			return AccessKindWrite
		}
		return AccessKindRead
	default:
		return AccessKindRead
	}
}

func reverseAccessKind(a AccessKind) AccessKind {
	switch a {
	case AccessKindRead:
		return AccessKindWrite
	case AccessKindWrite:
		return AccessKindRead
	case AccessKindReadWrite:
		return AccessKindReadWrite
	}
	panic("Unhandled case in reverseAccessKind")
}

type AccessKind int32

const (
	AccessKindRead      AccessKind = iota // Only reads from a variable
	AccessKindWrite                       // Only writes to a variable without ever reading it. E.g.: `x=1;`.
	AccessKindReadWrite                   // Reads from and writes to a variable. E.g.: `f(x++);`, `x/=1`.
)

// DeclarationBase

func (node *DeclarationBase) DeclarationData() *DeclarationBase { return node }

func IsDeclarationNode(node *Node) bool {
	return node.DeclarationData() != nil
}

// ExportableBase

func (node *ExportableBase) ExportableData() *ExportableBase { return node }

// ModifiersBase

func (node *ModifiersBase) Modifiers() *ModifierList             { return node.modifiers }
func (node *ModifiersBase) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers }

// LocalsContainerBase

func (node *LocalsContainerBase) LocalsContainerData() *LocalsContainerBase { return node }

func IsLocalsContainer(node *Node) bool {
	return node.LocalsContainerData() != nil
}

// FunctionLikeBase

func (node *FunctionLikeBase) LocalsContainerData() *LocalsContainerBase {
	return &node.LocalsContainerBase
}
func (node *FunctionLikeBase) FunctionLikeData() *FunctionLikeBase { return node }

// BodyBase

func (node *BodyBase) BodyData() *BodyBase { return node }

// FunctionLikeWithBodyBase

func (node *FunctionLikeWithBodyBase) LocalsContainerData() *LocalsContainerBase {
	return &node.LocalsContainerBase
}

func (node *FunctionLikeWithBodyBase) FunctionLikeData() *FunctionLikeBase {
	return &node.FunctionLikeBase
}
func (node *FunctionLikeWithBodyBase) BodyData() *BodyBase { return &node.BodyBase }

// FlowNodeBase

func (node *FlowNodeBase) FlowNodeData() *FlowNodeBase { return node }

// if you provide nil for file, this code will walk to the root of the tree to find the file
func (node *Node) JSDoc(file *SourceFile) []*Node {
	if node.Flags&NodeFlagsHasJSDoc == 0 {
		return nil
	}
	if file == nil {
		file = GetSourceFileOfNode(node)
		if file == nil {
			return nil
		}
	}
	if file.hasLazyJSDoc {
		return file.resolveJSDoc(node)
	}
	return file.jsdocCache[node]
}

// EagerJSDoc returns JSDoc nodes that have already been parsed and cached,
// without triggering lazy JSDoc parsing.
func (node *Node) EagerJSDoc(file *SourceFile) []*Node {
	if node.Flags&NodeFlagsHasJSDoc == 0 {
		return nil
	}
	if file == nil {
		file = GetSourceFileOfNode(node)
		if file == nil {
			return nil
		}
	}
	if file.hasLazyJSDoc {
		file.jsdocMu.RLock()
		jsdocs := file.jsdocCache[node]
		file.jsdocMu.RUnlock()
		return jsdocs
	}
	return file.jsdocCache[node]
}

// CompositeBase

func (node *CompositeBase) subtreeFactsWorker(self nodeData) SubtreeFacts {
	// computeSubtreeFacts() is expected to be idempotent, so races will only impact time, not correctness.
	facts := SubtreeFacts(node.facts.Load())
	if facts&SubtreeFactsComputed == 0 {
		facts |= self.computeSubtreeFacts() | SubtreeFactsComputed
		node.facts.Store(uint32(facts))
	}
	return facts &^ SubtreeFactsComputed
}

func (node *CompositeBase) computeSubtreeFacts() SubtreeFacts {
	// This method must be implemented by the concrete node type.
	panic("not implemented")
}

// TypeSyntaxBase

func (node *TypeSyntaxBase) computeSubtreeFacts() SubtreeFacts { return SubtreeContainsTypeScript }

func (node *TypeSyntaxBase) propagateSubtreeFacts() SubtreeFacts { return SubtreeContainsTypeScript }

func (node *Token) computeSubtreeFacts() SubtreeFacts {
	switch node.Kind {
	case KindUsingKeyword:
		return SubtreeContainsUsing
	case KindPublicKeyword,
		KindPrivateKeyword,
		KindProtectedKeyword,
		KindReadonlyKeyword,
		KindAbstractKeyword,
		KindDeclareKeyword,
		KindConstKeyword,
		KindAnyKeyword,
		KindNumberKeyword,
		KindBigIntKeyword,
		KindNeverKeyword,
		KindObjectKeyword,
		KindInKeyword,
		KindOutKeyword,
		KindOverrideKeyword,
		KindStringKeyword,
		KindBooleanKeyword,
		KindSymbolKeyword,
		KindVoidKeyword,
		KindUnknownKeyword,
		KindUndefinedKeyword,
		KindExportKeyword:
		return SubtreeContainsTypeScript
	case KindAccessorKeyword:
		return SubtreeContainsClassFields
	case KindAsyncKeyword:
		return SubtreeContainsAnyAwait
	case KindSuperKeyword:
		return SubtreeContainsLexicalSuper
	case KindThisKeyword:
		return SubtreeContainsLexicalThis
	case KindAsteriskAsteriskToken, KindAsteriskAsteriskEqualsToken:
		return SubtreeContainsExponentiationOperator
	case KindQuestionQuestionToken:
		return SubtreeContainsNullishCoalescing
	case KindQuestionDotToken:
		return SubtreeContainsOptionalChaining
	case KindQuestionQuestionEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken:
		return SubtreeContainsLogicalAssignments
	}
	return SubtreeFactsNone
}

func (node *PrivateIdentifier) computeSubtreeFacts() SubtreeFacts {
	return SubtreeContainsClassFields
}

func (f *NodeFactory) NewModifier(kind Kind) *Node {
	return f.NewToken(kind)
}

func (node *Decorator) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) |
		SubtreeContainsTypeScript |
		SubtreeContainsDecorators
}

func (node *ForInOrOfStatement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Initializer) |
		propagateSubtreeFacts(node.Expression) |
		propagateSubtreeFacts(node.Statement) |
		core.IfElse(node.AwaitModifier != nil, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone)
}

func (node *ReturnStatement) computeSubtreeFacts() SubtreeFacts {
	// return in an ES2018 async generator must be awaited
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator
}

func (node *CatchClause) computeSubtreeFacts() SubtreeFacts {
	res := propagateSubtreeFacts(node.VariableDeclaration) |
		propagateSubtreeFacts(node.Block)
	if node.VariableDeclaration == nil {
		res |= SubtreeContainsMissingCatchClauseVariable
	}
	return res
}

func (node *CatchClause) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsCatchClause
}

func (node *VariableStatement) computeSubtreeFacts() SubtreeFacts {
	if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.DeclarationList)
	}
}

func (node *VariableDeclaration) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) |
		propagateEraseableSyntaxSubtreeFacts(node.ExclamationToken) |
		propagateEraseableSyntaxSubtreeFacts(node.Type) |
		propagateSubtreeFacts(node.Initializer)
}

func (node *VariableDeclarationList) computeSubtreeFacts() SubtreeFacts {
	return propagateNodeListSubtreeFacts(node.Declarations, propagateSubtreeFacts) |
		core.IfElse(node.Flags&NodeFlagsUsing != 0, SubtreeContainsUsing, SubtreeFactsNone)
}

func (node *VariableDeclarationList) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsVariableDeclarationList
}

func (node *BindingPattern) computeSubtreeFacts() SubtreeFacts {
	switch node.Kind {
	case KindObjectBindingPattern:
		return propagateNodeListSubtreeFacts(node.Elements, propagateObjectBindingElementSubtreeFacts)
	case KindArrayBindingPattern:
		return propagateNodeListSubtreeFacts(node.Elements, propagateBindingElementSubtreeFacts)
	default:
		return SubtreeFactsNone
	}
}

func (node *BindingPattern) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsBindingPattern
}

func (node *ParameterDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.name != nil && IsThisIdentifier(node.name) {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateEraseableSyntaxSubtreeFacts(node.QuestionToken) |
			propagateEraseableSyntaxSubtreeFacts(node.Type) |
			propagateSubtreeFacts(node.Initializer)
	}
}

func (node *ParameterDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsParameter
}

func (node *BindingElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.PropertyName) |
		propagateSubtreeFacts(node.name) |
		propagateSubtreeFacts(node.Initializer) |
		core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRestOrSpread, SubtreeFactsNone)
}

func (node *FunctionDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.Body == nil || node.ModifierFlags()&ModifierFlagsAmbient != 0 {
		return SubtreeContainsTypeScript
	} else {
		isAsync := node.ModifierFlags()&ModifierFlagsAsync != 0
		isGenerator := node.AsteriskToken != nil
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.AsteriskToken) |
			propagateSubtreeFacts(node.name) |
			propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
			propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
			propagateEraseableSyntaxSubtreeFacts(node.Type) |
			propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
			propagateSubtreeFacts(node.Body) |
			core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
			core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
	}
}

func (node *FunctionDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsFunction
}

// ClassLikeBase

func (node *ClassLikeBase) Name() *DeclarationName { return node.name }

func (node *ClassLikeBase) ClassLikeData() *ClassLikeBase { return node }

func (node *ClassLikeBase) computeSubtreeFacts() SubtreeFacts {
	if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
			propagateNodeListSubtreeFacts(node.HeritageClauses, propagateSubtreeFacts) |
			propagateNodeListSubtreeFacts(node.Members, propagateSubtreeFacts)
	}
}

func (node *ClassDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsClass
}

func (node *ClassExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsClass
}

func (node *HeritageClause) computeSubtreeFacts() SubtreeFacts {
	switch node.Token {
	case KindExtendsKeyword:
		return propagateNodeListSubtreeFacts(node.Types, propagateSubtreeFacts)
	case KindImplementsKeyword:
		return SubtreeContainsTypeScript
	default:
		return SubtreeFactsNone
	}
}

func IsTypeOrJSTypeAliasDeclaration(node *Node) bool {
	return node.Kind == KindTypeAliasDeclaration || node.Kind == KindJSTypeAliasDeclaration
}

func (node *EnumMember) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) |
		propagateSubtreeFacts(node.Initializer) |
		SubtreeContainsTypeScript
}

func (node *EnumDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateNodeListSubtreeFacts(node.Members, propagateSubtreeFacts) |
			SubtreeContainsTypeScript
	}
}

func (node *ModuleDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.ModifierFlags()&ModifierFlagsAmbient != 0 {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateSubtreeFacts(node.Body) |
			SubtreeContainsTypeScript
	}
}

func (node *ModuleDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsModule
}

func (node *ImportEqualsDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.IsTypeOnly || !IsExternalModuleReference(node.ModuleReference) {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateSubtreeFacts(node.ModuleReference)
	}
}

func IsImportDeclarationOrJSImportDeclaration(node *Node) bool {
	return node.Kind == KindImportDeclaration || node.Kind == KindJSImportDeclaration
}

func (node *ImportSpecifier) computeSubtreeFacts() SubtreeFacts {
	if node.IsTypeOnly {
		return SubtreeContainsTypeScript
	} else {
		return propagateSubtreeFacts(node.PropertyName) |
			propagateSubtreeFacts(node.name)
	}
}

func (node *ImportClause) computeSubtreeFacts() SubtreeFacts {
	if node.PhaseModifier == KindTypeKeyword {
		return SubtreeContainsTypeScript
	} else {
		return propagateSubtreeFacts(node.name) |
			propagateSubtreeFacts(node.NamedBindings)
	}
}

func (node *ExportAssignment) computeSubtreeFacts() SubtreeFacts {
	return propagateModifierListSubtreeFacts(node.modifiers) | propagateSubtreeFacts(node.Type) | propagateSubtreeFacts(node.Expression) | core.IfElse(node.IsExportEquals, SubtreeContainsTypeScript, SubtreeFactsNone)
}

func IsAnyExportAssignment(node *Node) bool {
	return node.Kind == KindExportAssignment
}

func (node *ExportDeclaration) computeSubtreeFacts() SubtreeFacts {
	return propagateModifierListSubtreeFacts(node.modifiers) |
		propagateSubtreeFacts(node.ExportClause) |
		propagateSubtreeFacts(node.ModuleSpecifier) |
		propagateSubtreeFacts(node.Attributes) |
		core.IfElse(node.IsTypeOnly, SubtreeContainsTypeScript, SubtreeFactsNone)
}

func (node *ExportSpecifier) computeSubtreeFacts() SubtreeFacts {
	if node.IsTypeOnly {
		return SubtreeContainsTypeScript
	} else {
		return propagateSubtreeFacts(node.PropertyName) |
			propagateSubtreeFacts(node.name)
	}
}

// NamedMemberBase

func (node *NamedMemberBase) DeclarationData() *DeclarationBase    { return &node.DeclarationBase }
func (node *NamedMemberBase) Modifiers() *ModifierList             { return node.modifiers }
func (node *NamedMemberBase) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers }
func (node *NamedMemberBase) Name() *DeclarationName               { return node.name }

func (node *ConstructorDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.Body == nil {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
			propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
			propagateEraseableSyntaxSubtreeFacts(node.Type) |
			propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
			propagateSubtreeFacts(node.Body)
	}
}

func (node *ConstructorDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsConstructor
}

func (node *AccessorDeclarationBase) IsAccessorDeclaration() {}

func (node *AccessorDeclarationBase) computeSubtreeFacts() SubtreeFacts {
	if node.Body == nil {
		return SubtreeContainsTypeScript
	} else {
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.name) |
			propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
			propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
			propagateEraseableSyntaxSubtreeFacts(node.Type) |
			propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
			propagateSubtreeFacts(node.Body)
	}
}

func (node *AccessorDeclarationBase) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsAccessor |
		propagateSubtreeFacts(node.name)
}

func (node *MethodDeclaration) computeSubtreeFacts() SubtreeFacts {
	if node.Body == nil {
		return SubtreeContainsTypeScript
	} else {
		isAsync := node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAsync != 0
		isGenerator := node.AsteriskToken != nil
		return propagateModifierListSubtreeFacts(node.modifiers) |
			propagateSubtreeFacts(node.AsteriskToken) |
			propagateSubtreeFacts(node.name) |
			propagateEraseableSyntaxSubtreeFacts(node.PostfixToken) |
			propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
			propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
			propagateSubtreeFacts(node.Body) |
			propagateEraseableSyntaxSubtreeFacts(node.Type) |
			propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
			core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
			core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
	}
}

func (node *MethodDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsMethod |
		propagateSubtreeFacts(node.name)
}

func (node *PropertyDeclaration) computeSubtreeFacts() SubtreeFacts {
	return propagateModifierListSubtreeFacts(node.modifiers) |
		propagateSubtreeFacts(node.name) |
		propagateEraseableSyntaxSubtreeFacts(node.PostfixToken) |
		propagateEraseableSyntaxSubtreeFacts(node.Type) |
		propagateSubtreeFacts(node.Initializer) |
		SubtreeContainsClassFields
}

func (node *PropertyDeclaration) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsProperty |
		propagateSubtreeFacts(node.name)
}

func (node *ClassStaticBlockDeclaration) computeSubtreeFacts() SubtreeFacts {
	return propagateModifierListSubtreeFacts(node.modifiers) |
		propagateSubtreeFacts(node.Body) |
		SubtreeContainsClassFields
}

func (node *KeywordExpression) computeSubtreeFacts() SubtreeFacts {
	switch node.Kind {
	case KindThisKeyword:
		return SubtreeContainsLexicalThis
	case KindSuperKeyword:
		return SubtreeContainsLexicalSuper
	}
	return SubtreeFactsNone
}

// TemplateLiteralLikeBase

func (node *LiteralLikeNodeBase) LiteralLikeData() *LiteralLikeNodeBase { return node }

func (node *BigIntLiteral) computeSubtreeFacts() SubtreeFacts {
	return SubtreeFactsNone // `bigint` is not downleveled in any way
}

func (node *Identifier) computeSubtreeFacts() SubtreeFacts {
	return SubtreeContainsIdentifier
}

func (node *NoSubstitutionTemplateLiteral) computeSubtreeFacts() SubtreeFacts {
	if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 {
		return SubtreeContainsInvalidTemplateEscape
	}
	return SubtreeFactsNone
}

func (node *BinaryExpression) computeSubtreeFacts() SubtreeFacts {
	facts := propagateModifierListSubtreeFacts(node.modifiers) |
		propagateSubtreeFacts(node.Left) |
		propagateSubtreeFacts(node.Type) |
		propagateSubtreeFacts(node.OperatorToken) |
		propagateSubtreeFacts(node.Right) |
		core.IfElse(node.OperatorToken.Kind == KindInKeyword && IsPrivateIdentifier(node.Left), SubtreeContainsClassFields|SubtreeContainsPrivateIdentifierInExpression, SubtreeFactsNone)
	if node.OperatorToken.Kind == KindEqualsToken {
		if (IsObjectLiteralExpression(node.Left) || IsArrayLiteralExpression(node.Left)) && ContainsObjectRestOrSpread(node.Left) {
			facts |= SubtreeContainsObjectRestOrSpread
		}
	}
	return facts
}

func (node *BinaryExpression) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers }

func (node *YieldExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator
}

func (node *ArrowFunction) computeSubtreeFacts() SubtreeFacts {
	return propagateModifierListSubtreeFacts(node.modifiers) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
		propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
		propagateEraseableSyntaxSubtreeFacts(node.Type) |
		propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
		propagateSubtreeFacts(node.Body) |
		core.IfElse(node.ModifierFlags()&ModifierFlagsAsync != 0, SubtreeContainsAnyAwait, SubtreeFactsNone)
}

func (node *ArrowFunction) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsArrowFunction
}

func (node *FunctionExpression) computeSubtreeFacts() SubtreeFacts {
	isAsync := node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAsync != 0
	isGenerator := node.AsteriskToken != nil
	return propagateModifierListSubtreeFacts(node.modifiers) |
		propagateSubtreeFacts(node.AsteriskToken) |
		propagateSubtreeFacts(node.name) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) |
		propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
		propagateEraseableSyntaxSubtreeFacts(node.Type) |
		propagateEraseableSyntaxSubtreeFacts(node.FullSignature) |
		propagateSubtreeFacts(node.Body) |
		core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
		core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
}

func (node *FunctionExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsFunction
}

func (node *AsExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript
}

func (node *AsExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression
}

func (node *SatisfiesExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript
}

func (node *SatisfiesExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression
}

func (node *PropertyAccessExpression) computeSubtreeFacts() SubtreeFacts {
	privateName := SubtreeFactsNone
	if !IsIdentifier(node.name) {
		privateName = SubtreeContainsPrivateIdentifierInExpression
	}
	return propagateSubtreeFacts(node.Expression) |
		propagateSubtreeFacts(node.QuestionDotToken) |
		propagateSubtreeFacts(node.name) | privateName
}

func (node *PropertyAccessExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsPropertyAccess
}

func (node *ElementAccessExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsElementAccess
}

func (node *CallExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) |
		propagateSubtreeFacts(node.QuestionDotToken) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) |
		propagateNodeListSubtreeFacts(node.Arguments, propagateSubtreeFacts) |
		core.IfElse(node.Expression.Kind == KindImportKeyword, SubtreeContainsDynamicImport, SubtreeFactsNone)
}

func (node *CallExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsCall
}

func (node *NewExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) |
		propagateNodeListSubtreeFacts(node.Arguments, propagateSubtreeFacts)
}

func (node *NewExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsNew
}

func (node *MetaProperty) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) &^ SubtreeContainsIdentifier
}

func (node *NonNullExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript
}

func (node *SpreadElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsRestOrSpread
}

func (node *TaggedTemplateExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Tag) |
		propagateSubtreeFacts(node.QuestionDotToken) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) |
		propagateSubtreeFacts(node.Template)
}

// Hand-written subtree facts for nontrivial generated nodes.

func (node *ArrayLiteralExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsArrayLiteral
}

func (node *ObjectLiteralExpression) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsObjectLiteral
}

func (node *SpreadAssignment) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsESObjectRestOrSpread | SubtreeContainsObjectRestOrSpread
}

func (node *PropertyAssignment) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) |
		propagateSubtreeFacts(node.Type) |
		propagateSubtreeFacts(node.Initializer)
}

func (node *ShorthandPropertyAssignment) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) |
		propagateSubtreeFacts(node.Type) |
		propagateSubtreeFacts(node.ObjectAssignmentInitializer) |
		SubtreeContainsTypeScript
}

func (node *AwaitExpression) computeSubtreeFacts() SubtreeFacts {
	// await in an ES2018 async generator must use `yield __await(expr)`
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsAwait | SubtreeContainsAnyAwait | SubtreeContainsForAwaitOrAsyncGenerator
}

func (node *TypeAssertion) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript
}

func (node *TypeAssertion) propagateSubtreeFacts() SubtreeFacts {
	return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression
}

func (node *ExpressionWithTypeArguments) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments)
}

func (node *ImportAttributesNode) GetResolutionModeOverride( /* !!! grammarErrorOnNode?: (node: Node, diagnostic: DiagnosticMessage) => void*/ ) (core.ResolutionMode, bool) {
	if node == nil {
		return core.ResolutionModeNone, false
	}

	attributes := node.AsImportAttributes().Attributes

	if len(attributes.Nodes) != 1 {
		// !!!
		// grammarErrorOnNode?.(
		//     node,
		//     node.token === SyntaxKind.WithKeyword
		//         ? Diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require
		//         : Diagnostics.Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require,
		// );
		return core.ResolutionModeNone, false
	}

	elem := attributes.Nodes[0].AsImportAttribute()
	if !IsStringLiteralLike(elem.Name()) {
		return core.ResolutionModeNone, false
	}
	if elem.Name().Text() != "resolution-mode" {
		// !!!
		// grammarErrorOnNode?.(
		//     elem.name,
		//     node.token === SyntaxKind.WithKeyword
		//         ? Diagnostics.resolution_mode_is_the_only_valid_key_for_type_import_attributes
		//         : Diagnostics.resolution_mode_is_the_only_valid_key_for_type_import_assertions,
		// );
		return core.ResolutionModeNone, false
	}
	if !IsStringLiteralLike(elem.Value) {
		return core.ResolutionModeNone, false
	}
	if elem.Value.Text() != "import" && elem.Value.Text() != "require" {
		// !!!
		// grammarErrorOnNode?.(elem.value, Diagnostics.resolution_mode_should_be_either_require_or_import);
		return core.ResolutionModeNone, false
	}
	if elem.Value.Text() == "import" {
		return core.ResolutionModeESM, true
	} else {
		return core.ModuleKindCommonJS, true
	}
}

// FunctionOrConstructorTypeNodeBase

func (node *FunctionOrConstructorTypeNodeBase) DeclarationData() *DeclarationBase {
	return node.FunctionLikeBase.DeclarationData()
}

func (node *TemplateLiteralLikeNodeBase) LiteralLikeData() *LiteralLikeNodeBase {
	return &node.LiteralLikeNodeBase
}

func (node *TemplateLiteralLikeNodeBase) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase {
	return node
}

func (node *TemplateHead) computeSubtreeFacts() SubtreeFacts {
	if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 {
		return SubtreeContainsInvalidTemplateEscape
	}
	return SubtreeFactsNone
}

func (node *TemplateMiddle) computeSubtreeFacts() SubtreeFacts {
	if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 {
		return SubtreeContainsInvalidTemplateEscape
	}
	return SubtreeFactsNone
}

func (node *TemplateTail) computeSubtreeFacts() SubtreeFacts {
	if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 {
		return SubtreeContainsInvalidTemplateEscape
	}
	return SubtreeFactsNone
}

func (node *JsxElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.OpeningElement) |
		propagateNodeListSubtreeFacts(node.Children, propagateSubtreeFacts) |
		propagateSubtreeFacts(node.ClosingElement) |
		SubtreeContainsJsx
}

func (node *JsxAttributes) computeSubtreeFacts() SubtreeFacts {
	return propagateNodeListSubtreeFacts(node.Properties, propagateSubtreeFacts) |
		SubtreeContainsJsx
}

func (node *JsxNamespacedName) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Namespace) |
		propagateSubtreeFacts(node.name) |
		SubtreeContainsJsx
}

func (node *JsxOpeningElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.TagName) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) |
		propagateSubtreeFacts(node.Attributes) |
		SubtreeContainsJsx
}

func (node *JsxSelfClosingElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.TagName) |
		propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) |
		propagateSubtreeFacts(node.Attributes) |
		SubtreeContainsJsx
}

func (node *JsxFragment) computeSubtreeFacts() SubtreeFacts {
	return propagateNodeListSubtreeFacts(node.Children, propagateSubtreeFacts) |
		SubtreeContainsJsx
}

func (node *JsxOpeningFragment) computeSubtreeFacts() SubtreeFacts {
	return SubtreeContainsJsx
}

func (node *JsxClosingFragment) computeSubtreeFacts() SubtreeFacts {
	return SubtreeContainsJsx
}

func (node *JsxAttribute) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.name) |
		propagateSubtreeFacts(node.Initializer) |
		SubtreeContainsJsx
}

func (node *JsxSpreadAttribute) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsJsx
}

func (node *JsxClosingElement) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.TagName) | SubtreeContainsJsx
}

func (node *JsxExpression) computeSubtreeFacts() SubtreeFacts {
	return propagateSubtreeFacts(node.Expression) | SubtreeContainsJsx
}

func (node *JsxText) computeSubtreeFacts() SubtreeFacts {
	return SubtreeContainsJsx
}

/// JSDoc nodes ///

// JSDoc

func (node *Node) IsJSDoc() bool {
	return node.Kind == KindJSDoc
}

// JSDocText

// PatternAmbientModule

type PatternAmbientModule struct {
	Pattern core.Pattern
	Symbol  *Symbol
}

type CommentDirectiveKind int32

const (
	CommentDirectiveKindUnknown CommentDirectiveKind = iota
	CommentDirectiveKindExpectError
	CommentDirectiveKindIgnore
)

type CommentDirective struct {
	Loc  core.TextRange
	Kind CommentDirectiveKind
}

// SourceFile

type SourceFileMetaData struct {
	PackageJsonType      string
	PackageJsonDirectory string
	ImpliedNodeFormat    core.ResolutionMode
}

// SourceFileDataKey identifies lazily-computed data attached to a SourceFile by
// another package. Prefer regular SourceFile fields for ast-owned data.
type SourceFileDataKey[T any] struct {
	key sourceFileDataKey
	_   [0]T
}

type sourceFileDataKey uint64

var sourceFileDataKeyCounter atomic.Uint64

type sourceFileDataCell[T any] struct {
	once  sync.Once
	value T
}

func NewSourceFileDataKey[T any]() *SourceFileDataKey[T] {
	return &SourceFileDataKey[T]{key: sourceFileDataKey(sourceFileDataKeyCounter.Add(1))}
}

func GetOrComputeSourceFileData[T any](file *SourceFile, key *SourceFileDataKey[T], compute func(*SourceFile) T) T {
	cell := getSourceFileDataCell(file, key)
	cell.once.Do(func() {
		cell.value = compute(file)
	})
	return cell.value
}

func getSourceFileDataCell[T any](file *SourceFile, key *SourceFileDataKey[T]) *sourceFileDataCell[T] {
	if key == nil || key.key == 0 {
		panic("invalid SourceFileDataKey; use NewSourceFileDataKey")
	}

	file.dataMu.Lock()
	defer file.dataMu.Unlock()

	if file.data == nil {
		file.data = make(map[sourceFileDataKey]any)
	}
	if cell, ok := file.data[key.key]; ok {
		return cell.(*sourceFileDataCell[T])
	}
	cell := &sourceFileDataCell[T]{}
	file.data[key.key] = cell
	return cell
}

type CheckJsDirective struct {
	Enabled bool
	Range   CommentRange
}

type HasFileName interface {
	FileName() string
	Path() tspath.Path
}

type TokenCacheKey struct {
	parent *Node
	loc    core.TextRange
}

type SourceFile struct {
	NodeBase
	DeclarationBase
	LocalsContainerBase
	CompositeBase

	// Fields set by NewSourceFile
	fileName       string // For debugging convenience
	parseOptions   SourceFileParseOptions
	text           string
	Statements     *NodeList  // NodeList[*Statement]
	EndOfFileToken *TokenNode // TokenNode[*EndOfFileToken]

	// Fields for lazily-computed data owned by packages outside ast.
	dataMu sync.Mutex
	data   map[sourceFileDataKey]any

	// Fields set by parser
	diagnostics                 []*Diagnostic
	jsDiagnostics               []*Diagnostic
	jsdocDiagnostics            []*Diagnostic
	LanguageVariant             core.LanguageVariant
	ScriptKind                  core.ScriptKind
	IsDeclarationFile           bool
	ContainsNonASCII            bool
	UsesUriStyleNodeCoreModules core.Tristate
	Identifiers                 map[string]string
	IdentifierCount             int
	imports                     []*LiteralLikeNode // []LiteralLikeNode
	ModuleAugmentations         []*ModuleName      // []ModuleName
	AmbientModuleNames          []string
	CommentDirectives           []CommentDirective
	jsdocCache                  map[*Node][]*Node
	jsdocMu                     sync.RWMutex
	hasLazyJSDoc                bool
	ReparsedClones              []*Node
	Pragmas                     []Pragma
	ReferencedFiles             []*FileReference
	TypeReferenceDirectives     []*FileReference
	LibReferenceDirectives      []*FileReference
	CheckJsDirective            *CheckJsDirective
	NodeCount                   int
	TextCount                   int
	CommonJSModuleIndicator     *Node
	// If this is the SourceFile itself, then this module was "forced"
	// to be an external module (previously "true").
	ExternalModuleIndicator *Node

	// Fields set by binder

	isBound                   atomic.Bool
	bindOnce                  sync.Once
	bindDiagnostics           []*Diagnostic
	BindSuggestionDiagnostics []*Diagnostic
	EndFlowNode               *FlowNode
	SymbolCount               int
	ClassifiableNames         collections.Set[string]
	PatternAmbientModules     []*PatternAmbientModule
	GlobalExports             SymbolTable

	// Fields set by ECMALineMap

	ecmaLineMapMu sync.RWMutex
	ecmaLineMap   []core.TextPos

	// Fields set by language service

	Hash             xxh3.Uint128
	tokenCacheMu     sync.Mutex
	tokenCache       map[TokenCacheKey]*Node
	tokenFactory     *NodeFactory
	declarationMapMu sync.Mutex
	declarationMap   map[string][]*Node
	nameTableOnce    sync.Once
	nameTable        map[string]int

	// Fields for UTF-8 to UTF-16 position mapping

	positionMapOnce sync.Once
	positionMap     *PositionMap
}

func (f *NodeFactory) NewSourceFile(opts SourceFileParseOptions, text string, statements *NodeList, endOfFileToken *TokenNode) *Node {
	if tspath.GetEncodedRootLength(opts.FileName) == 0 || opts.FileName != tspath.NormalizePath(opts.FileName) {
		panic(fmt.Sprintf("fileName should be normalized and absolute: %q", opts.FileName))
	}
	data := &SourceFile{}
	data.fileName = opts.FileName
	data.parseOptions = opts
	data.text = text
	data.ContainsNonASCII = stringutil.ContainsNonASCII(text)
	data.Statements = statements
	data.EndOfFileToken = endOfFileToken
	return f.newNode(KindSourceFile, data)
}

func (node *SourceFile) ParseOptions() SourceFileParseOptions {
	return node.parseOptions
}

func (node *SourceFile) Text() string {
	return node.text
}

func (node *SourceFile) FileName() string {
	return node.parseOptions.FileName
}

func (node *SourceFile) Path() tspath.Path {
	return node.parseOptions.Path
}

func (node *SourceFile) Imports() []*LiteralLikeNode {
	return node.imports
}

func (node *SourceFile) Diagnostics() []*Diagnostic {
	return node.diagnostics
}

func (node *SourceFile) SetDiagnostics(diags []*Diagnostic) {
	node.diagnostics = diags
}

func (node *SourceFile) JSDiagnostics() []*Diagnostic {
	return node.jsDiagnostics
}

func (node *SourceFile) SetJSDiagnostics(diags []*Diagnostic) {
	node.jsDiagnostics = diags
}

func (node *SourceFile) JSDocDiagnostics() []*Diagnostic {
	return node.jsdocDiagnostics
}

func (node *SourceFile) SetJSDocDiagnostics(diags []*Diagnostic) {
	node.jsdocDiagnostics = diags
}

func (node *SourceFile) SetJSDocCache(cache map[*Node][]*Node) {
	node.jsdocCache = cache
}

func (node *SourceFile) SetHasLazyJSDoc(lazy bool) {
	node.hasLazyJSDoc = lazy
}

func (node *SourceFile) resolveJSDoc(n *Node) []*Node {
	if parseJSDocForNode == nil {
		panic("resolveJSDoc called but parseJSDocForNode is not registered; ensure the parser package is imported")
	}
	// Fast path: check cache under read lock
	node.jsdocMu.RLock()
	if jsdocs, ok := node.jsdocCache[n]; ok {
		node.jsdocMu.RUnlock()
		return jsdocs
	}
	node.jsdocMu.RUnlock()

	// Slow path: parse and cache under write lock
	node.jsdocMu.Lock()
	defer node.jsdocMu.Unlock()
	// Double-check after acquiring write lock
	if jsdocs, ok := node.jsdocCache[n]; ok {
		return jsdocs
	}
	jsdocs := parseJSDocForNode(node, n)
	if node.jsdocCache == nil {
		node.jsdocCache = make(map[*Node][]*Node)
	}
	node.jsdocCache[n] = jsdocs
	return jsdocs
}

func (node *SourceFile) BindDiagnostics() []*Diagnostic {
	return node.bindDiagnostics
}

func (node *SourceFile) SetBindDiagnostics(diags []*Diagnostic) {
	node.bindDiagnostics = diags
}

func (node *SourceFile) ForEachChild(v Visitor) bool {
	return visitNodeList(v, node.Statements) || visit(v, node.EndOfFileToken)
}

func (node *SourceFile) VisitEachChild(v *NodeVisitor) *Node {
	return v.Factory.UpdateSourceFile(node, v.visitTopLevelStatements(node.Statements), v.visitToken(node.EndOfFileToken))
}

func (node *SourceFile) IsJS() bool {
	return IsSourceFileJS(node)
}

func (node *SourceFile) copyFrom(other *SourceFile) {
	// Do not copy fields set by NewSourceFile (Text, FileName, Path, or Statements)
	node.LanguageVariant = other.LanguageVariant
	node.ScriptKind = other.ScriptKind
	node.IsDeclarationFile = other.IsDeclarationFile
	node.ContainsNonASCII = other.ContainsNonASCII
	node.UsesUriStyleNodeCoreModules = other.UsesUriStyleNodeCoreModules
	node.Identifiers = other.Identifiers
	node.imports = other.imports
	node.ModuleAugmentations = other.ModuleAugmentations
	node.AmbientModuleNames = other.AmbientModuleNames
	node.CommentDirectives = other.CommentDirectives
	node.Pragmas = other.Pragmas
	node.ReferencedFiles = other.ReferencedFiles
	node.TypeReferenceDirectives = other.TypeReferenceDirectives
	node.LibReferenceDirectives = other.LibReferenceDirectives
	node.CommonJSModuleIndicator = other.CommonJSModuleIndicator
	node.ExternalModuleIndicator = other.ExternalModuleIndicator
	node.Flags |= other.Flags
}

func (node *SourceFile) Clone(f NodeFactoryCoercible) *Node {
	updated := f.AsNodeFactory().NewSourceFile(node.parseOptions, node.text, node.Statements, node.EndOfFileToken)
	newFile := updated.AsSourceFile()
	newFile.copyFrom(node)
	return cloneNode(updated, node.AsNode(), f.AsNodeFactory().hooks)
}

func (node *SourceFile) computeSubtreeFacts() SubtreeFacts {
	return propagateNodeListSubtreeFacts(node.Statements, propagateSubtreeFacts)
}

func (f *NodeFactory) UpdateSourceFile(node *SourceFile, statements *StatementList, endOfFileToken *TokenNode) *Node {
	if statements != node.Statements || endOfFileToken != node.EndOfFileToken {
		updated := f.NewSourceFile(node.parseOptions, node.text, statements, endOfFileToken).AsSourceFile()
		updated.copyFrom(node)
		return updateNode(updated.AsNode(), node.AsNode(), f.hooks)
	}
	return node.AsNode()
}

func (node *SourceFile) ECMALineMap() []core.TextPos {
	node.ecmaLineMapMu.RLock()
	lineMap := node.ecmaLineMap
	node.ecmaLineMapMu.RUnlock()
	if lineMap == nil {
		node.ecmaLineMapMu.Lock()
		defer node.ecmaLineMapMu.Unlock()
		lineMap = node.ecmaLineMap
		if lineMap == nil {
			lineMap = core.ComputeECMALineStarts(node.Text())
			node.ecmaLineMap = lineMap
		}
	}
	return lineMap
}

// GetNameTable returns a map of all names in the file to their positions.
// If the name appears more than once, the value is -1.
func (file *SourceFile) GetNameTable() map[string]int {
	file.nameTableOnce.Do(func() {
		nameTable := make(map[string]int, file.IdentifierCount)

		var walk func(node *Node) bool
		walk = func(node *Node) bool {
			if IsIdentifier(node) && !IsTagName(node) && node.Text() != "" ||
				IsStringOrNumericLiteralLike(node) && literalIsName(node) ||
				IsPrivateIdentifier(node) {
				text := node.Text()
				if _, ok := nameTable[text]; ok {
					nameTable[text] = -1
				} else {
					nameTable[text] = node.Pos()
				}
			}

			node.ForEachChild(walk)
			jsdocNodes := node.JSDoc(file)
			for _, jsdoc := range jsdocNodes {
				jsdoc.ForEachChild(walk)
			}
			return false
		}
		file.ForEachChild(walk)

		file.nameTable = nameTable
	})
	return file.nameTable
}

func (node *SourceFile) IsBound() bool {
	return node.isBound.Load()
}

// GetPositionMap returns the PositionMap for this source file, computing it lazily.
func (file *SourceFile) GetPositionMap() *PositionMap {
	file.positionMapOnce.Do(func() {
		if !file.ContainsNonASCII {
			file.positionMap = &PositionMap{asciiOnly: true}
		} else {
			file.positionMap = ComputePositionMap(file.Text())
		}
	})
	return file.positionMap
}

func (node *SourceFile) BindOnce(bind func()) {
	node.bindOnce.Do(func() {
		bind()
		node.isBound.Store(true)
	})
}

// Gets a token from the file's token cache, or creates it if it does not already exist.
// This function should NOT be used for creating synthetic tokens that are not in the file in the first place.
func (node *SourceFile) GetOrCreateToken(
	kind Kind,
	pos int,
	end int,
	parent *Node,
	flags TokenFlags,
) *TokenNode {
	node.tokenCacheMu.Lock()
	defer node.tokenCacheMu.Unlock()
	loc := core.NewTextRange(pos, end)
	key := TokenCacheKey{parent, loc}
	if token, ok := node.tokenCache[key]; ok {
		if token.Kind != kind {
			panic(fmt.Sprintf("Token cache mismatch: %v != %v", token.Kind, kind))
		}
		return token
	}
	if parent.Flags&NodeFlagsReparsed != 0 {
		panic(fmt.Sprintf("Cannot create token from reparsed node of kind %v", parent.Kind))
	}
	if node.tokenCache == nil {
		node.tokenCache = make(map[TokenCacheKey]*Node)
	}
	token := createToken(kind, node, pos, end, flags)
	token.Loc = loc
	token.Parent = parent
	node.tokenCache[key] = token
	return token
}

// `kind` should be a token kind.
func createToken(kind Kind, file *SourceFile, pos, end int, flags TokenFlags) *Node {
	if file.tokenFactory == nil {
		file.tokenFactory = NewNodeFactory(NodeFactoryHooks{})
	}
	text := file.text[pos:end]
	switch kind {
	case KindNumericLiteral:
		return file.tokenFactory.NewNumericLiteral(text, flags)
	case KindBigIntLiteral:
		return file.tokenFactory.NewBigIntLiteral(text, flags)
	case KindStringLiteral:
		return file.tokenFactory.NewStringLiteral(text, flags)
	case KindJsxText, KindJsxTextAllWhiteSpaces:
		return file.tokenFactory.NewJsxText(text, kind == KindJsxTextAllWhiteSpaces)
	case KindRegularExpressionLiteral:
		return file.tokenFactory.NewRegularExpressionLiteral(text, flags)
	case KindNoSubstitutionTemplateLiteral:
		return file.tokenFactory.NewNoSubstitutionTemplateLiteral(text, flags)
	case KindTemplateHead:
		return file.tokenFactory.NewTemplateHead(text, "" /*rawText*/, flags)
	case KindTemplateMiddle:
		return file.tokenFactory.NewTemplateMiddle(text, "" /*rawText*/, flags)
	case KindTemplateTail:
		return file.tokenFactory.NewTemplateTail(text, "" /*rawText*/, flags)
	case KindIdentifier:
		return file.tokenFactory.NewIdentifier(text)
	case KindPrivateIdentifier:
		return file.tokenFactory.NewPrivateIdentifier(text)
	default: // Punctuation and keywords
		return file.tokenFactory.NewToken(kind)
	}
}

func (node *SourceFile) GetDeclarationMap() map[string][]*Node {
	node.declarationMapMu.Lock()
	defer node.declarationMapMu.Unlock()
	if node.declarationMap == nil {
		node.declarationMap = node.computeDeclarationMap()
	}
	return node.declarationMap
}

func (node *SourceFile) computeDeclarationMap() map[string][]*Node {
	result := make(map[string][]*Node)

	addDeclaration := func(declaration *Node) {
		name := GetDeclarationName(declaration)
		if name != "" {
			result[name] = append(result[name], declaration)
		}
	}

	var visit func(*Node) bool
	visit = func(node *Node) bool {
		switch node.Kind {
		case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration, KindMethodSignature:
			declarationName := GetDeclarationName(node)
			if declarationName != "" {
				declarations := result[declarationName]
				var lastDeclaration *Node
				if len(declarations) != 0 {
					lastDeclaration = declarations[len(declarations)-1]
				}
				// Check whether this declaration belongs to an "overload group".
				if lastDeclaration != nil && node.Parent == lastDeclaration.Parent && node.Symbol() == lastDeclaration.Symbol() {
					// Overwrite the last declaration if it was an overload and this one is an implementation.
					if node.Body() != nil && lastDeclaration.Body() == nil {
						declarations[len(declarations)-1] = node
					}
				} else {
					result[declarationName] = append(result[declarationName], node)
				}
			}
			node.ForEachChild(visit)
		case KindClassDeclaration, KindClassExpression, KindInterfaceDeclaration, KindTypeAliasDeclaration, KindEnumDeclaration, KindModuleDeclaration,
			KindImportEqualsDeclaration, KindImportClause, KindNamespaceImport, KindGetAccessor, KindSetAccessor, KindTypeLiteral:
			addDeclaration(node)
			node.ForEachChild(visit)
		case KindImportSpecifier, KindExportSpecifier:
			if node.PropertyName() != nil {
				addDeclaration(node)
			}
		case KindParameter:
			// Only consider parameter properties
			if !HasSyntacticModifier(node, ModifierFlagsParameterPropertyModifier) {
				break
			}
			fallthrough
		case KindVariableDeclaration, KindBindingElement:
			name := node.Name()
			if name != nil {
				if IsBindingPattern(name) {
					node.Name().ForEachChild(visit)
				} else {
					if node.Initializer() != nil {
						visit(node.Initializer())
					}
					addDeclaration(node)
				}
			}
		case KindEnumMember, KindPropertyDeclaration, KindPropertySignature:
			addDeclaration(node)
		case KindExportDeclaration:
			// Handle named exports case e.g.:
			//    export {a, b as B} from "mod";
			exportClause := node.AsExportDeclaration().ExportClause
			if exportClause != nil {
				if IsNamedExports(exportClause) {
					for _, element := range exportClause.Elements() {
						visit(element)
					}
				} else {
					visit(exportClause.AsNamespaceExport().Name())
				}
			}
		case KindImportDeclaration:
			importClause := node.AsImportDeclaration().ImportClause
			if importClause != nil {
				// Handle default import case e.g.:
				//    import d from "mod";
				if importClause.Name() != nil {
					addDeclaration(importClause.Name())
				}
				// Handle named bindings in imports e.g.:
				//    import * as NS from "mod";
				//    import {a, b as B} from "mod";
				namedBindings := importClause.AsImportClause().NamedBindings
				if namedBindings != nil {
					if namedBindings.Kind == KindNamespaceImport {
						addDeclaration(namedBindings)
					} else {
						for _, element := range namedBindings.Elements() {
							visit(element)
						}
					}
				}
			}
		case KindBinaryExpression:
			switch GetAssignmentDeclarationKind(node) {
			case JSDeclarationKindExportsProperty, JSDeclarationKindThisProperty, JSDeclarationKindProperty:
				addDeclaration(node)
			}
			node.ForEachChild(visit)
		default:
			node.ForEachChild(visit)
		}
		return false
	}
	node.ForEachChild(visit)
	return result
}

func GetDeclarationName(declaration *Node) string {
	name := GetNonAssignedNameOfDeclaration(declaration)
	if name != nil {
		if IsComputedPropertyName(name) {
			if IsStringOrNumericLiteralLike(name.Expression()) {
				return name.Expression().Text()
			}
			if IsPropertyAccessExpression(name.Expression()) {
				return name.Expression().Name().Text()
			}
		} else if IsPropertyName(name) {
			return name.Text()
		}
	}
	return ""
}

type SourceFileLike interface {
	Text() string
	ECMALineMap() []core.TextPos
}

type CommentRange struct {
	core.TextRange
	Kind               Kind
	HasTrailingNewLine bool
}

func (f *NodeFactory) NewCommentRange(kind Kind, pos int, end int, hasTrailingNewLine bool) CommentRange {
	return CommentRange{
		TextRange:          core.NewTextRange(pos, end),
		Kind:               kind,
		HasTrailingNewLine: hasTrailingNewLine,
	}
}

type FileReference struct {
	core.TextRange
	FileName       string
	ResolutionMode core.ResolutionMode
	Preserve       bool
}

type PragmaArgument struct {
	core.TextRange
	Name  string
	Value string
}

type Pragma struct {
	CommentRange
	Name string
	Args map[string]PragmaArgument
}

type PragmaKindFlags = uint8

const (
	PragmaKindTripleSlashXML PragmaKindFlags = 1 << iota
	PragmaKindSingleLine
	PragmaKindMultiLine
	PragmaKindFlagsNone PragmaKindFlags = 0
	PragmaKindAll                       = PragmaKindTripleSlashXML | PragmaKindSingleLine | PragmaKindMultiLine
	PragmaKindDefault                   = PragmaKindAll
)

type PragmaArgumentSpecification struct {
	Name        string
	Optional    bool
	CaptureSpan bool
}
type PragmaSpecification struct {
	Args []PragmaArgumentSpecification
	Kind PragmaKindFlags
}

func (spec *PragmaSpecification) IsTripleSlash() bool {
	return (spec.Kind & PragmaKindTripleSlashXML) > 0
}

// Hand-written visitor implementations for nodes with runtime-dependent
// child ordering. Generated code in ast_generated.go delegates to these.

func forEachChild_JSDocParameterOrPropertyTag(node *JSDocParameterOrPropertyTag, v Visitor) bool {
	return visit(v, node.TagName) ||
		(node.IsNameFirst &&
			(visit(v, node.name) || visit(v, node.TypeExpression))) ||
		(!node.IsNameFirst &&
			(visit(v, node.TypeExpression) || visit(v, node.name))) ||
		visitNodeList(v, node.Comment)
}

func visitEachChild_JSDocParameterOrPropertyTag(node *JSDocParameterOrPropertyTag, v *NodeVisitor) *Node {
	return v.Factory.UpdateJSDocParameterOrPropertyTag(node, v.visitNode(node.TagName), v.visitNode(node.name), node.IsBracketed, v.visitNode(node.TypeExpression), node.IsNameFirst, v.visitNodes(node.Comment))
}

func (f *NodeFactory) ReleaseArenas() {
	*f = NodeFactory{
		hooks:     f.hooks,
		textCount: f.textCount,
		nodeCount: f.nodeCount,
	}
}
