Skip to content

Commit

Permalink
Add support for assignable variables to *-prototype-* rules (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Nov 17, 2024
1 parent d16dff2 commit ab5bbd7
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 5 deletions.
87 changes: 82 additions & 5 deletions lib/util/type-checker/object-type-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,22 +202,99 @@ function buildExpressionTypeProviderImpl(context) {
// It has an initial value.
def.node.init &&
// It does not write new values.
(def.parent.kind === "const" ||
variable.references.every(
(ref) =>
ref.isReadOnly() || ref.identifier === def.name,
))
def.parent.kind === "const"
) {
// The type of the initial value is the type of the variable.
const init = getTypeInfo(def.node.init)
return init && getPatternTypeInfo(def.name, def.node.id, init)
}
if (def.parent.kind === "const") {
return null
}
return getAssignableVariableTypeInfo(variable)
} else if (def.type === "FunctionName") {
return getTypeInfo(def.node)
}
return null
}

/**
* @param {import("eslint").Scope.Variable} variable
* @returns {TypeInfo | null}
*/
function getAssignableVariableTypeInfo(variable) {
/** @type {TypeInfo[]} */
const typeInfos = []
for (const reference of variable.references) {
if (!reference.writeExpr) {
continue
}
/** @type {import("estree").Node|null} */
const parent = reference.writeExpr.parent
if (!parent) {
return null // unknown
}
if (parent.type === "VariableDeclarator") {
if (reference.writeExpr !== parent.init) {
return null // unknown
}
const init = getTypeInfo(reference.writeExpr)
if (!init) {
return null // unknown
}
const typeInfo = getPatternTypeInfo(
reference.identifier,
parent.id,
init,
)
if (!typeInfo) {
return null // unknown
}
typeInfos.push(typeInfo)
} else if (parent.type === "AssignmentExpression") {
if (reference.writeExpr !== parent.right) {
return null // unknown
}
const right = getTypeInfo(reference.writeExpr)
if (!right) {
return null // unknown
}
const typeInfo = getPatternTypeInfo(
reference.identifier,
parent.left,
right,
)
if (!typeInfo) {
return null // unknown
}
typeInfos.push(typeInfo)
}
}
const firstTypeInfo = typeInfos.shift()
if (!firstTypeInfo) {
return null
}
if (!typeInfos.length) {
return firstTypeInfo
}
if (typeInfos.every((t) => t.type === firstTypeInfo.type)) {
return {
type: firstTypeInfo.type,
get return() {
if (
typeInfos.every(
(t) => t.return === firstTypeInfo.return,
)
) {
return firstTypeInfo.return
}
return null
},
}
}
return null
}

/**
* @param {import("estree").BinaryOperator
* | import("estree").LogicalOperator
Expand Down
11 changes: 11 additions & 0 deletions tests/lib/rules/no-nonstandard-array-prototype-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ new RuleTester().run(ruleId, rule, {
"Non-standard 'Array.prototype.01' property is forbidden.",
],
},
{
code: `
let array2 = [1, 2, 3];
array2.unknown(3, 4);
array2 = [1, 2, 3, 4];
array2.unknown(3, 5);`,
errors: [
"Non-standard 'Array.prototype.unknown' property is forbidden.",
"Non-standard 'Array.prototype.unknown' property is forbidden.",
],
},
],
})

Expand Down
41 changes: 41 additions & 0 deletions tests/lib/util/type-checker/object-type-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,47 @@ describe("define-prototype-method-handler/object-type-checker", () => {
`,
result: ["String"],
},
{
code: `
let array = []
array = []
target(array);
`,
result: ["Array"],
},
{
code: `
let array = []
array = ''
target(array);
`,
result: [null],
},
{
code: `
let {EPSILON:a} = Number;
a = 42;
({MAX_SAFE_INTEGER:a} = Number);
target(a);
`,
result: ["Number"],
},
{
code: `
let a = Number.isFinite;
a = Number.isNaN;
target(a(foo));
`,
result: ["Boolean"],
},
{
code: `
let a = Number.isFinite;
a = Number;
target(a(foo));
`,
result: [null],
},
]) {
;(only ? it.only : it)(code, () => {
deepStrictEqual(
Expand Down

0 comments on commit ab5bbd7

Please sign in to comment.