codtracker-js/node_modules/eslint/lib/rules/no-unsafe-optional-chaining.js
2025-04-19 23:12:19 -04:00

219 lines
5.8 KiB
JavaScript

/**
* @fileoverview Rule to disallow unsafe optional chaining
* @author Yeon JuAn
*/
'use strict';
const UNSAFE_ARITHMETIC_OPERATORS = new Set(['+', '-', '/', '*', '%', '**']);
const UNSAFE_ASSIGNMENT_OPERATORS = new Set([
'+=',
'-=',
'/=',
'*=',
'%=',
'**=',
]);
const UNSAFE_RELATIONAL_OPERATORS = new Set(['in', 'instanceof']);
/**
* Checks whether a node is a destructuring pattern or not
* @param {ASTNode} node node to check
* @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
*/
function isDestructuringPattern(node) {
return node.type === 'ObjectPattern' || node.type === 'ArrayPattern';
}
/** @type {import('../types').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
defaultOptions: [
{
disallowArithmeticOperators: false,
},
],
docs: {
description:
'Disallow use of optional chaining in contexts where the `undefined` value is not allowed',
recommended: true,
url: 'https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining',
},
schema: [
{
type: 'object',
properties: {
disallowArithmeticOperators: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
fixable: null,
messages: {
unsafeOptionalChain:
"Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
unsafeArithmetic:
'Unsafe arithmetic operation on optional chaining. It can result in NaN.',
},
},
create(context) {
const [{ disallowArithmeticOperators }] = context.options;
/**
* Reports unsafe usage of optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeUsage(node) {
context.report({
messageId: 'unsafeOptionalChain',
node,
});
}
/**
* Reports unsafe arithmetic operation on optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeArithmetic(node) {
context.report({
messageId: 'unsafeArithmetic',
node,
});
}
/**
* Checks and reports if a node can short-circuit with `undefined` by optional chaining.
* @param {ASTNode} [node] node to check
* @param {Function} reportFunc report function
* @returns {void}
*/
function checkUndefinedShortCircuit(node, reportFunc) {
if (!node) {
return;
}
switch (node.type) {
case 'LogicalExpression':
if (node.operator === '||' || node.operator === '??') {
checkUndefinedShortCircuit(node.right, reportFunc);
} else if (node.operator === '&&') {
checkUndefinedShortCircuit(node.left, reportFunc);
checkUndefinedShortCircuit(node.right, reportFunc);
}
break;
case 'SequenceExpression':
checkUndefinedShortCircuit(node.expressions.at(-1), reportFunc);
break;
case 'ConditionalExpression':
checkUndefinedShortCircuit(node.consequent, reportFunc);
checkUndefinedShortCircuit(node.alternate, reportFunc);
break;
case 'AwaitExpression':
checkUndefinedShortCircuit(node.argument, reportFunc);
break;
case 'ChainExpression':
reportFunc(node);
break;
default:
break;
}
}
/**
* Checks unsafe usage of optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeUsage(node) {
checkUndefinedShortCircuit(node, reportUnsafeUsage);
}
/**
* Checks unsafe arithmetic operations on optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeArithmetic(node) {
checkUndefinedShortCircuit(node, reportUnsafeArithmetic);
}
return {
'AssignmentExpression, AssignmentPattern'(node) {
if (isDestructuringPattern(node.left)) {
checkUnsafeUsage(node.right);
}
},
'ClassDeclaration, ClassExpression'(node) {
checkUnsafeUsage(node.superClass);
},
CallExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.callee);
}
},
NewExpression(node) {
checkUnsafeUsage(node.callee);
},
VariableDeclarator(node) {
if (isDestructuringPattern(node.id)) {
checkUnsafeUsage(node.init);
}
},
MemberExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.object);
}
},
TaggedTemplateExpression(node) {
checkUnsafeUsage(node.tag);
},
ForOfStatement(node) {
checkUnsafeUsage(node.right);
},
SpreadElement(node) {
if (node.parent && node.parent.type !== 'ObjectExpression') {
checkUnsafeUsage(node.argument);
}
},
BinaryExpression(node) {
if (UNSAFE_RELATIONAL_OPERATORS.has(node.operator)) {
checkUnsafeUsage(node.right);
}
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
checkUnsafeArithmetic(node.left);
}
},
WithStatement(node) {
checkUnsafeUsage(node.object);
},
UnaryExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.argument);
}
},
AssignmentExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ASSIGNMENT_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
}
},
};
},
};