123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /**
- * @fileoverview Rule to flag statements that use != and == instead of !== and ===
- * @author Nicholas C. Zakas
- */
-
- "use strict";
-
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
-
- const astUtils = require("../util/ast-utils");
-
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
-
- module.exports = {
- meta: {
- type: "suggestion",
-
- docs: {
- description: "require the use of `===` and `!==`",
- category: "Best Practices",
- recommended: false,
- url: "https://eslint.org/docs/rules/eqeqeq"
- },
-
- schema: {
- anyOf: [
- {
- type: "array",
- items: [
- {
- enum: ["always"]
- },
- {
- type: "object",
- properties: {
- null: {
- enum: ["always", "never", "ignore"]
- }
- },
- additionalProperties: false
- }
- ],
- additionalItems: false
- },
- {
- type: "array",
- items: [
- {
- enum: ["smart", "allow-null"]
- }
- ],
- additionalItems: false
- }
- ]
- },
-
- fixable: "code",
-
- messages: {
- unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'."
- }
- },
-
- create(context) {
- const config = context.options[0] || "always";
- const options = context.options[1] || {};
- const sourceCode = context.getSourceCode();
-
- const nullOption = (config === "always")
- ? options.null || "always"
- : "ignore";
- const enforceRuleForNull = (nullOption === "always");
- const enforceInverseRuleForNull = (nullOption === "never");
-
- /**
- * Checks if an expression is a typeof expression
- * @param {ASTNode} node The node to check
- * @returns {boolean} if the node is a typeof expression
- */
- function isTypeOf(node) {
- return node.type === "UnaryExpression" && node.operator === "typeof";
- }
-
- /**
- * Checks if either operand of a binary expression is a typeof operation
- * @param {ASTNode} node The node to check
- * @returns {boolean} if one of the operands is typeof
- * @private
- */
- function isTypeOfBinary(node) {
- return isTypeOf(node.left) || isTypeOf(node.right);
- }
-
- /**
- * Checks if operands are literals of the same type (via typeof)
- * @param {ASTNode} node The node to check
- * @returns {boolean} if operands are of same type
- * @private
- */
- function areLiteralsAndSameType(node) {
- return node.left.type === "Literal" && node.right.type === "Literal" &&
- typeof node.left.value === typeof node.right.value;
- }
-
- /**
- * Checks if one of the operands is a literal null
- * @param {ASTNode} node The node to check
- * @returns {boolean} if operands are null
- * @private
- */
- function isNullCheck(node) {
- return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
- }
-
- /**
- * Gets the location (line and column) of the binary expression's operator
- * @param {ASTNode} node The binary expression node to check
- * @param {string} operator The operator to find
- * @returns {Object} { line, column } location of operator
- * @private
- */
- function getOperatorLocation(node) {
- const opToken = sourceCode.getTokenAfter(node.left);
-
- return { line: opToken.loc.start.line, column: opToken.loc.start.column };
- }
-
- /**
- * Reports a message for this rule.
- * @param {ASTNode} node The binary expression node that was checked
- * @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==')
- * @returns {void}
- * @private
- */
- function report(node, expectedOperator) {
- context.report({
- node,
- loc: getOperatorLocation(node),
- messageId: "unexpected",
- data: { expectedOperator, actualOperator: node.operator },
- fix(fixer) {
-
- // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.
- if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {
- const operatorToken = sourceCode.getFirstTokenBetween(
- node.left,
- node.right,
- token => token.value === node.operator
- );
-
- return fixer.replaceText(operatorToken, expectedOperator);
- }
- return null;
- }
- });
- }
-
- return {
- BinaryExpression(node) {
- const isNull = isNullCheck(node);
-
- if (node.operator !== "==" && node.operator !== "!=") {
- if (enforceInverseRuleForNull && isNull) {
- report(node, node.operator.slice(0, -1));
- }
- return;
- }
-
- if (config === "smart" && (isTypeOfBinary(node) ||
- areLiteralsAndSameType(node) || isNull)) {
- return;
- }
-
- if (!enforceRuleForNull && isNull) {
- return;
- }
-
- report(node, `${node.operator}=`);
- }
- };
-
- }
- };
|