Skip to content

Commit 9171aa5

Browse files
authored
fix forEachChildRecursively (microsoft#42300)
1 parent 33046e3 commit 9171aa5

File tree

1 file changed

+39
-46
lines changed

1 file changed

+39
-46
lines changed

src/compiler/parser.ts

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -558,61 +558,54 @@ namespace ts {
558558
* and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
559559
*/
560560
export function forEachChildRecursively<T>(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray<Node>, parent: Node) => T | "skip" | undefined): T | undefined {
561-
562-
const stack: Node[] = [rootNode];
563-
while (stack.length) {
564-
const parent = stack.pop()!;
565-
const res = visitAllPossibleChildren(parent, gatherPossibleChildren(parent));
566-
if (res) {
567-
return res;
568-
}
569-
}
570-
571-
return;
572-
573-
function gatherPossibleChildren(node: Node) {
574-
const children: (Node | NodeArray<Node>)[] = [];
575-
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
576-
return children;
577-
578-
function addWorkItem(n: Node | NodeArray<Node>) {
579-
children.unshift(n);
580-
}
581-
}
582-
583-
function visitAllPossibleChildren(parent: Node, children: readonly (Node | NodeArray<Node>)[]) {
584-
for (const child of children) {
585-
if (isArray(child)) {
586-
if (cbNodes) {
587-
const res = cbNodes(child, parent);
588-
if (res) {
589-
if (res === "skip") continue;
590-
return res;
591-
}
592-
}
593-
594-
for (let i = child.length - 1; i >= 0; i--) {
595-
const realChild = child[i];
596-
const res = cbNode(realChild, parent);
597-
if (res) {
598-
if (res === "skip") continue;
599-
return res;
600-
}
601-
stack.push(realChild);
602-
}
603-
}
604-
else {
605-
stack.push(child);
606-
const res = cbNode(child, parent);
561+
const queue: (Node | NodeArray<Node>)[] = gatherPossibleChildren(rootNode);
562+
const parents: Node[] = []; // tracks parent references for elements in queue
563+
while (parents.length < queue.length) {
564+
parents.push(rootNode);
565+
}
566+
while (queue.length !== 0) {
567+
const current = queue.pop()!;
568+
const parent = parents.pop()!;
569+
if (isArray(current)) {
570+
if (cbNodes) {
571+
const res = cbNodes(current, parent);
607572
if (res) {
608573
if (res === "skip") continue;
609574
return res;
610575
}
611576
}
577+
for (let i = current.length - 1; i >= 0; --i) {
578+
queue.push(current[i]);
579+
parents.push(parent);
580+
}
581+
}
582+
else {
583+
const res = cbNode(current, parent);
584+
if (res) {
585+
if (res === "skip") continue;
586+
return res;
587+
}
588+
if (current.kind >= SyntaxKind.FirstNode) {
589+
// add children in reverse order to the queue, so popping gives the first child
590+
for (const child of gatherPossibleChildren(current)) {
591+
queue.push(child);
592+
parents.push(current);
593+
}
594+
}
612595
}
613596
}
614597
}
615598

599+
function gatherPossibleChildren(node: Node) {
600+
const children: (Node | NodeArray<Node>)[] = [];
601+
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
602+
return children;
603+
604+
function addWorkItem(n: Node | NodeArray<Node>) {
605+
children.unshift(n);
606+
}
607+
}
608+
616609
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
617610
tracing.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true);
618611
performance.mark("beforeParse");

0 commit comments

Comments
 (0)