fix forEachChildRecursively (#42300)

This commit is contained in:
Klaus Meinhardt
2021-01-13 03:20:46 -08:00
committed by GitHub
parent 33046e389a
commit 9171aa5a1b
+38 -45
View File
@@ -558,58 +558,51 @@ namespace ts {
* and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
*/
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 {
const stack: Node[] = [rootNode];
while (stack.length) {
const parent = stack.pop()!;
const res = visitAllPossibleChildren(parent, gatherPossibleChildren(parent));
if (res) {
return res;
}
const queue: (Node | NodeArray<Node>)[] = gatherPossibleChildren(rootNode);
const parents: Node[] = []; // tracks parent references for elements in queue
while (parents.length < queue.length) {
parents.push(rootNode);
}
return;
function gatherPossibleChildren(node: Node) {
const children: (Node | NodeArray<Node>)[] = [];
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
return children;
function addWorkItem(n: Node | NodeArray<Node>) {
children.unshift(n);
}
}
function visitAllPossibleChildren(parent: Node, children: readonly (Node | NodeArray<Node>)[]) {
for (const child of children) {
if (isArray(child)) {
if (cbNodes) {
const res = cbNodes(child, parent);
if (res) {
if (res === "skip") continue;
return res;
}
}
for (let i = child.length - 1; i >= 0; i--) {
const realChild = child[i];
const res = cbNode(realChild, parent);
if (res) {
if (res === "skip") continue;
return res;
}
stack.push(realChild);
}
}
else {
stack.push(child);
const res = cbNode(child, parent);
while (queue.length !== 0) {
const current = queue.pop()!;
const parent = parents.pop()!;
if (isArray(current)) {
if (cbNodes) {
const res = cbNodes(current, parent);
if (res) {
if (res === "skip") continue;
return res;
}
}
for (let i = current.length - 1; i >= 0; --i) {
queue.push(current[i]);
parents.push(parent);
}
}
else {
const res = cbNode(current, parent);
if (res) {
if (res === "skip") continue;
return res;
}
if (current.kind >= SyntaxKind.FirstNode) {
// add children in reverse order to the queue, so popping gives the first child
for (const child of gatherPossibleChildren(current)) {
queue.push(child);
parents.push(current);
}
}
}
}
}
function gatherPossibleChildren(node: Node) {
const children: (Node | NodeArray<Node>)[] = [];
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
return children;
function addWorkItem(n: Node | NodeArray<Node>) {
children.unshift(n);
}
}