diff --git a/packages/babel-traverse/src/path/evaluation.js b/packages/babel-traverse/src/path/evaluation.js index 01d00fb78890..ca6d6dc50d82 100644 --- a/packages/babel-traverse/src/path/evaluation.js +++ b/packages/babel-traverse/src/path/evaluation.js @@ -219,7 +219,7 @@ function _evaluate(path, state) { if (elemValue.confident) { arr.push(elemValue.value); } else { - return deopt(elem, state); + return deopt(elemValue.deopt, state); } } return arr; @@ -237,7 +237,7 @@ function _evaluate(path, state) { if (prop.node.computed) { key = key.evaluate(); if (!key.confident) { - return deopt(keyPath, state); + return deopt(key.deopt, state); } key = key.value; } else if (key.isIdentifier()) { @@ -248,7 +248,7 @@ function _evaluate(path, state) { const valuePath = prop.get("value"); let value = valuePath.evaluate(); if (!value.confident) { - return deopt(valuePath, state); + return deopt(value.deopt, state); } value = value.value; obj[key] = value; @@ -407,19 +407,24 @@ function evaluateQuasis(path, quasis: Array, state, raw = false) { /** * Walk the input `node` and statically evaluate it. * - * Returns an object in the form `{ confident, value }`. `confident` indicates - * whether or not we had to drop out of evaluating the expression because of - * hitting an unknown node that we couldn't confidently find the value of. + * Returns an object in the form `{ confident, value, deopt }`. `confident` + * indicates whether or not we had to drop out of evaluating the expression + * because of hitting an unknown node that we couldn't confidently find the + * value of, in which case `deopt` is the path of said node. * * Example: * * t.evaluate(parse("5 + 5")) // { confident: true, value: 10 } * t.evaluate(parse("!true")) // { confident: true, value: false } - * t.evaluate(parse("foo + foo")) // { confident: false, value: undefined } + * t.evaluate(parse("foo + foo")) // { confident: false, value: undefined, deopt: NodePath } * */ -export function evaluate(): { confident: boolean, value: any } { +export function evaluate(): { + confident: boolean, + value: any, + deopt?: NodePath, +} { const state = { confident: true, deoptPath: null, diff --git a/packages/babel-traverse/test/evaluation.js b/packages/babel-traverse/test/evaluation.js index f988bb1184cd..d90ab87ff0d3 100644 --- a/packages/babel-traverse/test/evaluation.js +++ b/packages/babel-traverse/test/evaluation.js @@ -13,6 +13,25 @@ function getPath(code) { return path; } +function addDeoptTest(code, type, expectedType) { + it(type + " deopt: " + code, function () { + const visitor = {}; + + visitor[type] = function (path) { + const evaluate = path.evaluate(); + expect(evaluate.confident).toBeFalsy(); + expect(evaluate.deopt.type).toEqual(expectedType); + }; + + traverse( + parse(code, { + plugins: ["*"], + }), + visitor, + ); + }); +} + describe("evaluation", function () { describe("evaluateTruthy", function () { it("it should work with null", function () { @@ -228,4 +247,8 @@ describe("evaluation", function () { expect(result.deopt).toBeNull(); expect(result.value).toEqual(["foo", "bar"]); }); + + addDeoptTest("({a:{b}})", "ObjectExpression", "Identifier"); + addDeoptTest("({[a + 'b']: 1})", "ObjectExpression", "Identifier"); + addDeoptTest("[{a}]", "ArrayExpression", "Identifier"); });