Repository: eslint/eslint (JavaScript)

Issue:
`no-new-object`: warns on non-global Object instantiation
**Tell us about your environment**

https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IG5vLW5ldy1vYmplY3Q6IDIsIG5vLXJlZGVjbGFyZTogMCAqL1xudmFyIE9iamVjdCA9IGZ1bmN0aW9uIE9iamVjdCgpIHt9O1xubmV3IE9iamVjdCgpOyIsIm9wdGlvbnMiOnsicGFyc2VyT3B0aW9ucyI6eyJlY21hVmVyc2lvbiI6NSwic291cmNlVHlwZSI6InNjcmlwdCIsImVjbWFGZWF0dXJlcyI6e319LCJydWxlcyI6eyJjb25zdHJ1Y3Rvci1zdXBlciI6MiwiZm9yLWRpcmVjdGlvbiI6MiwiZ2V0dGVyLXJldHVybiI6Miwibm8tYXN5bmMtcHJvbWlzZS1leGVjdXRvciI6Miwibm8tY2FzZS1kZWNsYXJhdGlvbnMiOjIsIm5vLWNsYXNzLWFzc2lnbiI6Miwibm8tY29tcGFyZS1uZWctemVybyI6Miwibm8tY29uZC1hc3NpZ24iOjIsIm5vLWNvbnN0LWFzc2lnbiI6Miwibm8tY29uc3RhbnQtY29uZGl0aW9uIjoyLCJuby1jb250cm9sLXJlZ2V4IjoyLCJuby1kZWJ1Z2dlciI6Miwibm8tZGVsZXRlLXZhciI6Miwibm8tZHVwZS1hcmdzIjoyLCJuby1kdXBlLWNsYXNzLW1lbWJlcnMiOjIsIm5vLWR1cGUta2V5cyI6Miwibm8tZHVwbGljYXRlLWNhc2UiOjIsIm5vLWVtcHR5IjoyLCJuby1lbXB0eS1jaGFyYWN0ZXItY2xhc3MiOjIsIm5vLWVtcHR5LXBhdHRlcm4iOjIsIm5vLWV4LWFzc2lnbiI6Miwibm8tZXh0cmEtYm9vbGVhbi1jYXN0IjoyLCJuby1leHRyYS1zZW1pIjoyLCJuby1mYWxsdGhyb3VnaCI6Miwibm8tZnVuYy1hc3NpZ24iOjIsIm5vLWdsb2JhbC1hc3NpZ24iOjIsIm5vLWlubmVyLWRlY2xhcmF0aW9ucyI6Miwibm8taW52YWxpZC1yZWdleHAiOjIsIm5vLWlycmVndWxhci13aGl0ZXNwYWNlIjoyLCJuby1taXNsZWFkaW5nLWNoYXJhY3Rlci1jbGFzcyI6Miwibm8tbWl4ZWQtc3BhY2VzLWFuZC10YWJzIjoyLCJuby1uZXctc3ltYm9sIjoyLCJuby1vYmotY2FsbHMiOjIsIm5vLW9jdGFsIjoyLCJuby1wcm90b3R5cGUtYnVpbHRpbnMiOjIsIm5vLXJlZGVjbGFyZSI6Miwibm8tcmVnZXgtc3BhY2VzIjoyLCJuby1zZWxmLWFzc2lnbiI6Miwibm8tc2hhZG93LXJlc3RyaWN0ZWQtbmFtZXMiOjIsIm5vLXNwYXJzZS1hcnJheXMiOjIsIm5vLXRoaXMtYmVmb3JlLXN1cGVyIjoyLCJuby11bmRlZiI6Miwibm8tdW5leHBlY3RlZC1tdWx0aWxpbmUiOjIsIm5vLXVucmVhY2hhYmxlIjoyLCJuby11bnNhZmUtZmluYWxseSI6Miwibm8tdW5zYWZlLW5lZ2F0aW9uIjoyLCJuby11bnVzZWQtbGFiZWxzIjoyLCJuby11bnVzZWQtdmFycyI6Miwibm8tdXNlbGVzcy1jYXRjaCI6Miwibm8tdXNlbGVzcy1lc2NhcGUiOjIsIm5vLXdpdGgiOjIsInJlcXVpcmUteWllbGQiOjIsInVzZS1pc25hbiI6MiwidmFsaWQtdHlwZW9mIjoyfSwiZW52Ijp7fX19

