From 8f9e481ecc1204c7a1331b697f97903f90c75154 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 20 Oct 2019 21:43:49 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix=20reference=20tracker=20fals?= =?UTF-8?q?e=20positive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See also https://github.com/eslint/eslint/issues/12437. --- src/reference-tracker.js | 25 +++++++++++++++++++++++-- test/reference-tracker.js | 13 +++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/reference-tracker.js b/src/reference-tracker.js index f63c2bd..ec5a193 100644 --- a/src/reference-tracker.js +++ b/src/reference-tracker.js @@ -2,7 +2,6 @@ import { findVariable } from "./find-variable" import { getPropertyName } from "./get-property-name" import { getStringIfConstant } from "./get-string-if-constant" -const SENTINEL_TYPE = /^(?:.+?Statement|.+?Declaration|(?:Array|ArrowFunction|Assignment|Call|Class|Function|Member|New|Object)Expression|AssignmentPattern|Program|VariableDeclarator)$/u const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u const has = Function.call.bind(Object.hasOwnProperty) @@ -26,6 +25,28 @@ function isModifiedGlobal(variable) { ) } +/** + * Check if the value of a given node is passed through to the parent syntax as-is. + * For example, `a` and `b` in (`a || b` and `c ? a : b`) are passed through. + * @param {Node} node A node to check. + * @returns {boolean} `true` if the node is passed through. + */ +function isPassThrough(node) { + const parent = node.parent + + switch (parent && parent.type) { + case "ConditionalExpression": + return parent.consequent === node || parent.alternate === node + case "LogicalExpression": + return true + case "SequenceExpression": + return parent.expressions[parent.expressions.length - 1] === node + + default: + return false + } +} + /** * The reference tracker. */ @@ -227,7 +248,7 @@ export class ReferenceTracker { //eslint-disable-next-line complexity *_iteratePropertyReferences(rootNode, path, traceMap) { let node = rootNode - while (!SENTINEL_TYPE.test(node.parent.type)) { + while (isPassThrough(node)) { node = node.parent } diff --git a/test/reference-tracker.js b/test/reference-tracker.js index fc8b3ad..eb7166e 100644 --- a/test/reference-tracker.js +++ b/test/reference-tracker.js @@ -4,6 +4,7 @@ import { CALL, CONSTRUCT, ESM, READ, ReferenceTracker } from "../src/" const config = { parserOptions: { ecmaVersion: 2018, sourceType: "module" }, + globals: { Reflect: false }, rules: { test: "error" }, } @@ -369,6 +370,18 @@ describe("The 'ReferenceTracker' class:", () => { }, expected: [], }, + { + description: + "should not iterate the references through unary/binary expressions.", + code: [ + 'var construct = typeof Reflect !== "undefined" ? Reflect.construct : undefined', + "construct()", + ].join("\n"), + traceMap: { + Reflect: { [CALL]: 1 }, + }, + expected: [], + }, ]) { it(description, () => { const linter = new eslint.Linter()