Skip to content

Commit a1370ab

Browse files
authoredMar 24, 2020
Update: Report constructor calls in no-obj-calls (#12909)
* Update: Report constructor calls in no-obj-calls * Add tests for globalThis and unexpectedRefCall * Remove Math and JSON from no-new-wrappers
1 parent 2111c52 commit a1370ab

File tree

5 files changed

+137
-27
lines changed

5 files changed

+137
-27
lines changed
 

‎docs/rules/no-obj-calls.md

+18
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,45 @@ And the [ECMAScript 2017 specification](https://www.ecma-international.org/ecma-
1818

1919
This rule disallows calling the `Math`, `JSON`, `Reflect` and `Atomics` objects as functions.
2020

21+
This rule also disallows using these objects as constructors with the `new` operator.
22+
2123
Examples of **incorrect** code for this rule:
2224

2325
```js
2426
/*eslint no-obj-calls: "error"*/
27+
/*eslint-env es2017*/
2528

2629
var math = Math();
30+
31+
var newMath = new Math();
32+
2733
var json = JSON();
34+
35+
var newJSON = new JSON();
36+
2837
var reflect = Reflect();
38+
39+
var newReflect = new Reflect();
40+
2941
var atomics = Atomics();
42+
43+
var newAtomics = new Atomics();
3044
```
3145

3246
Examples of **correct** code for this rule:
3347

3448
```js
3549
/*eslint no-obj-calls: "error"*/
50+
/*eslint-env es2017*/
3651

3752
function area(r) {
3853
return Math.PI * r * r;
3954
}
55+
4056
var object = JSON.parse("{}");
57+
4158
var value = Reflect.get({ x: 1, y: 2 }, "x");
59+
4260
var first = Atomics.load(foo, 0);
4361
```
4462

‎lib/rules/no-new-wrappers.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = {
3232
return {
3333

3434
NewExpression(node) {
35-
const wrapperObjects = ["String", "Number", "Boolean", "Math", "JSON"];
35+
const wrapperObjects = ["String", "Number", "Boolean"];
3636

3737
if (wrapperObjects.indexOf(node.callee.name) > -1) {
3838
context.report({

‎lib/rules/no-obj-calls.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// Requirements
1010
//------------------------------------------------------------------------------
1111

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

1515
//------------------------------------------------------------------------------
@@ -63,7 +63,8 @@ module.exports = {
6363

6464
for (const g of nonCallableGlobals) {
6565
traceMap[g] = {
66-
[CALL]: true
66+
[CALL]: true,
67+
[CONSTRUCT]: true
6768
};
6869
}
6970

‎tests/lib/rules/no-new-wrappers.js

-20
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,6 @@ ruleTester.run("no-new-wrappers", rule, {
5353
},
5454
type: "NewExpression"
5555
}]
56-
},
57-
{
58-
code: "var a = new Math();",
59-
errors: [{
60-
messageId: "noConstructor",
61-
data: {
62-
fn: "Math"
63-
},
64-
type: "NewExpression"
65-
}]
66-
},
67-
{
68-
code: "var a = new JSON({ myProp: 10 });",
69-
errors: [{
70-
messageId: "noConstructor",
71-
data: {
72-
fn: "JSON"
73-
},
74-
type: "NewExpression"
75-
}]
7656
}
7757
]
7858
});

‎tests/lib/rules/no-obj-calls.js

+115-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,27 @@ ruleTester.run("no-obj-calls", rule, {
2424
"var x = Math.random();",
2525
"var x = Math.PI;",
2626
"var x = foo.Math();",
27+
"var x = new foo.Math();",
28+
"var x = new Math.foo;",
29+
"var x = new Math.foo();",
2730
"JSON.parse(foo)",
28-
"Reflect.get(foo, 'x')",
29-
"Atomics.load(foo, 0)",
31+
"new JSON.parse",
32+
{
33+
code: "Reflect.get(foo, 'x')",
34+
env: { es6: true }
35+
},
36+
{
37+
code: "new Reflect.foo(a, b)",
38+
env: { es6: true }
39+
},
40+
{
41+
code: "Atomics.load(foo, 0)",
42+
env: { es2017: true }
43+
},
44+
{
45+
code: "new Atomics.foo()",
46+
env: { es2017: true }
47+
},
3048

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

4462
// non-existing variables
4563
"/*globals Math: off*/ Math();",
64+
"/*globals Math: off*/ new Math();",
4665
{
4766
code: "JSON();",
4867
globals: { JSON: "off" }
4968
},
69+
{
70+
code: "new JSON();",
71+
globals: { JSON: "off" }
72+
},
5073
"Reflect();",
5174
"Atomics();",
75+
"new Reflect();",
76+
"new Atomics();",
5277
{
5378
code: "Atomics();",
5479
env: { es6: true }
5580
},
5681

5782
// shadowed variables
5883
"var Math; Math();",
84+
"var Math; new Math();",
5985
{
6086
code: "let JSON; JSON();",
6187
parserOptions: { ecmaVersion: 2015 }
6288
},
89+
{
90+
code: "let JSON; new JSON();",
91+
parserOptions: { ecmaVersion: 2015 }
92+
},
6393
{
6494
code: "if (foo) { const Reflect = 1; Reflect(); }",
6595
parserOptions: { ecmaVersion: 2015 },
6696
env: { es6: true }
6797
},
98+
{
99+
code: "if (foo) { const Reflect = 1; new Reflect(); }",
100+
parserOptions: { ecmaVersion: 2015 },
101+
env: { es6: true }
102+
},
68103
"function foo(Math) { Math(); }",
104+
"function foo(JSON) { new JSON(); }",
69105
{
70106
code: "function foo(Atomics) { Atomics(); }",
71107
env: { es2017: true }
72108
},
109+
{
110+
code: "function foo() { if (bar) { let Atomics; if (baz) { new Atomics(); } } }",
111+
parserOptions: { ecmaVersion: 2015 },
112+
env: { es2017: true }
113+
},
73114
"function foo() { var JSON; JSON(); }",
74115
{
75116
code: "function foo() { var Atomics = bar(); var baz = Atomics(5); }",
@@ -81,12 +122,10 @@ ruleTester.run("no-obj-calls", rule, {
81122
}
82123
],
83124
invalid: [
84-
85125
{
86126
code: "Math();",
87127
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }]
88128
},
89-
90129
{
91130
code: "var x = Math();",
92131
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }]
@@ -99,6 +138,26 @@ ruleTester.run("no-obj-calls", rule, {
99138
code: "Math().foo;",
100139
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression", column: 1, endColumn: 7 }]
101140
},
141+
{
142+
code: "new Math;",
143+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
144+
},
145+
{
146+
code: "new Math();",
147+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
148+
},
149+
{
150+
code: "new Math(foo);",
151+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
152+
},
153+
{
154+
code: "new Math().foo;",
155+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
156+
},
157+
{
158+
code: "(new Math).foo();",
159+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
160+
},
102161
{
103162
code: "var x = JSON();",
104163
errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "CallExpression" }]
@@ -107,6 +166,10 @@ ruleTester.run("no-obj-calls", rule, {
107166
code: "x = JSON(str);",
108167
errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "CallExpression" }]
109168
},
169+
{
170+
code: "var x = new JSON();",
171+
errors: [{ messageId: "unexpectedCall", data: { name: "JSON" }, type: "NewExpression" }]
172+
},
110173
{
111174
code: "Math( JSON() );",
112175
errors: [
@@ -119,6 +182,11 @@ ruleTester.run("no-obj-calls", rule, {
119182
env: { es6: true },
120183
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }]
121184
},
185+
{
186+
code: "var x = new Reflect();",
187+
env: { es6: true },
188+
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }]
189+
},
122190
{
123191
code: "var x = Reflect();",
124192
env: { es2017: true },
@@ -128,11 +196,20 @@ ruleTester.run("no-obj-calls", rule, {
128196
code: "/*globals Reflect: true*/ Reflect();",
129197
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }]
130198
},
199+
{
200+
code: "/*globals Reflect: true*/ new Reflect();",
201+
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }]
202+
},
131203
{
132204
code: "var x = Atomics();",
133205
env: { es2017: true },
134206
errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "CallExpression" }]
135207
},
208+
{
209+
code: "var x = new Atomics();",
210+
env: { es2017: true },
211+
errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "NewExpression" }]
212+
},
136213
{
137214
code: "var x = Atomics();",
138215
env: { es2020: true },
@@ -143,11 +220,21 @@ ruleTester.run("no-obj-calls", rule, {
143220
globals: { Atomics: false },
144221
errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "CallExpression" }]
145222
},
223+
{
224+
code: "var x = new Atomics();",
225+
globals: { Atomics: "writable" },
226+
errors: [{ messageId: "unexpectedCall", data: { name: "Atomics" }, type: "NewExpression" }]
227+
},
146228
{
147229
code: "var x = globalThis.Math();",
148230
env: { es2020: true },
149231
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression" }]
150232
},
233+
{
234+
code: "var x = new globalThis.Math();",
235+
env: { es2020: true },
236+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression" }]
237+
},
151238
{
152239
code: "f(globalThis.Math());",
153240
env: { es2020: true },
@@ -158,6 +245,11 @@ ruleTester.run("no-obj-calls", rule, {
158245
env: { es2020: true },
159246
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "CallExpression", column: 1, endColumn: 18 }]
160247
},
248+
{
249+
code: "new globalThis.Math().foo;",
250+
env: { es2020: true },
251+
errors: [{ messageId: "unexpectedCall", data: { name: "Math" }, type: "NewExpression", column: 1, endColumn: 22 }]
252+
},
161253
{
162254
code: "var x = globalThis.JSON();",
163255
env: { es2020: true },
@@ -181,6 +273,11 @@ ruleTester.run("no-obj-calls", rule, {
181273
env: { es2020: true },
182274
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "CallExpression" }]
183275
},
276+
{
277+
code: "var x = new globalThis.Reflect;",
278+
env: { es2020: true },
279+
errors: [{ messageId: "unexpectedCall", data: { name: "Reflect" }, type: "NewExpression" }]
280+
},
184281
{
185282
code: "/*globals Reflect: true*/ Reflect();",
186283
env: { es2020: true },
@@ -195,15 +292,29 @@ ruleTester.run("no-obj-calls", rule, {
195292
code: "var foo = bar ? baz: JSON; foo();",
196293
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "CallExpression" }]
197294
},
295+
{
296+
code: "var foo = bar ? baz: JSON; new foo();",
297+
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "NewExpression" }]
298+
},
198299
{
199300
code: "var foo = bar ? baz: globalThis.JSON; foo();",
200301
env: { es2020: true },
201302
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "CallExpression" }]
202303
},
304+
{
305+
code: "var foo = bar ? baz: globalThis.JSON; new foo();",
306+
env: { es2020: true },
307+
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "JSON" }, type: "NewExpression" }]
308+
},
203309
{
204310
code: "var foo = window.Atomics; foo();",
205311
env: { es2020: true, browser: true },
206312
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "Atomics" }, type: "CallExpression" }]
313+
},
314+
{
315+
code: "var foo = window.Atomics; new foo;",
316+
env: { es2020: true, browser: true },
317+
errors: [{ messageId: "unexpectedRefCall", data: { name: "foo", ref: "Atomics" }, type: "NewExpression" }]
207318
}
208319
]
209320
});

0 commit comments

Comments
 (0)
Please sign in to comment.