| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 | 
							- /**
 
-  * @fileoverview A rule to disallow the type conversions with shorter notations.
 
-  * @author Toru Nagashima
 
-  */
 
- "use strict";
 
- const astUtils = require("./utils/ast-utils");
 
- //------------------------------------------------------------------------------
 
- // Helpers
 
- //------------------------------------------------------------------------------
 
- const INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/u;
 
- const ALLOWABLE_OPERATORS = ["~", "!!", "+", "*"];
 
- /**
 
-  * Parses and normalizes an option object.
 
-  * @param {Object} options An option object to parse.
 
-  * @returns {Object} The parsed and normalized option object.
 
-  */
 
- function parseOptions(options) {
 
-     return {
 
-         boolean: "boolean" in options ? options.boolean : true,
 
-         number: "number" in options ? options.number : true,
 
-         string: "string" in options ? options.string : true,
 
-         allow: options.allow || []
 
-     };
 
- }
 
- /**
 
-  * Checks whether or not a node is a double logical nigating.
 
-  * @param {ASTNode} node An UnaryExpression node to check.
 
-  * @returns {boolean} Whether or not the node is a double logical nigating.
 
-  */
 
- function isDoubleLogicalNegating(node) {
 
-     return (
 
-         node.operator === "!" &&
 
-         node.argument.type === "UnaryExpression" &&
 
-         node.argument.operator === "!"
 
-     );
 
- }
 
- /**
 
-  * Checks whether or not a node is a binary negating of `.indexOf()` method calling.
 
-  * @param {ASTNode} node An UnaryExpression node to check.
 
-  * @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
 
-  */
 
- function isBinaryNegatingOfIndexOf(node) {
 
-     if (node.operator !== "~") {
 
-         return false;
 
-     }
 
-     const callNode = astUtils.skipChainExpression(node.argument);
 
-     return (
 
-         callNode.type === "CallExpression" &&
 
-         astUtils.isSpecificMemberAccess(callNode.callee, null, INDEX_OF_PATTERN)
 
-     );
 
- }
 
- /**
 
-  * Checks whether or not a node is a multiplying by one.
 
-  * @param {BinaryExpression} node A BinaryExpression node to check.
 
-  * @returns {boolean} Whether or not the node is a multiplying by one.
 
-  */
 
- function isMultiplyByOne(node) {
 
-     return node.operator === "*" && (
 
-         node.left.type === "Literal" && node.left.value === 1 ||
 
-         node.right.type === "Literal" && node.right.value === 1
 
-     );
 
- }
 
- /**
 
-  * Checks whether the result of a node is numeric or not
 
-  * @param {ASTNode} node The node to test
 
-  * @returns {boolean} true if the node is a number literal or a `Number()`, `parseInt` or `parseFloat` call
 
-  */
 
- function isNumeric(node) {
 
-     return (
 
-         node.type === "Literal" && typeof node.value === "number" ||
 
-         node.type === "CallExpression" && (
 
-             node.callee.name === "Number" ||
 
-             node.callee.name === "parseInt" ||
 
-             node.callee.name === "parseFloat"
 
-         )
 
-     );
 
- }
 
- /**
 
-  * Returns the first non-numeric operand in a BinaryExpression. Designed to be
 
-  * used from bottom to up since it walks up the BinaryExpression trees using
 
-  * node.parent to find the result.
 
-  * @param {BinaryExpression} node The BinaryExpression node to be walked up on
 
-  * @returns {ASTNode|null} The first non-numeric item in the BinaryExpression tree or null
 
-  */
 
- function getNonNumericOperand(node) {
 
-     const left = node.left,
 
-         right = node.right;
 
-     if (right.type !== "BinaryExpression" && !isNumeric(right)) {
 
-         return right;
 
-     }
 
-     if (left.type !== "BinaryExpression" && !isNumeric(left)) {
 
-         return left;
 
-     }
 
-     return null;
 
- }
 
- /**
 
-  * Checks whether a node is an empty string literal or not.
 
-  * @param {ASTNode} node The node to check.
 
-  * @returns {boolean} Whether or not the passed in node is an
 
-  * empty string literal or not.
 
-  */
 
- function isEmptyString(node) {
 
-     return astUtils.isStringLiteral(node) && (node.value === "" || (node.type === "TemplateLiteral" && node.quasis.length === 1 && node.quasis[0].value.cooked === ""));
 
- }
 
- /**
 
-  * Checks whether or not a node is a concatenating with an empty string.
 
-  * @param {ASTNode} node A BinaryExpression node to check.
 
-  * @returns {boolean} Whether or not the node is a concatenating with an empty string.
 
-  */
 
- function isConcatWithEmptyString(node) {
 
-     return node.operator === "+" && (
 
-         (isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) ||
 
-         (isEmptyString(node.right) && !astUtils.isStringLiteral(node.left))
 
-     );
 
- }
 
- /**
 
-  * Checks whether or not a node is appended with an empty string.
 
-  * @param {ASTNode} node An AssignmentExpression node to check.
 
-  * @returns {boolean} Whether or not the node is appended with an empty string.
 
-  */
 