**What did you expect to happen?**
No warning.

**What actually happened? Please include the actual, raw output from ESLint.**
`The object literal notation {} is preferrable. eslint(no-new-object)`

**Are you willing to submit a pull request to fix this bug?**
Sure.


Discussion:
There is an inconsistency between rules that target particular use of some globals:

* Some rules check just identifier names (e.g., `no-new-object`).
* Some rules additionally check if the variable is shadowed (e.g., `radix`).
* Some rules use `eslint-utils.ReferenceTracker` (e.g., `no-obj-calls`).

```js
/* eslint no-new-object: "error" */
/* eslint radix: "error" */
/* eslint no-obj-calls: "error" */

/* eslint-env browser*/

new Object(); // error
parseInt(a); // error
JSON(); // error

new window.Object(); // ok
window.parseInt(a); // ok
window.JSON(); // error

function foo(Object, parseInt, JSON) {
    new Object(); // error
    parseInt(a); // ok
    JSON(); // ok
}
```

[Online Demo Link](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IG5vLW5ldy1vYmplY3Q6IFwiZXJyb3JcIiAqL1xuLyogZXNsaW50IHJhZGl4OiBcImVycm9yXCIgKi9cbi8qIGVzbGludCBuby1vYmotY2FsbHM6IFwiZXJyb3JcIiAqL1xuXG4vKiBlc2xpbnQtZW52IGJyb3dzZXIqL1xuXG5uZXcgT2JqZWN0KCk7IC8vIGVycm9yXG5wYXJzZUludChhKTsgLy8gZXJyb3JcbkpTT04oKTsgLy8gZXJyb3JcblxubmV3IHdpbmRvdy5PYmplY3QoKTsgLy8gb2tcbndpbmRvdy5wYXJzZUludChhKTsgLy8gb2tcbndpbmRvdy5KU09OKCk7IC8vIGVycm9yXG5cbmZ1bmN0aW9uIGZvbyhPYmplY3QsIHBhcnNlSW50LCBKU09OKSB7XG4gICAgbmV3IE9iamVjdCgpOyAvLyBlcnJvclxuICAgIHBhcnNlSW50KGEpOyAvLyBva1xuICAgIEpTT04oKTsgLy8gb2tcbn1cbiIsIm9wdGlvbnMiOnsicGFyc2VyT3B0aW9ucyI6eyJlY21hVmVyc2lvbiI6Niwic291cmNlVHlwZSI6InNjcmlwdCIsImVjbWFGZWF0dXJlcyI6e319LCJydWxlcyI6e30sImVudiI6e319fQ==)

Bug fix for `no-new-object` could be to do something like `radix`, and that shouldn't produce any new warnings.

On the other hand, it might be nice to align the rules of this kind and use `ReferenceTracker` in all of them. 

But, it would also start reporting code such as `new window.Object()` or `var x = Object; new x();`, which is an enhancement I guess (maybe even breaking?). 

Thoughts on how to proceed?

Since v7 is coming, I'd definitely want to get all those ReferenceTracker changes in.

In general, I feel like anyone who enables this rule *wants* those things reported, so i'd consider it a patch.
I have concerns about cases such as the following, which would be reported if this rule uses `ReferenceTracker`:

```js
var x = something ? MyClass : Object;

var y = new x();
```

In this case, user can't just replace something with `{}`. The code has to be refactored to avoid warning. There could be a more complex example that makes sense but wouldn't satisfy the new version of this rule.

I'm wondering is `ReferenceTracker` suitable for some rules that are already using it, because it tracks all *potential* references to the targeted global.

It seems useful in `no-obj-calls` as it can warn about possible calls to `Math`, `JSON` and other non-callable globals in some code paths, which is indeed a runtime error.

On the other hand, this looks like a false positive in the `prefer-object-spread` rule and an autofix that changes the behavior:

```js
/* eslint prefer-object-spread: ["error"] */

var doSomething = foo ? myFunc : Object.assign;

var x = doSomething({}, a, b); // gets fixed to: var x = { ...a, ...b};
```

