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);
}
// 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
*/
@ -47,14 +52,24 @@ export function _getTypeAnnotation(): ?Object {
return node.typeAnnotation;
}
let inferer = inferers[node.type];
if (inferer) {
return inferer.call(this, node);
if (typeAnnotationInferringNodes.has(node)) {
// Bail out from type inference to avoid infinite loops
return;
}
typeAnnotationInferringNodes.add(node);
inferer = inferers[this.parentPath.type];
if (inferer?.validParent) {
return this.parentPath.getTypeAnnotation();
try {
let inferer = inferers[node.type];
if (inferer) {
return inferer.call(this, node);
}
inferer = inferers[this.parentPath.type];
if (inferer?.validParent) {
return this.parentPath.getTypeAnnotation();
}
} finally {
typeAnnotationInferringNodes.delete(node);
}
}

View File

@ -289,5 +289,21 @@ describe("inference", function () {
const type = path.getTypeAnnotation();
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" });
});
});
});