securityos/node_modules/eslint-plugin-unicorn/rules/utils/is-same-reference.js

174 lines
3.8 KiB
JavaScript
Raw Normal View History

2024-09-06 15:32:35 +00:00
'use strict';
const {getStaticValue} = require('@eslint-community/eslint-utils');
// Copied from https://github.com/eslint/eslint/blob/94ba68d76a6940f68ff82eea7332c6505f93df76/lib/rules/utils/ast-utils.js#L392
/**
Gets the property name of a given node.
The node can be a MemberExpression, a Property, or a MethodDefinition.
If the name is dynamic, this returns `null`.
For examples:
a.b // => "b"
a["b"] // => "b"
a['b'] // => "b"
a[`b`] // => "b"
a[100] // => "100"
a[b] // => null
a["a" + "b"] // => null
a[tag`b`] // => null
a[`${b}`] // => null
let a = {b: 1} // => "b"
let a = {["b"]: 1} // => "b"
let a = {['b']: 1} // => "b"
let a = {[`b`]: 1} // => "b"
let a = {[100]: 1} // => "100"
let a = {[b]: 1} // => null
let a = {["a" + "b"]: 1} // => null
let a = {[tag`b`]: 1} // => null
let a = {[`${b}`]: 1} // => null
@param {ASTNode} node The node to get.
@returns {string|undefined} The property name if static. Otherwise, undefined.
*/
function getStaticPropertyName(node) {
let property;
switch (node?.type) {
case 'MemberExpression': {
property = node.property;
break;
}
/* c8 ignore next 2 */
case 'ChainExpression': {
return getStaticPropertyName(node.expression);
}
// Only reachable when use this to get class/object member key
/* c8 ignore next */
case 'Property':
case 'MethodDefinition': {
/* c8 ignore next 2 */
property = node.key;
break;
}
// No default
}
if (property) {
if (property.type === 'Identifier' && !node.computed) {
return property.name;
}
const staticResult = getStaticValue(property);
if (!staticResult) {
return;
}
return String(staticResult.value);
}
}
/**
Check if two literal nodes are the same value.
@param {ASTNode} left The Literal node to compare.
@param {ASTNode} right The other Literal node to compare.
@returns {boolean} `true` if the two literal nodes are the same value.
*/
function equalLiteralValue(left, right) {
// RegExp literal.
if (left.regex || right.regex) {
return Boolean(
left.regex
&& right.regex
&& left.regex.pattern === right.regex.pattern
&& left.regex.flags === right.regex.flags,
);
}
// BigInt literal.
if (left.bigint || right.bigint) {
return left.bigint === right.bigint;
}
return left.value === right.value;
}
/**
Check if two expressions reference the same value. For example:
a = a
a.b = a.b
a[0] = a[0]
a['b'] = a['b']
@param {ASTNode} left The left side of the comparison.
@param {ASTNode} right The right side of the comparison.
@returns {boolean} `true` if both sides match and reference the same value.
*/
function isSameReference(left, right) {
if (left.type !== right.type) {
// Handle `a.b` and `a?.b` are samely.
if (left.type === 'ChainExpression') {
return isSameReference(left.expression, right);
}
if (right.type === 'ChainExpression') {
return isSameReference(left, right.expression);
}
return false;
}
switch (left.type) {
case 'Super':
case 'ThisExpression': {
return true;
}
case 'Identifier':
case 'PrivateIdentifier': {
return left.name === right.name;
}
case 'Literal': {
return equalLiteralValue(left, right);
}
case 'ChainExpression': {
return isSameReference(left.expression, right.expression);
}
case 'MemberExpression': {
const nameA = getStaticPropertyName(left);
// `x.y = x["y"]`
if (nameA !== undefined) {
return (
isSameReference(left.object, right.object)
&& nameA === getStaticPropertyName(right)
);
}
/*
`x[0] = x[0]`
`x[y] = x[y]`
`x.y = x.y`
*/
return (
left.computed === right.computed
&& isSameReference(left.object, right.object)
&& isSameReference(left.property, right.property)
);
}
default: {
return false;
}
}
}
module.exports = isSameReference;