diff --git a/lib/rules/no-eval.js b/lib/rules/no-eval.js index dab04dd77e2..7af8dfac7fa 100644 --- a/lib/rules/no-eval.js +++ b/lib/rules/no-eval.js @@ -72,15 +72,18 @@ module.exports = { let funcInfo = null; /** - * Pushs a variable scope (Program or Function) information to the stack. + * Pushs a `this` scope (non-arrow function, class static block, or class field initializer) information to the stack. + * Top-level scopes are handled separately. * * This is used in order to check whether or not `this` binding is a * reference to the global object. - * @param {ASTNode} node A node of the scope. This is one of Program, - * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression. + * @param {ASTNode} node A node of the scope. + * For functions, this is one of FunctionDeclaration, FunctionExpression. + * For class static blocks, this is StaticBlock. + * For class field initializers, this can be any node that is PropertyDefinition#value. * @returns {void} */ - function enterVarScope(node) { + function enterThisScope(node) { const strict = context.getScope().isStrict; funcInfo = { @@ -97,7 +100,7 @@ module.exports = { * Pops a variable scope from the stack. * @returns {void} */ - function exitVarScope() { + function exitThisScope() { funcInfo = funcInfo.upper; } @@ -239,21 +242,19 @@ module.exports = { "Program:exit"() { const globalScope = context.getScope(); - exitVarScope(); + exitThisScope(); reportAccessingEval(globalScope); reportAccessingEvalViaGlobalObject(globalScope); }, - FunctionDeclaration: enterVarScope, - "FunctionDeclaration:exit": exitVarScope, - FunctionExpression: enterVarScope, - "FunctionExpression:exit": exitVarScope, - ArrowFunctionExpression: enterVarScope, - "ArrowFunctionExpression:exit": exitVarScope, - "PropertyDefinition > *.value": enterVarScope, - "PropertyDefinition > *.value:exit": exitVarScope, - StaticBlock: enterVarScope, - "StaticBlock:exit": exitVarScope, + FunctionDeclaration: enterThisScope, + "FunctionDeclaration:exit": exitThisScope, + FunctionExpression: enterThisScope, + "FunctionExpression:exit": exitThisScope, + "PropertyDefinition > *.value": enterThisScope, + "PropertyDefinition > *.value:exit": exitThisScope, + StaticBlock: enterThisScope, + "StaticBlock:exit": exitThisScope, ThisExpression(node) { if (!isMember(node.parent, "eval")) { diff --git a/tests/lib/rules/no-eval.js b/tests/lib/rules/no-eval.js index fdd961f6546..840bcb20f1d 100644 --- a/tests/lib/rules/no-eval.js +++ b/tests/lib/rules/no-eval.js @@ -48,6 +48,9 @@ ruleTester.run("no-eval", rule, { { code: "function foo() { this.eval('foo'); }", parserOptions: { ecmaFeatures: { impliedStrict: true } } }, "var obj = {foo: function() { this.eval('foo'); }}", "var obj = {}; obj.foo = function() { this.eval('foo'); }", + { code: "() => { this.eval('foo') }", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "function f() { 'use strict'; () => { this.eval('foo') } }", parserOptions: { ecmaVersion: 6 } }, + { code: "(function f() { 'use strict'; () => { this.eval('foo') } })", parserOptions: { ecmaVersion: 6 } }, { code: "class A { foo() { this.eval(); } }", parserOptions: { ecmaVersion: 6 } }, { code: "class A { static foo() { this.eval(); } }", parserOptions: { ecmaVersion: 6 } }, { code: "class A { field = this.eval(); }", parserOptions: { ecmaVersion: 2022 } }, @@ -95,6 +98,10 @@ ruleTester.run("no-eval", rule, { { code: "var EVAL = eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "Identifier", column: 12, endColumn: 16 }] }, { code: "var EVAL = this.eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression", column: 17, endColumn: 21 }] }, { code: "'use strict'; var EVAL = this.eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression", column: 31, endColumn: 35 }] }, + { code: "() => { this.eval('foo'); }", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 14, endColumn: 18 }] }, + { code: "() => { 'use strict'; this.eval('foo'); }", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 28, endColumn: 32 }] }, + { code: "'use strict'; () => { this.eval('foo'); }", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 28, endColumn: 32 }] }, + { code: "() => { 'use strict'; () => { this.eval('foo'); } }", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 36, endColumn: 40 }] }, { code: "(function(exe){ exe('foo') })(eval);", errors: [{ messageId: "unexpected", type: "Identifier", column: 31, endColumn: 35 }] }, { code: "window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 8, endColumn: 12 }] }, { code: "window.window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 15, endColumn: 19 }] },