Skip to content

Commit

Permalink
Update: Report constructor calls in no-obj-calls (#12909)
Browse files Browse the repository at this point in the history
* Update: Report constructor calls in no-obj-calls

* Add tests for globalThis and unexpectedRefCall

* Remove Math and JSON from no-new-wrappers
  • Loading branch information
mdjermanovic committed Mar 24, 2020
1 parent 2111c52 commit a1370ab
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 27 deletions.
18 changes: 18 additions & 0 deletions docs/rules/no-obj-calls.md
Expand Up @@ -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);
```

Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-new-wrappers.js
Expand Up @@ -32,7 +32,7 @@ module.exports = {
return {

NewExpression(node) {
const wrapperObjects = ["String", "Number", "Boolean", "Math", "JSON"];
const wrapperObjects = ["String", "Number", "Boolean"];

if (wrapperObjects.indexOf(node.callee.name) > -1) {
context.report({
Expand Down
5 changes: 3 additions & 2 deletions lib/rules/no-obj-calls.js
Expand Up @@ -9,7 +9,7 @@
// Requirements
//------------------------------------------------------------------------------

const { CALL, ReferenceTracker } = require("eslint-utils");
const { CALL, CONSTRUCT, ReferenceTracker } = require("eslint-utils");
const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -63,7 +63,8 @@ module.exports = {

for (const g of nonCallableGlobals) {
traceMap[g] = {
[CALL]: true
[CALL]: true,
[CONSTRUCT]: true
};
}

Expand Down
20 changes: 0 additions & 20 deletions tests/lib/rules/no-new-wrappers.js
Expand Up @@ -53,26 +53,6 @@ ruleTester.run("no-new-wrappers", rule, {
},
type: "NewExpression"
}]
},
{
code: "var a = new Math();",
errors: [{
messageId: "noConstructor",
data: {
fn: "Math"
},
type: "NewExpression"
}]
},
{
code: "var a = new JSON({ myProp: 10 });",
errors: [{
messageId: "noConstructor",
data: {
fn: "JSON"
},
type: "NewExpression"
}]
}
]
});
119 changes: 115 additions & 4 deletions tests/lib/rules/no-obj-calls.js
Expand Up @@ -24,9 +24,27 @@ 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 }
},

{ code: "globalThis.Math();", env: { es6: true } },
{ code: "var x = globalThis.Math();", env: { es6: true } },
Expand All @@ -43,33 +61,56 @@ ruleTester.run("no-obj-calls", rule, {

// 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 }
},

// 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); }",
Expand All @@ -81,12 +122,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" }]
Expand All @@ -99,6 +138,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" }]
Expand All @@ -107,6 +166,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: [
Expand All @@ -119,6 +182,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 },
Expand All @@ -128,11 +196,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 },
Expand All @@ -143,11 +220,21 @@ ruleTester.run("no-obj-calls", rule, {
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" }]
},
{
code: "var x = globalThis.Math();",
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }]
},
{
code: "var x = new globalThis.Math();",
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
},
{
code: "f(globalThis.Math());",
env: { es2020: true },
Expand All @@ -158,6 +245,11 @@ ruleTester.run("no-obj-calls", rule, {
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression", column: 1, endColumn: 18 }]
},
{
code: "new globalThis.Math().foo;",
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression", column: 1, endColumn: 22 }]
},
{
code: "var x = globalThis.JSON();",
env: { es2020: true },
Expand All @@ -181,6 +273,11 @@ ruleTester.run("no-obj-calls", rule, {
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }]
},
{
code: "var x = new globalThis.Reflect;",
env: { es2020: true },
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }]
},
{
code: "/*globals Reflect: true*/ Reflect();",
env: { es2020: true },
Expand All @@ -195,15 +292,29 @@ ruleTester.run("no-obj-calls", rule, {
code: "var foo = bar ? baz: JSON; foo();",
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "CallExpression" }]
},
{
code: "var foo = bar ? baz: JSON; new foo();",
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "NewExpression" }]
},
{
code: "var foo = bar ? baz: globalThis.JSON; foo();",
env: { es2020: true },
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "CallExpression" }]
},
{
code: "var foo = bar ? baz: globalThis.JSON; new foo();",
env: { es2020: true },
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "NewExpression" }]
},
{
code: "var foo = window.Atomics; foo();",
env: { es2020: true, browser: true },
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "Atomics" }, type: "CallExpression" }]
},
{
code: "var foo = window.Atomics; new foo;",
env: { es2020: true, browser: true },
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "Atomics" }, type: "NewExpression" }]
}
]
});

0 comments on commit a1370ab

Please sign in to comment.