+++ /dev/null
-/**
- * @fileoverview Rule to disallow assignments where both sides are exactly the same
- * @author Toru Nagashima
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const astUtils = require("./utils/ast-utils");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-const SPACES = /\s+/gu;
-
-/**
- * Checks whether the property of 2 given member expression nodes are the same
- * property or not.
- * @param {ASTNode} left A member expression node to check.
- * @param {ASTNode} right Another member expression node to check.
- * @returns {boolean} `true` if the member expressions have the same property.
- */
-function isSameProperty(left, right) {
- if (left.property.type === "Identifier" &&
- left.property.type === right.property.type &&
- left.property.name === right.property.name &&
- left.computed === right.computed
- ) {
- return true;
- }
-
- const lname = astUtils.getStaticPropertyName(left);
- const rname = astUtils.getStaticPropertyName(right);
-
- return lname !== null && lname === rname;
-}
-
-/**
- * Checks whether 2 given member expression nodes are the reference to the same
- * property or not.
- * @param {ASTNode} left A member expression node to check.
- * @param {ASTNode} right Another member expression node to check.
- * @returns {boolean} `true` if the member expressions are the reference to the
- * same property or not.
- */
-function isSameMember(left, right) {
- if (!isSameProperty(left, right)) {
- return false;
- }
-
- const lobj = left.object;
- const robj = right.object;
-
- if (lobj.type !== robj.type) {
- return false;
- }
- if (lobj.type === "MemberExpression") {
- return isSameMember(lobj, robj);
- }
- if (lobj.type === "ThisExpression") {
- return true;
- }
- return lobj.type === "Identifier" && lobj.name === robj.name;
-}
-
-/**
- * Traverses 2 Pattern nodes in parallel, then reports self-assignments.
- * @param {ASTNode|null} left A left node to traverse. This is a Pattern or
- * a Property.
- * @param {ASTNode|null} right A right node to traverse. This is a Pattern or
- * a Property.
- * @param {boolean} props The flag to check member expressions as well.
- * @param {Function} report A callback function to report.
- * @returns {void}
- */
-function eachSelfAssignment(left, right, props, report) {
- if (!left || !right) {
-
- // do nothing
- } else if (
- left.type === "Identifier" &&
- right.type === "Identifier" &&
- left.name === right.name
- ) {
- report(right);
- } else if (
- left.type === "ArrayPattern" &&
- right.type === "ArrayExpression"
- ) {
- const end = Math.min(left.elements.length, right.elements.length);
-
- for (let i = 0; i < end; ++i) {
- const leftElement = left.elements[i];
- const rightElement = right.elements[i];
-
- // Avoid cases such as [...a] = [...a, 1]
- if (
- leftElement &&
- leftElement.type === "RestElement" &&
- i < right.elements.length - 1
- ) {
- break;
- }
-
- eachSelfAssignment(leftElement, rightElement, props, report);
-
- // After a spread element, those indices are unknown.
- if (rightElement && rightElement.type === "SpreadElement") {
- break;
- }
- }
- } else if (
- left.type === "RestElement" &&
- right.type === "SpreadElement"
- ) {
- eachSelfAssignment(left.argument, right.argument, props, report);
- } else if (
- left.type === "ObjectPattern" &&
- right.type === "ObjectExpression" &&
- right.properties.length >= 1
- ) {
-
- /*
- * Gets the index of the last spread property.
- * It's possible to overwrite properties followed by it.
- */
- let startJ = 0;
-
- for (let i = right.properties.length - 1; i >= 0; --i) {
- const propType = right.properties[i].type;
-
- if (propType === "SpreadElement" || propType === "ExperimentalSpreadProperty") {
- startJ = i + 1;
- break;
- }
- }
-
- for (let i = 0; i < left.properties.length; ++i) {
- for (let j = startJ; j < right.properties.length; ++j) {
- eachSelfAssignment(
- left.properties[i],
- right.properties[j],
- props,
- report
- );
- }
- }
- } else if (
- left.type === "Property" &&
- right.type === "Property" &&
- right.kind === "init" &&
- !right.method
- ) {
- const leftName = astUtils.getStaticPropertyName(left);
-
- if (leftName !== null && leftName === astUtils.getStaticPropertyName(right)) {
- eachSelfAssignment(left.value, right.value, props, report);
- }
- } else if (
- props &&
- left.type === "MemberExpression" &&
- right.type === "MemberExpression" &&
- isSameMember(left, right)
- ) {
- report(right);
- }
-}
-
-//------------------------------------------------------------------------------
-// Rule Definition
-//------------------------------------------------------------------------------
-
-module.exports = {
- meta: {
- type: "problem",
-
- docs: {
- description: "disallow assignments where both sides are exactly the same",
- category: "Best Practices",
- recommended: true,
- url: "https://eslint.org/docs/rules/no-self-assign"
- },
-
- schema: [
- {
- type: "object",
- properties: {
- props: {
- type: "boolean",
- default: true
- }
- },
- additionalProperties: false
- }
- ]
- },
-
- create(context) {
- const sourceCode = context.getSourceCode();
- const [{ props = true } = {}] = context.options;
-
- /**
- * Reports a given node as self assignments.
- * @param {ASTNode} node A node to report. This is an Identifier node.
- * @returns {void}
- */
- function report(node) {
- context.report({
- node,
- message: "'{{name}}' is assigned to itself.",
- data: {
- name: sourceCode.getText(node).replace(SPACES, "")
- }
- });
- }
-
- return {
- AssignmentExpression(node) {
- if (node.operator === "=") {
- eachSelfAssignment(node.left, node.right, props, report);
- }
- }
- };
- }
-};