- function isAppendEmptyString(node) {
 
-     return node.operator === "+=" && isEmptyString(node.right);
 
- }
 
- /**
 
-  * Returns the operand that is not an empty string from a flagged BinaryExpression.
 
-  * @param {ASTNode} node The flagged BinaryExpression node to check.
 
-  * @returns {ASTNode} The operand that is not an empty string from a flagged BinaryExpression.
 
-  */
 
- function getNonEmptyOperand(node) {
 
-     return isEmptyString(node.left) ? node.right : node.left;
 
- }
 
- //------------------------------------------------------------------------------
 
- // Rule Definition
 
- //------------------------------------------------------------------------------
 
- module.exports = {
 
-     meta: {
 
-         type: "suggestion",
 
-         docs: {
 
-             description: "disallow shorthand type conversions",
 
-             category: "Best Practices",
 
-             recommended: false,
 
-             url: "https://eslint.org/docs/rules/no-implicit-coercion"
 
-         },
 
-         fixable: "code",
 
-         schema: [{
 
-             type: "object",
 
-             properties: {
 
-                 boolean: {
 
-                     type: "boolean",
 
-                     default: true
 
-                 },
 
-                 number: {
 
-                     type: "boolean",
 
-                     default: true
 
-                 },
 
-                 string: {
 
-                     type: "boolean",
 
-                     default: true
 
-                 },
 
-                 allow: {
 
-                     type: "array",
 
-                     items: {
 
-                         enum: ALLOWABLE_OPERATORS
 
-                     },
 
-                     uniqueItems: true
 
-                 }
 
-             },
 
-             additionalProperties: false
 
-         }],
 
-         messages: {
 
-             useRecommendation: "use `{{recommendation}}` instead."
 
-         }
 
-     },
 
-     create(context) {
 
-         const options = parseOptions(context.options[0] || {});
 
-         const sourceCode = context.getSourceCode();
 
-         /**
 
-          * Reports an error and autofixes the node
 
-          * @param {ASTNode} node An ast node to report the error on.
 
-          * @param {string} recommendation The recommended code for the issue
 
-          * @param {bool} shouldFix Whether this report should fix the node
 
-          * @returns {void}
 
-          */
 
-         function report(node, recommendation, shouldFix) {
 
-             context.report({
 
-                 node,
 
-                 messageId: "useRecommendation",
 
-                 data: {
 
-                     recommendation
 
-                 },
 
-                 fix(fixer) {
 
-                     if (!shouldFix) {
 
-                         return null;
 
-                     }
 
-                     const tokenBefore = sourceCode.getTokenBefore(node);
 
-                     if (
 
-                         tokenBefore &&
 
-                         tokenBefore.range[1] === node.range[0] &&
 
-                         !astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
 
-                     ) {
 
-                         return fixer.replaceText(node, ` ${recommendation}`);
 
-                     }
 
-                     return fixer.replaceText(node, recommendation);
 
-                 }
 
-             });
 
-         }
 
-         return {
 
-             UnaryExpression(node) {
 
-                 let operatorAllowed;
 
-                 // !!foo
 
-                 operatorAllowed = options.allow.indexOf("!!") >= 0;
 
-                 if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
 
-                     const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
 
-                     report(node, recommendation, true);
 
-                 }
 
-                 // ~foo.indexOf(bar)
 
-                 operatorAllowed = options.allow.indexOf("~") >= 0;
 
-                 if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
 
-                     // `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
 
-                     const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
 
-                     const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
 
-                     report(node, recommendation, false);
 
-                 }
 
-                 // +foo
 
-                 operatorAllowed = options.allow.indexOf("+") >= 0;
 
-                 if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
 
-                     const recommendation = `Number(${sourceCode.getText(node.argument)})`;
 
-                     report(node, recommendation, true);
 
-                 }
 
-             },
 
-             // Use `:exit` to prevent double reporting
 
-             "BinaryExpression:exit"(node) {
 
-                 let operatorAllowed;
 
-                 // 1 * foo
 
-                 operatorAllowed = options.allow.indexOf("*") >= 0;
 
-                 const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
 
-                 if (nonNumericOperand) {
 
-                     const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
 
-                     report(node, recommendation, true);
 
-                 }
 
-                 // "" + foo
 
-                 operatorAllowed = options.allow.indexOf("+") >= 0;
 
-                 if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
 
-                     const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
 
-                     report(node, recommendation, true);
 
-                 }
 
-             },
 
-             AssignmentExpression(node) {
 
-                 // foo += ""
 
-                 const operatorAllowed = options.allow.indexOf("+") >= 0;
 
-                 if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
 
-                     const code = sourceCode.getText(getNonEmptyOperand(node));
 
-                     const recommendation = `${code} = String(${code})`;
 
-                     report(node, recommendation, true);
 
-                 }
 
-             }
 
-         };
 
-     }
 
- };
 
 
  |