Avoid infinite loops in type inference logic (#12390)

This commit is contained in:
Nicolò Ribaudo 2020-11-23 17:03:01 +01:00 committed by GitHub
parent 243d3b2dfd
commit 4f9ad5cc3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 6 deletions

View File

@ -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,6 +52,13 @@ export function _getTypeAnnotation(): ?Object {
return node.typeAnnotation; return node.typeAnnotation;
} }
if (typeAnnotationInferringNodes.has(node)) {
// Bail out from type inference to avoid infinite loops
return;
}
typeAnnotationInferringNodes.add(node);
try {
let inferer = inferers[node.type]; let inferer = inferers[node.type];
if (inferer) { if (inferer) {
return inferer.call(this, node); return inferer.call(this, node);
@ -56,6 +68,9 @@ export function _getTypeAnnotation(): ?Object {
if (inferer?.validParent) { if (inferer?.validParent) {
return this.parentPath.getTypeAnnotation(); return this.parentPath.getTypeAnnotation();
} }
} finally {
typeAnnotationInferringNodes.delete(node);
}
} }
export function isBaseType(baseName: string, soft?: boolean): boolean { export function isBaseType(baseName: string, soft?: boolean): boolean {

View File

@ -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" });
});
}); });
}); });