From 0540eb3435a80cce4a02c663833d800812dcff51 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 24 Mar 2023 04:40:40 +0100 Subject: [PATCH] feat(getStaticValue): Dereference variables that are effectively `const` (#80) --- src/get-static-value.mjs | 21 ++++++++++++++++++++- test/get-static-value.mjs | 15 +++++++++++++-- test/get-string-if-constant.mjs | 6 ++++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/get-static-value.mjs b/src/get-static-value.mjs index efd4aae..074f298 100644 --- a/src/get-static-value.mjs +++ b/src/get-static-value.mjs @@ -249,6 +249,23 @@ function getElementValues(nodeList, initialScope) { return valueList } +/** + * Returns whether the given variable is never written to after initialization. + * @param {import("eslint").Scope.Variable} variable + * @returns {boolean} + */ +function isEffectivelyConst(variable) { + const refs = variable.references + + const inits = refs.filter((r) => r.init).length + const reads = refs.filter((r) => r.isReadOnly()).length + if (inits === 1 && reads + inits === refs.length) { + // there is only one init and all other references only read + return true + } + return false +} + const operations = Object.freeze({ ArrayExpression(node, initialScope) { const elements = getElementValues(node.elements, initialScope) @@ -407,7 +424,9 @@ const operations = Object.freeze({ const def = variable.defs[0] if ( def.parent && - def.parent.kind === "const" && + def.type === "Variable" && + (def.parent.kind === "const" || + isEffectivelyConst(variable)) && // TODO(mysticatea): don't support destructuring here. def.node.id.type === "Identifier" ) { diff --git a/test/get-static-value.mjs b/test/get-static-value.mjs index 1a37ce8..b898f32 100644 --- a/test/get-static-value.mjs +++ b/test/get-static-value.mjs @@ -67,7 +67,11 @@ describe("The 'getStaticValue' function", () => { { code: "var undefined; undefined", expected: null }, { code: "const undefined = 1; undefined", expected: { value: 1 } }, { code: "const a = 2; a", expected: { value: 2 } }, - { code: "let a = 2; a", expected: null }, + { code: "let a = 2; a", expected: { value: 2 } }, + { code: "var a = 2; a", expected: { value: 2 } }, + { code: "let a = 2; a = 1; a", expected: null }, + { code: "let a = 2; a++; a", expected: null }, + { code: "let a; a = 1; a", expected: null }, { code: "const a = 2; a", expected: null, noScope: true }, { code: "const a = { b: 7 }; a.b", expected: { value: 7 } }, { code: "null", expected: { value: null } }, @@ -158,7 +162,14 @@ describe("The 'getStaticValue' function", () => { code: "const obj = {b: 2}; ({a: 1, ...obj})", expected: { value: { a: 1, b: 2 } }, }, - { code: "var obj = {b: 2}; ({a: 1, ...obj})", expected: null }, + { + code: "var obj = {b: 2}; ({a: 1, ...obj})", + expected: { value: { a: 1, b: 2 } }, + }, + { + code: "var obj = {b: 2}; obj = {}; ({a: 1, ...obj})", + expected: null, + }, { code: "({ get a() {} })", expected: null }, { code: "({ a })", expected: null }, { code: "({ a: b })", expected: null }, diff --git a/test/get-string-if-constant.mjs b/test/get-string-if-constant.mjs index bdaa552..0ea091c 100644 --- a/test/get-string-if-constant.mjs +++ b/test/get-string-if-constant.mjs @@ -43,8 +43,10 @@ describe("The 'getStringIfConstant' function", () => { for (const { code, expected } of [ { code: "id", expected: null }, { code: "const id = 'abc'; id", expected: "abc" }, - { code: "let id = 'abc'; id", expected: null }, - { code: "var id = 'abc'; id", expected: null }, + { code: "let id = 'abc'; id", expected: "abc" }, + { code: "var id = 'abc'; id", expected: "abc" }, + { code: "let id = 'abc'; id = 'foo'; id", expected: null }, + { code: "var id = 'abc'; id = 'foo'; id", expected: null }, { code: "const id = otherId; id", expected: null }, ]) { it(`should return ${JSON.stringify(expected)} from ${code}`, () => {