From 53d750800b1c0c1f8c29393c488bb3167bb1d2a5 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 2 Aug 2023 16:52:21 +0200 Subject: [PATCH] feat: update regex for methods with `thisArg` (#17439) * feat: update regex for methods with `thisArg` * rename regex constant --- lib/rules/utils/ast-utils.js | 8 +-- tests/lib/rules/no-eval.js | 24 +++++++ tests/lib/rules/no-invalid-this.js | 112 ++++++++--------------------- 3 files changed, 56 insertions(+), 88 deletions(-) diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js index 08a23c88878..bebb4d5168b 100644 --- a/lib/rules/utils/ast-utils.js +++ b/lib/rules/utils/ast-utils.js @@ -26,8 +26,8 @@ const { const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u; +const arrayMethodWithThisArgPattern = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|some)$/u; const arrayOrTypedArrayPattern = /Array$/u; -const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u; const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u; const thisTagPattern = /^[\s*]*@this/mu; @@ -467,12 +467,12 @@ function isArrayFromMethod(node) { } /** - * Checks whether or not a node is a method which has `thisArg`. + * Checks whether or not a node is a method which expects a function as a first argument, and `thisArg` as a second argument. * @param {ASTNode} node A node to check. - * @returns {boolean} Whether or not the node is a method which has `thisArg`. + * @returns {boolean} Whether or not the node is a method which expects a function as a first argument, and `thisArg` as a second argument. */ function isMethodWhichHasThisArg(node) { - return isSpecificMemberAccess(node, null, arrayMethodPattern); + return isSpecificMemberAccess(node, null, arrayMethodWithThisArgPattern); } /** diff --git a/tests/lib/rules/no-eval.js b/tests/lib/rules/no-eval.js index 840bcb20f1d..de1e11df19e 100644 --- a/tests/lib/rules/no-eval.js +++ b/tests/lib/rules/no-eval.js @@ -57,6 +57,11 @@ ruleTester.run("no-eval", rule, { { code: "class A { field = () => this.eval(); }", parserOptions: { ecmaVersion: 2022 } }, { code: "class A { static { this.eval(); } }", parserOptions: { ecmaVersion: 2022 } }, + // User-defined this.eval in callbacks + "array.findLast(function (x) { return this.eval.includes(x); }, { eval: ['foo', 'bar'] });", + "callbacks.findLastIndex(function (cb) { return cb(this.eval); }, this);", + "['1+1'].flatMap(function (str) { return this.eval(str); }, new Evaluator);", + // Allows indirect eval { code: "(0, eval)('foo')", options: [{ allowIndirect: true }] }, { code: "(0, window.eval)('foo')", options: [{ allowIndirect: true }], env: { browser: true } }, @@ -162,6 +167,25 @@ ruleTester.run("no-eval", rule, { code: "function foo() { 'use strict'; this.eval(); }", parserOptions: { ecmaVersion: 3 }, errors: [{ messageId: "unexpected" }] + }, + + // this.eval in callbacks (not user-defined) + { + code: "array.findLast(x => this.eval.includes(x), { eval: 'abc' });", + parserOptions: { ecmaVersion: 2023 }, + errors: [{ messageId: "unexpected" }] + }, + { + code: "callbacks.findLastIndex(function (cb) { return cb(eval); }, this);", + errors: [{ messageId: "unexpected" }] + }, + { + code: "['1+1'].flatMap(function (str) { return this.eval(str); });", + errors: [{ messageId: "unexpected" }] + }, + { + code: "['1'].reduce(function (a, b) { return this.eval(a) ? a : b; }, '0');", + errors: [{ messageId: "unexpected" }] } ] }); diff --git a/tests/lib/rules/no-invalid-this.js b/tests/lib/rules/no-invalid-this.js index dd72be49991..7e8d4776d9b 100644 --- a/tests/lib/rules/no-invalid-this.js +++ b/tests/lib/rules/no-invalid-this.js @@ -474,103 +474,47 @@ const patterns = [ valid: [NORMAL], invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] }, - { - code: "foo.every(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.filter(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.find(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.findIndex(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.forEach(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.map(function() { console.log(this); z(x => console.log(x, this)); });", - parserOptions: { ecmaVersion: 6 }, - errors, - valid: [NORMAL], - invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, - { - code: "foo.some(function() { console.log(this); z(x => console.log(x, this)); });", + ...[ + "every", + "filter", + "find", + "findIndex", + "findLast", + "findLastIndex", + "flatMap", + "forEach", + "map", + "some" + ].map(methodName => ({ + code: `foo.${methodName}(function() { console.log(this); z(x => console.log(x, this)); });`, parserOptions: { ecmaVersion: 6 }, errors, valid: [NORMAL], invalid: [USE_STRICT, IMPLIED_STRICT, MODULES] - }, + })), { code: "Array.from([], function() { console.log(this); z(x => console.log(x, this)); }, obj);", parserOptions: { ecmaVersion: 6 }, valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], invalid: [] }, - { - code: "foo.every(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, - { - code: "foo.filter(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, - { - code: "foo.find(function() { console.log(this); z(x => console.log(x, this)); }, obj);", + ...[ + "every", + "filter", + "find", + "findIndex", + "findLast", + "findLastIndex", + "flatMap", + "forEach", + "map", + "some" + ].map(methodName => ({ + code: `foo.${methodName}(function() { console.log(this); z(x => console.log(x, this)); }, obj);`, parserOptions: { ecmaVersion: 6 }, valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], invalid: [] - }, - { - code: "foo.findIndex(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, - { - code: "foo.forEach(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, - { - code: "foo.map(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, - { - code: "foo.some(function() { console.log(this); z(x => console.log(x, this)); }, obj);", - parserOptions: { ecmaVersion: 6 }, - valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES], - invalid: [] - }, + })), { code: "foo.forEach(function() { console.log(this); z(x => console.log(x, this)); }, null);", parserOptions: { ecmaVersion: 6 },