diff --git a/_scripts/generate-go-ast.ts b/_scripts/generate-go-ast.ts index 0be1fb223bb..bdae93dd19c 100644 --- a/_scripts/generate-go-ast.ts +++ b/_scripts/generate-go-ast.ts @@ -478,6 +478,54 @@ function generateForEachChild(w: CodeWriter, node: NodeType) { w.write(""); } +// ── Generate ForEachChild dispatch ───────────────────────────────────────── + +// Determines whether a node has a meaningful ForEachChild implementation (i.e. +// one that is not the no-op inherited from NodeDefault). This is true for any +// generated node with child members, or for hand-written nodes (e.g. SourceFile) +// which define ForEachChild manually in ast.go. +function hasForEachChild(node: NodeType): boolean { + if (node.handWritten) return true; + return schemaMembers(node).some(m => m.isChild()); +} + +// Generates a (*Node).ForEachChild method that dispatches on node.Kind to the +// concrete node type's ForEachChild method. +// +// This deliberately avoids calling node.data.ForEachChild(v) through the +// nodeData interface. An interface (or other indirect) call is opaque to escape +// analysis, which must then assume the visitor `v` escapes; that forces caller +// closures — and any locals they capture — onto the heap. Dispatching through a +// Kind switch to a statically-resolved concrete method lets escape analysis +// prove the visitor does not escape, keeping caller closures on the stack. The +// integer switch over Kind also compiles to a jump table, making dispatch +// cheaper than the interface call it replaces. +// +// Kinds whose node has no children fall through to `default` and return false, +// matching NodeDefault.ForEachChild. +function generateForEachChildDispatch(w: CodeWriter) { + w.write("func (n *Node) ForEachChild(v Visitor) bool {"); + w.push(); + w.write("switch n.Kind {"); + for (const node of api.nodes()) { + if (!hasForEachChild(node)) continue; + const kinds = node.allKinds().map(k => k.formatGoConstant()); + if (kinds.length === 0) continue; + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`return n.data.(*${node.name}).ForEachChild(v)`); + w.pop(); + } + w.write("default:"); + w.push(); + w.write("return false"); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + // ── Generate VisitEachChild() ────────────────────────────────────────────── function generateVisitEachChild(w: CodeWriter, node: NodeType) { @@ -824,6 +872,13 @@ function generate(): string { } } + // ForEachChild dispatch + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// ForEachChild dispatch"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + generateForEachChildDispatch(w); + // As*() casts w.write("// ──────────────────────────────────────────────────────────────────────"); w.write("// As*() cast methods"); diff --git a/internal/ast/ast.go b/internal/ast/ast.go index f4fd6dcde8a..65186491f96 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -188,11 +188,21 @@ type Node struct { // 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) ForEachChild(v Visitor) bool { return n.data.ForEachChild(v) } -func (n *Node) IterChildren() iter.Seq[*Node] { return n.data.IterChildren() } +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() } @@ -1170,7 +1180,6 @@ func (n *Node) AsFlowReduceLabelData() *FlowReduceLabelData { type nodeData interface { AsNode() *Node ForEachChild(v Visitor) bool - IterChildren() iter.Seq[*Node] VisitEachChild(v *NodeVisitor) *Node Clone(v NodeFactoryCoercible) *Node Name() *DeclarationName @@ -1197,21 +1206,8 @@ type NodeDefault struct { Node } -func invert(yield func(v *Node) bool) Visitor { - return func(n *Node) bool { - return !yield(n) - } -} - func (node *NodeDefault) AsNode() *Node { return &node.Node } func (node *NodeDefault) ForEachChild(v Visitor) bool { return false } -func (node *NodeDefault) forEachChildIter(yield func(v *Node) bool) { - node.data.ForEachChild(invert(yield)) // `true` is return early for a ts visitor, `false` is return early for a go iterator yield function -} - -func (node *NodeDefault) IterChildren() iter.Seq[*Node] { - return node.forEachChildIter -} func (node *NodeDefault) VisitEachChild(v *NodeVisitor) *Node { return node.AsNode() } func (node *NodeDefault) Clone(v NodeFactoryCoercible) *Node { return nil } diff --git a/internal/ast/ast_generated.go b/internal/ast/ast_generated.go index 7567b2e7833..6a3f383eec4 100644 --- a/internal/ast/ast_generated.go +++ b/internal/ast/ast_generated.go @@ -8855,6 +8855,351 @@ func IsJSDocPropertyTag(node *Node) bool { return node.Kind == KindJSDocPropertyTag } +// ────────────────────────────────────────────────────────────────────── +// ForEachChild dispatch +// ────────────────────────────────────────────────────────────────────── + +func (n *Node) ForEachChild(v Visitor) bool { + switch n.Kind { + case KindQualifiedName: + return n.data.(*QualifiedName).ForEachChild(v) + case KindComputedPropertyName: + return n.data.(*ComputedPropertyName).ForEachChild(v) + case KindDecorator: + return n.data.(*Decorator).ForEachChild(v) + case KindIfStatement: + return n.data.(*IfStatement).ForEachChild(v) + case KindDoStatement: + return n.data.(*DoStatement).ForEachChild(v) + case KindWhileStatement: + return n.data.(*WhileStatement).ForEachChild(v) + case KindForStatement: + return n.data.(*ForStatement).ForEachChild(v) + case KindForInStatement, KindForOfStatement: + return n.data.(*ForInOrOfStatement).ForEachChild(v) + case KindBreakStatement: + return n.data.(*BreakStatement).ForEachChild(v) + case KindContinueStatement: + return n.data.(*ContinueStatement).ForEachChild(v) + case KindReturnStatement: + return n.data.(*ReturnStatement).ForEachChild(v) + case KindWithStatement: + return n.data.(*WithStatement).ForEachChild(v) + case KindSwitchStatement: + return n.data.(*SwitchStatement).ForEachChild(v) + case KindCaseBlock: + return n.data.(*CaseBlock).ForEachChild(v) + case KindCaseClause, KindDefaultClause: + return n.data.(*CaseOrDefaultClause).ForEachChild(v) + case KindThrowStatement: + return n.data.(*ThrowStatement).ForEachChild(v) + case KindTryStatement: + return n.data.(*TryStatement).ForEachChild(v) + case KindCatchClause: + return n.data.(*CatchClause).ForEachChild(v) + case KindLabeledStatement: + return n.data.(*LabeledStatement).ForEachChild(v) + case KindExpressionStatement: + return n.data.(*ExpressionStatement).ForEachChild(v) + case KindBlock: + return n.data.(*Block).ForEachChild(v) + case KindVariableStatement: + return n.data.(*VariableStatement).ForEachChild(v) + case KindVariableDeclaration: + return n.data.(*VariableDeclaration).ForEachChild(v) + case KindVariableDeclarationList: + return n.data.(*VariableDeclarationList).ForEachChild(v) + case KindObjectBindingPattern, KindArrayBindingPattern: + return n.data.(*BindingPattern).ForEachChild(v) + case KindParameter: + return n.data.(*ParameterDeclaration).ForEachChild(v) + case KindBindingElement: + return n.data.(*BindingElement).ForEachChild(v) + case KindMissingDeclaration: + return n.data.(*MissingDeclaration).ForEachChild(v) + case KindFunctionDeclaration: + return n.data.(*FunctionDeclaration).ForEachChild(v) + case KindClassDeclaration: + return n.data.(*ClassDeclaration).ForEachChild(v) + case KindClassExpression: + return n.data.(*ClassExpression).ForEachChild(v) + case KindHeritageClause: + return n.data.(*HeritageClause).ForEachChild(v) + case KindInterfaceDeclaration: + return n.data.(*InterfaceDeclaration).ForEachChild(v) + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + return n.data.(*TypeAliasDeclaration).ForEachChild(v) + case KindEnumMember: + return n.data.(*EnumMember).ForEachChild(v) + case KindEnumDeclaration: + return n.data.(*EnumDeclaration).ForEachChild(v) + case KindModuleBlock: + return n.data.(*ModuleBlock).ForEachChild(v) + case KindImportDeclaration, KindJSImportDeclaration: + return n.data.(*ImportDeclaration).ForEachChild(v) + case KindExternalModuleReference: + return n.data.(*ExternalModuleReference).ForEachChild(v) + case KindNamespaceImport: + return n.data.(*NamespaceImport).ForEachChild(v) + case KindNamedImports: + return n.data.(*NamedImports).ForEachChild(v) + case KindExportAssignment: + return n.data.(*ExportAssignment).ForEachChild(v) + case KindNamespaceExportDeclaration: + return n.data.(*NamespaceExportDeclaration).ForEachChild(v) + case KindNamespaceExport: + return n.data.(*NamespaceExport).ForEachChild(v) + case KindNamedExports: + return n.data.(*NamedExports).ForEachChild(v) + case KindExportSpecifier: + return n.data.(*ExportSpecifier).ForEachChild(v) + case KindCallSignature: + return n.data.(*CallSignatureDeclaration).ForEachChild(v) + case KindConstructSignature: + return n.data.(*ConstructSignatureDeclaration).ForEachChild(v) + case KindConstructor: + return n.data.(*ConstructorDeclaration).ForEachChild(v) + case KindGetAccessor: + return n.data.(*GetAccessorDeclaration).ForEachChild(v) + case KindSetAccessor: + return n.data.(*SetAccessorDeclaration).ForEachChild(v) + case KindIndexSignature: + return n.data.(*IndexSignatureDeclaration).ForEachChild(v) + case KindMethodSignature: + return n.data.(*MethodSignatureDeclaration).ForEachChild(v) + case KindMethodDeclaration: + return n.data.(*MethodDeclaration).ForEachChild(v) + case KindPropertySignature: + return n.data.(*PropertySignatureDeclaration).ForEachChild(v) + case KindPropertyDeclaration: + return n.data.(*PropertyDeclaration).ForEachChild(v) + case KindClassStaticBlockDeclaration: + return n.data.(*ClassStaticBlockDeclaration).ForEachChild(v) + case KindBinaryExpression: + return n.data.(*BinaryExpression).ForEachChild(v) + case KindPrefixUnaryExpression: + return n.data.(*PrefixUnaryExpression).ForEachChild(v) + case KindPostfixUnaryExpression: + return n.data.(*PostfixUnaryExpression).ForEachChild(v) + case KindYieldExpression: + return n.data.(*YieldExpression).ForEachChild(v) + case KindArrowFunction: + return n.data.(*ArrowFunction).ForEachChild(v) + case KindFunctionExpression: + return n.data.(*FunctionExpression).ForEachChild(v) + case KindAsExpression: + return n.data.(*AsExpression).ForEachChild(v) + case KindSatisfiesExpression: + return n.data.(*SatisfiesExpression).ForEachChild(v) + case KindConditionalExpression: + return n.data.(*ConditionalExpression).ForEachChild(v) + case KindPropertyAccessExpression: + return n.data.(*PropertyAccessExpression).ForEachChild(v) + case KindElementAccessExpression: + return n.data.(*ElementAccessExpression).ForEachChild(v) + case KindCallExpression: + return n.data.(*CallExpression).ForEachChild(v) + case KindNewExpression: + return n.data.(*NewExpression).ForEachChild(v) + case KindMetaProperty: + return n.data.(*MetaProperty).ForEachChild(v) + case KindNonNullExpression: + return n.data.(*NonNullExpression).ForEachChild(v) + case KindSpreadElement: + return n.data.(*SpreadElement).ForEachChild(v) + case KindTemplateExpression: + return n.data.(*TemplateExpression).ForEachChild(v) + case KindTemplateSpan: + return n.data.(*TemplateSpan).ForEachChild(v) + case KindTaggedTemplateExpression: + return n.data.(*TaggedTemplateExpression).ForEachChild(v) + case KindParenthesizedExpression: + return n.data.(*ParenthesizedExpression).ForEachChild(v) + case KindArrayLiteralExpression: + return n.data.(*ArrayLiteralExpression).ForEachChild(v) + case KindObjectLiteralExpression: + return n.data.(*ObjectLiteralExpression).ForEachChild(v) + case KindSpreadAssignment: + return n.data.(*SpreadAssignment).ForEachChild(v) + case KindPropertyAssignment: + return n.data.(*PropertyAssignment).ForEachChild(v) + case KindShorthandPropertyAssignment: + return n.data.(*ShorthandPropertyAssignment).ForEachChild(v) + case KindDeleteExpression: + return n.data.(*DeleteExpression).ForEachChild(v) + case KindTypeOfExpression: + return n.data.(*TypeOfExpression).ForEachChild(v) + case KindVoidExpression: + return n.data.(*VoidExpression).ForEachChild(v) + case KindAwaitExpression: + return n.data.(*AwaitExpression).ForEachChild(v) + case KindTypeAssertionExpression: + return n.data.(*TypeAssertion).ForEachChild(v) + case KindUnionType: + return n.data.(*UnionTypeNode).ForEachChild(v) + case KindIntersectionType: + return n.data.(*IntersectionTypeNode).ForEachChild(v) + case KindConditionalType: + return n.data.(*ConditionalTypeNode).ForEachChild(v) + case KindTypeOperator: + return n.data.(*TypeOperatorNode).ForEachChild(v) + case KindInferType: + return n.data.(*InferTypeNode).ForEachChild(v) + case KindArrayType: + return n.data.(*ArrayTypeNode).ForEachChild(v) + case KindIndexedAccessType: + return n.data.(*IndexedAccessTypeNode).ForEachChild(v) + case KindTypeReference: + return n.data.(*TypeReferenceNode).ForEachChild(v) + case KindExpressionWithTypeArguments: + return n.data.(*ExpressionWithTypeArguments).ForEachChild(v) + case KindLiteralType: + return n.data.(*LiteralTypeNode).ForEachChild(v) + case KindTypePredicate: + return n.data.(*TypePredicateNode).ForEachChild(v) + case KindImportAttribute: + return n.data.(*ImportAttribute).ForEachChild(v) + case KindImportAttributes: + return n.data.(*ImportAttributes).ForEachChild(v) + case KindTypeQuery: + return n.data.(*TypeQueryNode).ForEachChild(v) + case KindMappedType: + return n.data.(*MappedTypeNode).ForEachChild(v) + case KindTypeLiteral: + return n.data.(*TypeLiteralNode).ForEachChild(v) + case KindTupleType: + return n.data.(*TupleTypeNode).ForEachChild(v) + case KindNamedTupleMember: + return n.data.(*NamedTupleMember).ForEachChild(v) + case KindOptionalType: + return n.data.(*OptionalTypeNode).ForEachChild(v) + case KindRestType: + return n.data.(*RestTypeNode).ForEachChild(v) + case KindParenthesizedType: + return n.data.(*ParenthesizedTypeNode).ForEachChild(v) + case KindFunctionType: + return n.data.(*FunctionTypeNode).ForEachChild(v) + case KindConstructorType: + return n.data.(*ConstructorTypeNode).ForEachChild(v) + case KindTemplateLiteralType: + return n.data.(*TemplateLiteralTypeNode).ForEachChild(v) + case KindTemplateLiteralTypeSpan: + return n.data.(*TemplateLiteralTypeSpan).ForEachChild(v) + case KindSyntheticExpression: + return n.data.(*SyntheticExpression).ForEachChild(v) + case KindPartiallyEmittedExpression: + return n.data.(*PartiallyEmittedExpression).ForEachChild(v) + case KindJsxElement: + return n.data.(*JsxElement).ForEachChild(v) + case KindJsxAttributes: + return n.data.(*JsxAttributes).ForEachChild(v) + case KindJsxNamespacedName: + return n.data.(*JsxNamespacedName).ForEachChild(v) + case KindJsxOpeningElement: + return n.data.(*JsxOpeningElement).ForEachChild(v) + case KindJsxSelfClosingElement: + return n.data.(*JsxSelfClosingElement).ForEachChild(v) + case KindJsxFragment: + return n.data.(*JsxFragment).ForEachChild(v) + case KindJsxAttribute: + return n.data.(*JsxAttribute).ForEachChild(v) + case KindJsxSpreadAttribute: + return n.data.(*JsxSpreadAttribute).ForEachChild(v) + case KindJsxClosingElement: + return n.data.(*JsxClosingElement).ForEachChild(v) + case KindJsxExpression: + return n.data.(*JsxExpression).ForEachChild(v) + case KindSyntaxList: + return n.data.(*SyntaxList).ForEachChild(v) + case KindJSDoc: + return n.data.(*JSDoc).ForEachChild(v) + case KindJSDocTypeExpression: + return n.data.(*JSDocTypeExpression).ForEachChild(v) + case KindJSDocNonNullableType: + return n.data.(*JSDocNonNullableType).ForEachChild(v) + case KindJSDocNullableType: + return n.data.(*JSDocNullableType).ForEachChild(v) + case KindJSDocVariadicType: + return n.data.(*JSDocVariadicType).ForEachChild(v) + case KindJSDocOptionalType: + return n.data.(*JSDocOptionalType).ForEachChild(v) + case KindJSDocTypeTag: + return n.data.(*JSDocTypeTag).ForEachChild(v) + case KindJSDocUnknownTag: + return n.data.(*JSDocUnknownTag).ForEachChild(v) + case KindJSDocTemplateTag: + return n.data.(*JSDocTemplateTag).ForEachChild(v) + case KindJSDocReturnTag: + return n.data.(*JSDocReturnTag).ForEachChild(v) + case KindJSDocPublicTag: + return n.data.(*JSDocPublicTag).ForEachChild(v) + case KindJSDocPrivateTag: + return n.data.(*JSDocPrivateTag).ForEachChild(v) + case KindJSDocProtectedTag: + return n.data.(*JSDocProtectedTag).ForEachChild(v) + case KindJSDocReadonlyTag: + return n.data.(*JSDocReadonlyTag).ForEachChild(v) + case KindJSDocOverrideTag: + return n.data.(*JSDocOverrideTag).ForEachChild(v) + case KindJSDocDeprecatedTag: + return n.data.(*JSDocDeprecatedTag).ForEachChild(v) + case KindJSDocSeeTag: + return n.data.(*JSDocSeeTag).ForEachChild(v) + case KindJSDocImplementsTag: + return n.data.(*JSDocImplementsTag).ForEachChild(v) + case KindJSDocAugmentsTag: + return n.data.(*JSDocAugmentsTag).ForEachChild(v) + case KindJSDocSatisfiesTag: + return n.data.(*JSDocSatisfiesTag).ForEachChild(v) + case KindJSDocThrowsTag: + return n.data.(*JSDocThrowsTag).ForEachChild(v) + case KindJSDocThisTag: + return n.data.(*JSDocThisTag).ForEachChild(v) + case KindJSDocImportTag: + return n.data.(*JSDocImportTag).ForEachChild(v) + case KindJSDocCallbackTag: + return n.data.(*JSDocCallbackTag).ForEachChild(v) + case KindJSDocOverloadTag: + return n.data.(*JSDocOverloadTag).ForEachChild(v) + case KindJSDocTypedefTag: + return n.data.(*JSDocTypedefTag).ForEachChild(v) + case KindJSDocSignature: + return n.data.(*JSDocSignature).ForEachChild(v) + case KindJSDocNameReference: + return n.data.(*JSDocNameReference).ForEachChild(v) + case KindSourceFile: + return n.data.(*SourceFile).ForEachChild(v) + case KindModuleDeclaration: + return n.data.(*ModuleDeclaration).ForEachChild(v) + case KindImportEqualsDeclaration: + return n.data.(*ImportEqualsDeclaration).ForEachChild(v) + case KindExportDeclaration: + return n.data.(*ExportDeclaration).ForEachChild(v) + case KindImportType: + return n.data.(*ImportTypeNode).ForEachChild(v) + case KindImportClause: + return n.data.(*ImportClause).ForEachChild(v) + case KindImportSpecifier: + return n.data.(*ImportSpecifier).ForEachChild(v) + case KindJSDocLink: + return n.data.(*JSDocLink).ForEachChild(v) + case KindJSDocLinkPlain: + return n.data.(*JSDocLinkPlain).ForEachChild(v) + case KindJSDocLinkCode: + return n.data.(*JSDocLinkCode).ForEachChild(v) + case KindTypeParameter: + return n.data.(*TypeParameterDeclaration).ForEachChild(v) + case KindSyntheticReferenceExpression: + return n.data.(*SyntheticReferenceExpression).ForEachChild(v) + case KindJSDocTypeLiteral: + return n.data.(*JSDocTypeLiteral).ForEachChild(v) + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.data.(*JSDocParameterOrPropertyTag).ForEachChild(v) + default: + return false + } +} + // ────────────────────────────────────────────────────────────────────── // As*() cast methods // ──────────────────────────────────────────────────────────────────────