Avoid infinite loops in type inference logic (#12390)
This commit is contained in:
parent
243d3b2dfd
commit
4f9ad5cc3a
@ -14,6 +14,11 @@ export function getTypeAnnotation(): Object {
|
|||||||
return (this.typeAnnotation = type);
|
return (this.typeAnnotation = type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to avoid infinite recursion in cases like
|
||||||
|
// var b, c; if (0) { c = 1; b = c; } c = b;
|
||||||
|
// It also works with indirect recursion.
|
||||||
|
const typeAnnotationInferringNodes = new WeakSet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* todo: split up this method
|
* todo: split up this method
|
||||||
*/
|
*/
|
||||||
@ -47,14 +52,24 @@ export function _getTypeAnnotation(): ?Object {
|
|||||||
return node.typeAnnotation;
|
return node.typeAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inferer = inferers[node.type];
|
if (typeAnnotationInferringNodes.has(node)) {
|
||||||
if (inferer) {
|
// Bail out from type inference to avoid infinite loops
|
||||||
return inferer.call(this, node);
|
return;
|
||||||
}
|
}
|
||||||
|
typeAnnotationInferringNodes.add(node);
|
||||||
|
|
||||||
inferer = inferers[this.parentPath.type];
|
try {
|
||||||
if (inferer?.validParent) {
|
let inferer = inferers[node.type];
|
||||||
return this.parentPath.getTypeAnnotation();
|
if (inferer) {
|
||||||
|
return inferer.call(this, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
inferer = inferers[this.parentPath.type];
|
||||||
|
if (inferer?.validParent) {
|
||||||
|
return this.parentPath.getTypeAnnotation();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
typeAnnotationInferringNodes.delete(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -289,5 +289,21 @@ describe("inference", function () {
|
|||||||
const type = path.getTypeAnnotation();
|
const type = path.getTypeAnnotation();
|
||||||
expect(t.isAnyTypeAnnotation(type)).toBeTruthy();
|
expect(t.isAnyTypeAnnotation(type)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
it("should not cause a stack overflow when two variable depend on eachother", function () {
|
||||||
|
const path = getPath(`
|
||||||
|
var b, c;
|
||||||
|
while (0) {
|
||||||
|
c = 1;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
c = b;
|
||||||
|
`).get("body.2.expression");
|
||||||
|
|
||||||
|
expect(path.toString()).toBe("c = b");
|
||||||
|
|
||||||
|
// Note: this could technically be "number | void", but the cycle detection
|
||||||
|
// logic just bails out to "any" to avoid infinite loops.
|
||||||
|
expect(path.getTypeAnnotation()).toEqual({ type: "AnyTypeAnnotation" });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user