Repository: eslint/eslint (js)

Issue:
`prefer-object-spread` rule should not report `Object.assign({}, ...array)`
**Tell us about your environment**

* **ESLint Version:** 5.0.0-alpha.3
* **Node Version:** 10.1.0
* **npm Version:** 5.6.0

**What parser (default, Babel-ESLint, etc.) are you using?** default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```yml
root: true
parserOptions:
  ecmaVersion: 2018
  sourceType: script
rules:
  prefer-object-spread: 2
```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
```js
const objects = array.map(mapObject)
const var = Object.assign({}, ...objects)
```

<!-- Paste the command you used to run ESLint: -->
```bash
eslint .
```

**What did you expect to happen?**
`Object.assign()` should not be reported as wrong. There is no way to use object spreads in this example because `objects` is an array of objects. It is not possible to use object spreads to concatenate an array of objects (e.g. `[{ ... }, { ... }]`) into a single object.

The `prefer-object-spread` rule should not report a linting error when one of the arguments to `Object.assign()` uses an array spread.

This also means the above line with `--fix` would be converted to `{ ...objects }` which is incorrect.

**What actually happened? Please include the actual, raw output from ESLint.**
`Object.assign()` was reported as wrong. 

Pull request description:
Fix: allow array spread for prefer-object-spread rule (fixes #10344)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[x] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->
https://github.com/eslint/eslint/issues/10344

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**
Allow, eg:
```js
Object.assign({}, ...objects)
```

**Is there anything you'd like reviewers to focus on?**




Patch:
diff --git a/docs/user-guide/integrations.md b/docs/user-guide/integrations.md
index 159066348..17d037860 100644
--- a/docs/user-guide/integrations.md
+++ b/docs/user-guide/integrations.md
@@ -27,7 +27,7 @@
 * Rollup: [rollup-plugin-eslint](https://www.npmjs.com/package/rollup-plugin-eslint)
 * Ember-cli: [ember-cli-eslint](https://www.npmjs.com/package/ember-cli-eslint)
 * Sails.js: [sails-hook-lint](https://www.npmjs.com/package/sails-hook-lint), [sails-eslint](https://www.npmjs.com/package/sails-eslint)
-* Start: [start-eslint](https://www.npmjs.com/package/start-eslint)
+* Start: [@start/plugin-lib-eslint](https://www.npmjs.com/package/@start/plugin-lib-eslint)
 * Brunch: [eslint-brunch](https://www.npmjs.com/package/eslint-brunch)
 
 ## Command Line Tools
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 46d561516..87f60f91c 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -567,6 +567,22 @@ function load(filePath, configContext, relativeTo) {
     return config;
 }
 
+/**
+ * Checks whether the given filename points to a file
+ * @param {string} filename A path to a file
+ * @returns {boolean} `true` if a file exists at the given location
+ */
+function isExistingFile(filename) {
+    try {
+        return fs.statSync(filename).isFile();
+    } catch (err) {
+        if (err.code === "ENOENT") {
+            return false;
+        }
+        throw err;
+    }
+}
+
 
 //------------------------------------------------------------------------------
 // Public Interface
@@ -591,14 +607,6 @@ module.exports = {
      *      or null if there is no configuration file in the directory.
      */
     getFilenameForDirectory(directory) {
-        for (let i = 0, len = CONFIG_FILES.length; i < len; i++) {
-            const filename = path.join(directory, CONFIG_FILES[i]);
-
-            if (fs.existsSync(filename) && fs.statSync(filename).isFile()) {
-                return filename;
-            }
-        }
-
-        return null;
+        return CONFIG_FILES.map(filename => path.join(directory, filename)).find(isExistingFile) || null;
     }
 };
diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js
index 67c23a8a6..6c298d364 100644
--- a/lib/config/config-ops.js
+++ b/lib/config/config-ops.js
@@ -240,14 +240,7 @@ module.exports = {
      * @returns {boolean} True if the rule represents an error, false if not.
      */
     isErrorSeverity(ruleConfig) {
-
-        let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
-
-        if (typeof severity === "string") {
-            severity = RULE_SEVERITY[severity.toLowerCase()] || 0;
-        }
-
-        return (typeof severity === "number" && severity === 2);
+        return module.exports.getRuleSeverity(ruleConfig) === 2;
     },
 
     /**
diff --git a/lib/rules/prefer-object-spread.js b/lib/rules/prefer-object-spread.js
index 6a213c690..aa69824a5 100644
--- a/lib/rules/prefer-object-spread.js
+++ b/lib/rules/prefer-object-spread.js
@@ -22,6 +22,15 @@ function isObjectAssign(node) {
     );
 }
 
+/**
+ * Helper that checks if the Object.assign call has array spread
+ * @param {ASTNode} node - The node that the rule warns on
+ * @returns {boolean} - Returns true if the Object.assign call has array spread
+ */
+function hasArraySpread(node) {
+    return node.arguments.some(arg => arg.type === "SpreadElement");
+}
+
 /**
  * Helper that checks if the node needs parentheses to be valid JS.
  * The default is to wrap the node in parentheses to avoid parsing errors.
@@ -264,7 +273,8 @@ module.exports = {
                     !overWritingNativeObject &&
                     node.arguments.length > 1 &&
                     node.arguments[0].type === "ObjectExpression" &&
-                    isObjectAssign(node)
+                    isObjectAssign(node) &&
+                    !hasArraySpread(node)
                 ) {
                     context.report({
                         node,


Test patch:
diff --git a/tests/lib/rules/prefer-object-spread.js b/tests/lib/rules/prefer-object-spread.js
index 51c7e8be3..75b422f8b 100644
--- a/tests/lib/rules/prefer-object-spread.js
+++ b/tests/lib/rules/prefer-object-spread.js
@@ -28,6 +28,7 @@ ruleTester.run("prefer-object-spread", rule, {
         "const bar = { ...foo }",
         "Object.assign(...foo)",
         "Object.assign(foo, { bar: baz })",
+        "Object.assign({}, ...objects)",
         "foo({ foo: 'bar' })",
         `
         const Object = {};
@@ -536,18 +537,6 @@ ruleTester.run("prefer-object-spread", rule, {
                 }
             ]
         },
-        {
-            code: "let a = Object.assign({}, ...b)",
-            output: "let a = {...b}",
-            errors: [
-                {
-                    messageId: "useSpreadMessage",
-                    type: "CallExpression",
-                    line: 1,
-                    column: 9
-                }
-            ]
-        },
         {
             code: "function foo() { return Object.assign({}, a) }",
             output: "function foo() { return {...a} }",