From 5e7eb824a66daad5493174654efc6cfc11b674c9 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Thu, 13 Feb 2020 07:51:45 +0100 Subject: [PATCH] Update: Report constructor calls in no-obj-calls --- docs/rules/no-obj-calls.md | 18 +++++++ lib/rules/no-obj-calls.js | 5 +- tests/lib/rules/no-obj-calls.js | 90 +++++++++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/docs/rules/no-obj-calls.md b/docs/rules/no-obj-calls.md index 7cd5ac4d4fa..7c8f715cedc 100644 --- a/docs/rules/no-obj-calls.md +++ b/docs/rules/no-obj-calls.md @@ -18,27 +18,45 @@ And the [ECMAScript 2017 specification](https://www.ecma-international.org/ecma- This rule disallows calling the `Math`, `JSON`, `Reflect` and `Atomics` objects as functions. +This rule also disallows using these objects as constructors with the `new` operator. + Examples of **incorrect** code for this rule: ```js /*eslint no-obj-calls: "error"*/ +/*eslint-env es2017*/ var math = Math(); + +var newMath = new Math(); + var json = JSON(); + +var newJSON = new JSON(); + var reflect = Reflect(); + +var newReflect = new Reflect(); + var atomics = Atomics(); + +var newAtomics = new Atomics(); ``` Examples of **correct** code for this rule: ```js /*eslint no-obj-calls: "error"*/ +/*eslint-env es2017*/ function area(r) { return Math.PI * r * r; } + var object = JSON.parse("{}"); + var value = Reflect.get({ x: 1, y: 2 }, "x"); + var first = Atomics.load(foo, 0); ``` diff --git a/lib/rules/no-obj-calls.js b/lib/rules/no-obj-calls.js index 9ff666b0328..82a52ea4d93 100644 --- a/lib/rules/no-obj-calls.js +++ b/lib/rules/no-obj-calls.js @@ -9,7 +9,7 @@ // Requirements //------------------------------------------------------------------------------ -const { CALL, ReferenceTracker } = require("eslint-utils"); +const { CALL, CONSTRUCT, ReferenceTracker } = require("eslint-utils"); //------------------------------------------------------------------------------ // Helpers @@ -49,7 +49,8 @@ module.exports = { for (const g of nonCallableGlobals) { traceMap[g] = { - [CALL]: true + [CALL]: true, + [CONSTRUCT]: true }; } diff --git a/tests/lib/rules/no-obj-calls.js b/tests/lib/rules/no-obj-calls.js index df0c39afb5a..6e44990b5fb 100644 --- a/tests/lib/rules/no-obj-calls.js +++ b/tests/lib/rules/no-obj-calls.js @@ -24,18 +24,43 @@ ruleTester.run("no-obj-calls", rule, { "var x = Math.random();", "var x = Math.PI;", "var x = foo.Math();", + "var x = new foo.Math();", + "var x = new Math.foo;", + "var x = new Math.foo();", "JSON.parse(foo)", - "Reflect.get(foo, 'x')", - "Atomics.load(foo, 0)", + "new JSON.parse", + { + code: "Reflect.get(foo, 'x')", + env: { es6: true } + }, + { + code: "new Reflect.foo(a, b)", + env: { es6: true } + }, + { + code: "Atomics.load(foo, 0)", + env: { es2017: true } + }, + { + code: "new Atomics.foo()", + env: { es2017: true } + }, // non-existing variables "/*globals Math: off*/ Math();", + "/*globals Math: off*/ new Math();", { code: "JSON();", globals: { JSON: "off" } }, + { + code: "new JSON();", + globals: { JSON: "off" } + }, "Reflect();", "Atomics();", + "new Reflect();", + "new Atomics();", { code: "Atomics();", env: { es6: true } @@ -43,20 +68,36 @@ ruleTester.run("no-obj-calls", rule, { // shadowed variables "var Math; Math();", + "var Math; new Math();", { code: "let JSON; JSON();", parserOptions: { ecmaVersion: 2015 } }, + { + code: "let JSON; new JSON();", + parserOptions: { ecmaVersion: 2015 } + }, { code: "if (foo) { const Reflect = 1; Reflect(); }", parserOptions: { ecmaVersion: 2015 }, env: { es6: true } }, + { + code: "if (foo) { const Reflect = 1; new Reflect(); }", + parserOptions: { ecmaVersion: 2015 }, + env: { es6: true } + }, "function foo(Math) { Math(); }", + "function foo(JSON) { new JSON(); }", { code: "function foo(Atomics) { Atomics(); }", env: { es2017: true } }, + { + code: "function foo() { if (bar) { let Atomics; if (baz) { new Atomics(); } } }", + parserOptions: { ecmaVersion: 2015 }, + env: { es2017: true } + }, "function foo() { var JSON; JSON(); }", { code: "function foo() { var Atomics = bar(); var baz = Atomics(5); }", @@ -68,12 +109,10 @@ ruleTester.run("no-obj-calls", rule, { } ], invalid: [ - { code: "Math();", errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }] }, - { code: "var x = Math();", errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }] @@ -86,6 +125,26 @@ ruleTester.run("no-obj-calls", rule, { code: "Math().foo;", errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression", column: 1, endColumn: 7 }] }, + { + code: "new Math;", + errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }] + }, + { + code: "new Math();", + errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }] + }, + { + code: "new Math(foo);", + errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }] + }, + { + code: "new Math().foo;", + errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }] + }, + { + code: "(new Math).foo();", + errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }] + }, { code: "var x = JSON();", errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "CallExpression" }] @@ -94,6 +153,10 @@ ruleTester.run("no-obj-calls", rule, { code: "x = JSON(str);", errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "CallExpression" }] }, + { + code: "var x = new JSON();", + errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "NewExpression" }] + }, { code: "Math( JSON() );", errors: [ @@ -106,6 +169,11 @@ ruleTester.run("no-obj-calls", rule, { env: { es6: true }, errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }] }, + { + code: "var x = new Reflect();", + env: { es6: true }, + errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }] + }, { code: "var x = Reflect();", env: { es2017: true }, @@ -115,11 +183,20 @@ ruleTester.run("no-obj-calls", rule, { code: "/*globals Reflect: true*/ Reflect();", errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }] }, + { + code: "/*globals Reflect: true*/ new Reflect();", + errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }] + }, { code: "var x = Atomics();", env: { es2017: true }, errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "CallExpression" }] }, + { + code: "var x = new Atomics();", + env: { es2017: true }, + errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "NewExpression" }] + }, { code: "var x = Atomics();", env: { es2020: true }, @@ -129,6 +206,11 @@ ruleTester.run("no-obj-calls", rule, { code: "var x = Atomics();", globals: { Atomics: false }, errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "CallExpression" }] + }, + { + code: "var x = new Atomics();", + globals: { Atomics: "writable" }, + errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "NewExpression" }] } ] });