[Online Demo](https://eslint.org/demo#eyJ0ZXh0IjoiLyogZXNsaW50IHByZWZlci1vYmplY3Qtc3ByZWFkOiBbXCJlcnJvclwiXSAqL1xuXG52YXIgZG9Tb21ldGhpbmcgPSBmb28gPyBteUZ1bmMgOiBPYmplY3QuYXNzaWduO1xuXG52YXIgeCA9IGRvU29tZXRoaW5nKHt9LCBhLCBiKTsiLCJvcHRpb25zIjp7InBhcnNlck9wdGlvbnMiOnsiZWNtYVZlcnNpb24iOjksInNvdXJjZVR5cGUiOiJzY3JpcHQiLCJlY21hRmVhdHVyZXMiOnt9fSwicnVsZXMiOnt9LCJlbnYiOnt9fX0=)

We should probably revisit those rules.


In that case, that code should be rewritten, as `new` should never be used with `Object` :-)
I certainly do agree, though, that if a reference does not *always* result in being a thing, it shouldn't be warned/autofixed as being that thing.
@ljharb Agreed that this case is a bug.

@mdjermanovic Thanks for digging into this! Could we create a new meta issue with all the differences in how we handle globals at the moment?
I'll open an issue for globals, it would be nice to unify that.

As for this issue with `no-new-objects`, it looks safe for now to just fix the shadowing bug, without `ReferenceTracker`?



Patch:
diff --git a/docs/rules/no-new-object.md b/docs/rules/no-new-object.md
index 5186682fc058..aef055c810b5 100644
--- a/docs/rules/no-new-object.md
+++ b/docs/rules/no-new-object.md
@@ -27,7 +27,7 @@ Examples of **incorrect** code for this rule:
 
 var myObject = new Object();
 
-var myObject = new Object;
+new Object();
 ```
 
 Examples of **correct** code for this rule:
@@ -38,6 +38,9 @@ Examples of **correct** code for this rule:
 var myObject = new CustomObject();
 
 var myObject = {};
+
+var Object = function Object() {};
+new Object();
 ```
 
 ## When Not To Use It
diff --git a/lib/rules/no-new-object.js b/lib/rules/no-new-object.js
index f3e99c9bd135..e9f915db5eaa 100644
--- a/lib/rules/no-new-object.js
+++ b/lib/rules/no-new-object.js
@@ -5,6 +5,12 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -28,10 +34,17 @@ module.exports = {
     },
 
     create(context) {
-
         return {
-
             NewExpression(node) {
+                const variable = astUtils.getVariableByName(
+                    context.getScope(),
+                    node.callee.name
+                );
+
+                if (variable && variable.identifiers.length > 0) {
+                    return;
+                }
+
                 if (node.callee.name === "Object") {
                     context.report({
                         node,
@@ -40,6 +53,5 @@ module.exports = {
                 }
             }
         };
-
     }
 };


Test patch:
diff --git a/tests/lib/rules/no-new-object.js b/tests/lib/rules/no-new-object.js
index 667da7c43970..0e8f5eb99573 100644
--- a/tests/lib/rules/no-new-object.js
+++ b/tests/lib/rules/no-new-object.js
@@ -20,15 +20,50 @@ const ruleTester = new RuleTester();
 
 ruleTester.run("no-new-object", rule, {
     valid: [
-        "var foo = new foo.Object()"
+        "var myObject = {};",
+        "var myObject = new CustomObject();",
+        "var foo = new foo.Object()",
+        `var Object = function Object() {};
+            new Object();`,
+        `var x = something ? MyClass : Object;
+        var y = new x();`,
+        {
+            code: `
+        class Object {
+            constructor(){
+
+            }
+        }
+        new Object();
+        `,
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: `
+        import { Object } from './'
+        new Object();
+        `,
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        }
     ],
     invalid: [
         {
             code: "var foo = new Object()",
-            errors: [{
-                messageId: "preferLiteral",
-                type: "NewExpression"
-            }]
+            errors: [
+                {
+                    messageId: "preferLiteral",
+                    type: "NewExpression"
+                }
+            ]
+        },
+        {
+            code: "new Object();",
+            errors: [{ messageId: "preferLiteral", type: "NewExpression" }]
+        },
+        {
+            code: "const a = new Object()",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ messageId: "preferLiteral", type: "NewExpression" }]
         }
     ]
 });