Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: infer array type from outside of arrow function #12727

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,10 @@
const keys = []
export const foo = () => {
for (const key of keys) {}
}

export const bar = () => {
const keys = []
for (const key of keys) {}
}

@@ -0,0 +1,22 @@
const keys = [];
export const foo = () => {
var _iterator = babelHelpers.createForOfIteratorHelper(keys),
_step;

try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
const key = _step.value;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
};
export const bar = () => {
const keys = [];

for (var _i = 0, _keys = keys; _i < _keys.length; _i++) {
const key = _keys[_i];
}
};
@@ -0,0 +1,17 @@
const keys = []

function a() {
for (const key of keys) {}
}

const b = () => {
for (const key of keys) {}
}

const c = function () {
for (const key of keys) {}
}

const { foo } = () => {
for (const key of keys) {}
}
@@ -0,0 +1,36 @@
const keys = [];

function a() {
for (var _i = 0, _keys = keys; _i < _keys.length; _i++) {
const key = _keys[_i];
}
}

const b = () => {
for (var _i2 = 0, _keys2 = keys; _i2 < _keys2.length; _i2++) {
const key = _keys2[_i2];
}
};

const c = function () {
for (var _i3 = 0, _keys3 = keys; _i3 < _keys3.length; _i3++) {
const key = _keys3[_i3];
}
};

const {
foo
} = () => {
var _iterator = babelHelpers.createForOfIteratorHelper(keys),
_step;

try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
const key = _step.value;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
};
@@ -0,0 +1,3 @@
const foo = []

for (const i of foo) {}
@@ -0,0 +1,5 @@
const foo = [];

for (var _i = 0, _foo = foo; _i < _foo.length; _i++) {
const i = _foo[_i];
}
@@ -0,0 +1,24 @@
const keys = []
const d = () => {
const e = () => {
for (const key of keys) {}
}
}

const a = () => {
const keys = []
const c = () => {
for (const key of keys) {}
}
return c
}

var _keys = []
const b = () => {
const c = () => {
for (const key of _keys) {}
}
return c
}

_keys = 'foo'
@@ -0,0 +1,44 @@
const keys = [];

const d = () => {
const e = () => {
for (var _i = 0, _keys2 = keys; _i < _keys2.length; _i++) {
const key = _keys2[_i];
}
};
};

const a = () => {
const keys = [];

const c = () => {
for (var _i2 = 0, _keys3 = keys; _i2 < _keys3.length; _i2++) {
const key = _keys3[_i2];
}
};

return c;
};

var _keys = [];

const b = () => {
const c = () => {
var _iterator = babelHelpers.createForOfIteratorHelper(_keys),
_step;

try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
const key = _step.value;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
};

return c;
};

_keys = 'foo';
18 changes: 14 additions & 4 deletions packages/babel-traverse/src/path/introspection.ts
Expand Up @@ -219,7 +219,7 @@ export function willIMaybeExecuteBefore(this: NodePath, target): boolean {
return this._guessExecutionStatusRelativeTo(target) !== "after";
}

function getOuterFunction(path) {
function getOuterFunction(path: NodePath) {
return (path.scope.getFunctionParent() || path.scope.getProgramParent()).path;
}

Expand Down Expand Up @@ -385,8 +385,12 @@ export function _guessExecutionStatusRelativeToDifferentFunctions(
this: NodePath,
target: NodePath,
): RelativeExecutionStatus {
const targetIsGenericFunctionExpression =
(target.isArrowFunctionExpression() || target.isFunctionExpression()) &&
t.isVariableDeclarator(target.parent) &&
t.isIdentifier(target.parent.id);
if (
!target.isFunctionDeclaration() ||
(!target.isFunctionDeclaration() && !targetIsGenericFunctionExpression) ||
target.parentPath.isExportDeclaration()
) {
return "unknown";
Expand All @@ -396,7 +400,10 @@ export function _guessExecutionStatusRelativeToDifferentFunctions(
// then we can be a bit smarter and handle cases where the function is either
// a. not called at all (part of an export)
// b. called directly
const binding = target.scope.getBinding(target.node.id.name);
const bindingName = targetIsGenericFunctionExpression
? ((target.parent as t.VariableDeclarator).id as t.Identifier).name
: (target.node as t.FunctionDeclaration).id.name;
const binding = target.scope.getBinding(bindingName);

// no references!
if (!binding.references) return "before";
Expand All @@ -412,7 +419,10 @@ export function _guessExecutionStatusRelativeToDifferentFunctions(
const childOfFunction = !!path.find(path => path.node === target.node);
if (childOfFunction) continue;

if (path.key !== "callee" || !path.parentPath.isCallExpression()) {
if (
(path.key !== "callee" || !path.parentPath.isCallExpression()) &&
!path.parentPath.isReturnStatement()
) {
// This function is passed as a reference, so we don't
// know when it will be called.
return "unknown";
Expand Down
30 changes: 30 additions & 0 deletions packages/babel-traverse/test/inference.js
Expand Up @@ -149,6 +149,36 @@ describe("inference", function () {
t.isGenericTypeAnnotation(type) && type.id.name === "Function",
).toBeTruthy();
});
it("should infer Array type from outside of function declaration", function () {
const path = getPath(`
const arr = []; function func() { const a = arr }`).get(
"body.1.body.body.0.declarations.0",
);
const type = path.getTypeAnnotation();
expect(
t.isGenericTypeAnnotation(type) && type.id.name === "Array",
).toBeTruthy();
});
it("should infer Array type from outside of function expression", function () {
const path = getPath(`
const arr = []; const func = function () { const a = arr }`).get(
"body.1.declarations.0.init.body.body.0.declarations.0",
);
const type = path.getTypeAnnotation();
expect(
t.isGenericTypeAnnotation(type) && type.id.name === "Array",
).toBeTruthy();
});
it("should infer Array type from outside of arrow function", function () {
const path = getPath(`
const arr = []; const func = () => { const a = arr }`).get(
"body.1.declarations.0.init.body.body.0.declarations.0",
);
const type = path.getTypeAnnotation();
expect(
t.isGenericTypeAnnotation(type) && type.id.name === "Array",
).toBeTruthy();
});
it("should infer call return type using function", function () {
const path = getPath("(function (): string {})()")
.get("body")[0]
Expand Down