Skip to content

Commit 04f573f

Browse files
motiz88danez
authored andcommitted
fix: avoid circular reference when resolving name from within assignment
This minimal tweak to `resolveToValue` allows us, in particular, to follow some reassignments of the variable holding a component, e.g. when wrapping it with a HOC.
1 parent b34977a commit 04f573f

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

src/handlers/__tests__/flowTypeHandler-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,5 +368,25 @@ describe('flowTypeHandler', () => {
368368
},
369369
});
370370
});
371+
372+
it('resolves when the function is rebound and not inline', () => {
373+
const src = `
374+
import React from 'react';
375+
type Props = { foo: string };
376+
let Component = (props: Props, ref) => <div ref={ref}>{props.foo}</div>;
377+
Component = React.forwardRef(Component);
378+
`;
379+
flowTypeHandler(
380+
documentation,
381+
parse(src).get('body', 3, 'expression', 'right'),
382+
);
383+
expect(documentation.descriptors).toEqual({
384+
foo: {
385+
flowType: {},
386+
required: true,
387+
description: '',
388+
},
389+
});
390+
});
371391
});
372392
});

src/utils/__tests__/resolveToValue-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ describe('resolveToValue', () => {
109109

110110
expect(resolveToValue(path)).toEqualASTNode(builders.literal(42));
111111
});
112+
113+
it('resolves to other assigned value if ref is in an assignment lhs', () => {
114+
const path = parsePath(
115+
['var foo;', 'foo = 42;', 'foo = wrap(foo);'].join('\n'),
116+
);
117+
118+
expect(resolveToValue(path.get('left'))).toEqualASTNode(
119+
builders.literal(42),
120+
);
121+
});
122+
123+
it('resolves to other assigned value if ref is in an assignment rhs', () => {
124+
const path = parsePath(
125+
['var foo;', 'foo = 42;', 'foo = wrap(foo);'].join('\n'),
126+
);
127+
128+
expect(resolveToValue(path.get('right', 'arguments', 0))).toEqualASTNode(
129+
builders.literal(42),
130+
);
131+
});
112132
});
113133

114134
describe('ImportDeclaration', () => {

src/utils/resolveToValue.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,32 @@ function findScopePath(paths: Array<NodePath>, path: NodePath): ?NodePath {
7272
* Tries to find the last value assigned to `name` in the scope created by
7373
* `scope`. We are not descending into any statements (blocks).
7474
*/
75-
function findLastAssignedValue(scope, name) {
75+
function findLastAssignedValue(scope, idPath) {
7676
const results = [];
77+
const name = idPath.node.name;
7778

7879
traverseShallow(scope.path, {
7980
visitAssignmentExpression: function(path) {
8081
const node = path.node;
8182
// Skip anything that is not an assignment to a variable with the
8283
// passed name.
84+
// Ensure the LHS isn't the reference we're trying to resolve.
8385
if (
8486
!t.Identifier.check(node.left) ||
87+
node.left === idPath.node ||
8588
node.left.name !== name ||
8689
node.operator !== '='
8790
) {
8891
return this.traverse(path);
8992
}
90-
results.push(path.get('right'));
93+
// Ensure the RHS doesn't contain the reference we're trying to resolve.
94+
const candidatePath = path.get('right');
95+
for (let p = idPath; p && p.node != null; p = p.parent) {
96+
if (p.node === candidatePath.node) {
97+
return this.traverse(path);
98+
}
99+
}
100+
results.push(candidatePath);
91101
return false;
92102
},
93103
});
@@ -159,7 +169,7 @@ export default function resolveToValue(path: NodePath): NodePath {
159169
// The variable may be assigned a different value after initialization.
160170
// We are first trying to find all assignments to the variable in the
161171
// block where it is defined (i.e. we are not traversing into statements)
162-
resolvedPath = findLastAssignedValue(scope, node.name);
172+
resolvedPath = findLastAssignedValue(scope, path);
163173
if (!resolvedPath) {
164174
const bindings = scope.getBindings()[node.name];
165175
resolvedPath = findScopePath(bindings, path);

0 commit comments

Comments
 (0)