From 50e789a14bf7c776abbe91d4b22d13e50ddf4b01 Mon Sep 17 00:00:00 2001 From: Rebecca Stevens Date: Mon, 25 Mar 2024 18:30:00 +1300 Subject: [PATCH 1/2] fix(immutable-data): ignore casting when evaluating the expressions (#792) fix #790 --- src/rules/immutable-data.ts | 41 +++++++++++-------- src/utils/misc.ts | 5 ++- src/utils/type-guards.ts | 6 +++ .../rules/immutable-data/ts/array/invalid.ts | 14 +++++++ tests/rules/immutable-data/ts/array/valid.ts | 12 ++++++ .../rules/immutable-data/ts/object/invalid.ts | 14 +++++++ tests/rules/immutable-data/ts/object/valid.ts | 6 +++ 7 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/rules/immutable-data.ts b/src/rules/immutable-data.ts index c4b0a31f5..8cf02efac 100644 --- a/src/rules/immutable-data.ts +++ b/src/rules/immutable-data.ts @@ -40,6 +40,7 @@ import { isMemberExpression, isNewExpression, isObjectConstructorType, + isTSAsExpression, } from "#eslint-plugin-functional/utils/type-guards"; /** @@ -361,42 +362,46 @@ function checkUpdateExpression( * a mutator method call. */ function isInChainCallAndFollowsNew( - node: TSESTree.MemberExpression, + node: TSESTree.Expression, context: Readonly>, ): boolean { + if (isMemberExpression(node)) { + return isInChainCallAndFollowsNew(node.object, context); + } + + if (isTSAsExpression(node)) { + return isInChainCallAndFollowsNew(node.expression, context); + } + // Check for: [0, 1, 2] - if (isArrayExpression(node.object)) { + if (isArrayExpression(node)) { return true; } // Check for: new Array() if ( - isNewExpression(node.object) && - isArrayConstructorType(getTypeOfNode(node.object.callee, context)) + isNewExpression(node) && + isArrayConstructorType(getTypeOfNode(node.callee, context)) ) { return true; } if ( - isCallExpression(node.object) && - isMemberExpression(node.object.callee) && - isIdentifier(node.object.callee.property) + isCallExpression(node) && + isMemberExpression(node.callee) && + isIdentifier(node.callee.property) ) { // Check for: Array.from(iterable) if ( - arrayConstructorFunctions.some( - isExpected(node.object.callee.property.name), - ) && - isArrayConstructorType(getTypeOfNode(node.object.callee.object, context)) + arrayConstructorFunctions.some(isExpected(node.callee.property.name)) && + isArrayConstructorType(getTypeOfNode(node.callee.object, context)) ) { return true; } // Check for: array.slice(0) if ( - arrayNewObjectReturningMethods.some( - isExpected(node.object.callee.property.name), - ) + arrayNewObjectReturningMethods.some(isExpected(node.callee.property.name)) ) { return true; } @@ -404,9 +409,9 @@ function isInChainCallAndFollowsNew( // Check for: Object.entries(object) if ( objectConstructorNewObjectReturningMethods.some( - isExpected(node.object.callee.property.name), + isExpected(node.callee.property.name), ) && - isObjectConstructorType(getTypeOfNode(node.object.callee.object, context)) + isObjectConstructorType(getTypeOfNode(node.callee.object, context)) ) { return true; } @@ -414,9 +419,9 @@ function isInChainCallAndFollowsNew( // Check for: "".split("") if ( stringConstructorNewObjectReturningMethods.some( - isExpected(node.object.callee.property.name), + isExpected(node.callee.property.name), ) && - getTypeOfNode(node.object.callee.object, context).isStringLiteral() + getTypeOfNode(node.callee.object, context).isStringLiteral() ) { return true; } diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 3a96b1952..576303ce8 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -12,6 +12,7 @@ import { isMemberExpression, isPrivateIdentifier, isThisExpression, + isTSAsExpression, isTSTypeAnnotation, isUnaryExpression, isVariableDeclaration, @@ -72,7 +73,9 @@ function getNodeIdentifierText( ? context.sourceCode .getText(node.typeAnnotation as TSESTree.Node) .replaceAll(/\s+/gmu, "") - : null; + : isTSAsExpression(node) + ? getNodeIdentifierText(node.expression, context) + : null; if (identifierText !== null) { return identifierText; diff --git a/src/utils/type-guards.ts b/src/utils/type-guards.ts index d3ea661cb..804011a72 100644 --- a/src/utils/type-guards.ts +++ b/src/utils/type-guards.ts @@ -237,6 +237,12 @@ export function isTSArrayType( return node.type === AST_NODE_TYPES.TSArrayType; } +export function isTSAsExpression( + node: TSESTree.Node, +): node is TSESTree.TSAsExpression { + return node.type === AST_NODE_TYPES.TSAsExpression; +} + export function isTSFunctionType( node: TSESTree.Node, ): node is TSESTree.TSFunctionType { diff --git a/tests/rules/immutable-data/ts/array/invalid.ts b/tests/rules/immutable-data/ts/array/invalid.ts index 724c99ae3..f9ebd1ebb 100644 --- a/tests/rules/immutable-data/ts/array/invalid.ts +++ b/tests/rules/immutable-data/ts/array/invalid.ts @@ -795,6 +795,20 @@ const tests: Array< }, ], }, + { + code: dedent` + (mutable_foo as string[]).sort(); + `, + optionsSet: [[]], + errors: [ + { + messageId: "array", + type: AST_NODE_TYPES.CallExpression, + line: 1, + column: 1, + }, + ], + }, ]; export default tests; diff --git a/tests/rules/immutable-data/ts/array/valid.ts b/tests/rules/immutable-data/ts/array/valid.ts index 3ca049855..37ed38065 100644 --- a/tests/rules/immutable-data/ts/array/valid.ts +++ b/tests/rules/immutable-data/ts/array/valid.ts @@ -338,6 +338,18 @@ const tests: Array>> = [ `, optionsSet: [[{ ignoreImmediateMutation: true }]], }, + { + code: dedent` + (mutable_foo as string[]).sort(); + `, + optionsSet: [[{ ignoreAccessorPattern: "mutable*" }]], + }, + { + code: dedent` + ([a, b, c] as string[]).sort(); + `, + optionsSet: [[{ ignoreImmediateMutation: true }]], + }, ]; export default tests; diff --git a/tests/rules/immutable-data/ts/object/invalid.ts b/tests/rules/immutable-data/ts/object/invalid.ts index 465facd57..41ad753f2 100644 --- a/tests/rules/immutable-data/ts/object/invalid.ts +++ b/tests/rules/immutable-data/ts/object/invalid.ts @@ -511,6 +511,20 @@ const tests: Array< }, ], }, + { + code: dedent` + (mutable_foo as Bar).baz = "hello world"; + `, + optionsSet: [[]], + errors: [ + { + messageId: "generic", + type: AST_NODE_TYPES.AssignmentExpression, + line: 1, + column: 1, + }, + ], + }, ]; export default tests; diff --git a/tests/rules/immutable-data/ts/object/valid.ts b/tests/rules/immutable-data/ts/object/valid.ts index b62efe3f4..2f0655b5b 100644 --- a/tests/rules/immutable-data/ts/object/valid.ts +++ b/tests/rules/immutable-data/ts/object/valid.ts @@ -195,6 +195,12 @@ const tests: Array>> = [ `, optionsSet: [[{ ignoreNonConstDeclarations: true }]], }, + { + code: dedent` + (mutable_foo as Bar).baz = "hello world"; + `, + optionsSet: [[{ ignoreAccessorPattern: "mutable*.*" }]], + }, ]; export default tests; From fc4aaccd3f4bfb15a53c0143d44118d4759e8127 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 25 Mar 2024 05:32:35 +0000 Subject: [PATCH 2/2] chore(release): 6.2.2 [skip ci] ## [6.2.2](https://github.com/eslint-functional/eslint-plugin-functional/compare/v6.2.1...v6.2.2) (2024-03-25) ### Bug Fixes * **immutable-data:** ignore casting when evaluating the expressions ([#792](https://github.com/eslint-functional/eslint-plugin-functional/issues/792)) ([50e789a](https://github.com/eslint-functional/eslint-plugin-functional/commit/50e789a14bf7c776abbe91d4b22d13e50ddf4b01)), closes [#790](https://github.com/eslint-functional/eslint-plugin-functional/issues/790) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c19ba75..4a560b34d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog All notable changes to this project will be documented in this file. Dates are displayed in UTC. +## [6.2.2](https://github.com/eslint-functional/eslint-plugin-functional/compare/v6.2.1...v6.2.2) (2024-03-25) + + +### Bug Fixes + +* **immutable-data:** ignore casting when evaluating the expressions ([#792](https://github.com/eslint-functional/eslint-plugin-functional/issues/792)) ([50e789a](https://github.com/eslint-functional/eslint-plugin-functional/commit/50e789a14bf7c776abbe91d4b22d13e50ddf4b01)), closes [#790](https://github.com/eslint-functional/eslint-plugin-functional/issues/790) + ## [6.2.1](https://github.com/eslint-functional/eslint-plugin-functional/compare/v6.2.0...v6.2.1) (2024-03-24)