| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 | /** * @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('../shared/types').Rule} */module.exports = {    meta: {        type: "problem",        docs: {            description: "Disallow use of optional chaining in contexts where the `undefined` value is not allowed",            recommended: true,            url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"        },        schema: [{            type: "object",            properties: {                disallowArithmeticOperators: {                    type: "boolean",                    default: false                }            },            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 options = context.options[0] || {};        const disallowArithmeticOperators = (options.disallowArithmeticOperators) || false;        /**         * 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[node.expressions.length - 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);                }            }        };    }};
 |