Repository: eslint/eslint (js)

Issue:
Adding ignore-path flag referencing a file in node_modules not respecting ignore config
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for bug reports. If you are reporting a bug, please continue on. If you are here for another reason, please see below:

    1. To propose a new rule: http://eslint.org/docs/developer-guide/contributing/new-rules
    2. To request a change: http://eslint.org/docs/developer-guide/contributing/changes
    3. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**Tell us about your environment**

* **ESLint Version:** 4.6.1 (latest)
* **Node Version:** 8.4.0 (latest)
* **npm Version:** 5.4.0 (latest)

**What parser (default, Babel-ESLint, etc.) are you using?** N/A

**Please show your full configuration:** N/A

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
{}
```

</details>

**What did you do? Please include the actual source code causing the issue.**

<!-- Paste the source code below: -->
```
eslint --ignore-path node_modules/kcd-scripts/dist/config/eslintignore .
```

**What did you expect to happen?**

I expected the eslintignore to be applied

**What actually happened? Please include the actual, raw output from ESLint.**

It is not. I get an error indicating that eslint is processing files in `node_modules`.

Here's the contents of `node_modules/kcd-scripts/dist/config/eslintignore`:

```
node_modules
coverage
dist
build
out
.next
```

I think that the docs indicate that the patterns here will be relative to the `.eslintignore` file, so I changed the eslintignore file to various patterns (including `../../../node_modules/`, `**/node_modules/**`). None of those combinations seemed to work. I think there may be a bug here, but I'm uncertain... Happy to dig in more if someone can tell me I'm not missing something obvious.

To reproduce: https://github.com/kentcdodds/eslint-config-issue

Install deps and run `npm run broken` and `npm run working`.

Thanks!

Pull request description:
Fix: always resolve default ignore patterns from CWD (fixes #9227)
<!--
    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)**

[x] Bug fix (https://github.com/eslint/eslint/issues/9227)

<!--
    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.)
-->

<!--
    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)**

This fixes an issue where the default ignore patterns, such as `/node_modules`, would always be resolved from the location of an `.eslintignore` file. As a result of the bug, the `node_modules` folder in the project root directory would not be ignored if an `.eslintignore` file in a subdirectory was being used.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular

Patch:
diff --git a/Makefile.js b/Makefile.js
index 9c48620ef..cd756a746 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -684,7 +684,7 @@ target.gensite = function(prereleaseVersion) {
     const rules = require(".").linter.getRules();
 
     const RECOMMENDED_TEXT = "\n\n(recommended) The `\"extends\": \"eslint:recommended\"` property in a configuration file enables this rule.";
-    const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.";
+    const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.";
 
     // 4. Loop through all files in temporary directory
     process.stdout.write("> Updating files (Steps 4-9): 0/... - ...\r");
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index e56fd5789..2e3390b93 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -330,7 +330,7 @@ var CLIEngine = require("eslint").CLIEngine;
 The `CLIEngine` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:
 
 * `allowInlineConfig` - Set to `false` to disable the use of configuration comments (such as `/*eslint-disable*/`). Corresponds to `--no-inline-config`.
-* `baseConfig` - Set to false to disable use of base config. Could be set to an object to override default base config as well.
+* `baseConfig` - Can optionally be set to a config object. This will used as a default config, and will be merged with any configuration defined in `.eslintrc.*` files, with the `.eslintrc.*` files having precedence.
 * `cache` - Operate only on changed files (default: `false`). Corresponds to `--cache`.
 * `cacheFile` - Name of the file where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-file`. Deprecated: use `cacheLocation` instead.
 * `cacheLocation` - Name of the file or directory where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-location`.
diff --git a/docs/rules/dot-location.md b/docs/rules/dot-location.md
index c4283cf7f..1fe4a3129 100644
--- a/docs/rules/dot-location.md
+++ b/docs/rules/dot-location.md
@@ -20,7 +20,7 @@ This rule aims to enforce newline consistency in member expressions. This rule p
 
 The rule takes one option, a string:
 
-* If it is `"object"`, the dot in a member expression should be on the same line as the object portion. The default is `"object"`.
+* If it is `"object"` (default), the dot in a member expression should be on the same line as the object portion.
 * If it is `"property"`, the dot in a member expression should be on the same line as the property portion.
 
 ### object
diff --git a/docs/rules/no-prototype-builtins.md b/docs/rules/no-prototype-builtins.md
index 1694356a3..e2dd4f5b9 100644
--- a/docs/rules/no-prototype-builtins.md
+++ b/docs/rules/no-prototype-builtins.md
@@ -1,6 +1,6 @@
 # Disallow use of Object.prototypes builtins directly (no-prototype-builtins)
 
-In ECMAScript 5.1, `Object.create` was added, which enables the creation of objects with a specified `[[Prototype]]`. `Object.create(null)` is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from `Object.prototype`. This rule prevents calling `Object.prototype` methods directly from an object.
+In ECMAScript 5.1, `Object.create` was added, which enables the creation of objects with a specified `[[Prototype]]`. `Object.create(null)` is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from `Object.prototype`. This rule prevents calling some `Object.prototype` methods directly from an object.
 
 ## Rule Details
 
diff --git a/docs/rules/no-unsafe-negation.md b/docs/rules/no-unsafe-negation.md
index 689796209..8e1428ea9 100644
--- a/docs/rules/no-unsafe-negation.md
+++ b/docs/rules/no-unsafe-negation.md
@@ -46,10 +46,6 @@ if(("" + !key) in object) {
 }
 ```
 
-## Options
-
-Nothing.
-
 ## When Not To Use It
 
 If you don't want to notify unsafe logical negations, then it's safe to disable this rule.
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 8442de2de..aa0f5521c 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -41,7 +41,7 @@ const resolver = new ModuleResolver();
  * The options to configure a CLI engine with.
  * @typedef {Object} CLIEngineOptions
  * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
- * @property {boolean|Object} baseConfig Base config object. True enables recommend rules and environments.
+ * @property {Object} baseConfig Base config object, extended by all configs used with this CLIEngine instance
  * @property {boolean} cache Enable result caching.
  * @property {string} cacheLocation The cache file to use instead of .eslintcache.
  * @property {string} configFile The configuration file to use.
diff --git a/lib/ignored-paths.js b/lib/ignored-paths.js
index 8134d6d32..4ef15ae75 100644
--- a/lib/ignored-paths.js
+++ b/lib/ignored-paths.js
@@ -79,6 +79,27 @@ function mergeDefaultOptions(options) {
     return Object.assign({}, DEFAULT_OPTIONS, options);
 }
 
+/**
+ * Converts a glob pattern to a new glob pattern relative to a different directory
+ * @param {string} globPattern The glob pattern, relative the the old base directory
+ * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one
+ * @returns {string} A glob pattern relative to the new base directory
+ */
+function relativize(globPattern, relativePathToOldBaseDir) {
+    if (relativePathToOldBaseDir === "") {
+        return globPattern;
+    }
+
+    const prefix = globPattern.startsWith("!") ? "!" : "";
+    const globWithoutPrefix = globPattern.replace(/^!/, "");
+
+    if (globWithoutPrefix.startsWith("/")) {
+        return `${prefix}/${relativePathToOldBaseDir}${globWithoutPrefix}`;
+    }
+
+    return globPattern;
+}
+
 //------------------------------------------------------------------------------
 // Public Interface
 //------------------------------------------------------------------------------
@@ -96,41 +117,35 @@ class IgnoredPaths {
 
         this.cache = {};
 
-        /**
-         * add pattern to node-ignore instance
-         * @param {Object} ig, instance of node-ignore
-         * @param {string} pattern, pattern do add to ig
-         * @returns {array} raw ignore rules
-         */
-        function addPattern(ig, pattern) {
-            return ig.addPattern(pattern);
-        }
-
         this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []);
-        this.baseDir = options.cwd;
+
+        this.ignoreFileDir = options.ignore !== false && options.ignorePath
+            ? path.dirname(path.resolve(options.cwd, options.ignorePath))
+            : options.cwd;
+        this.options = options;
 
         this.ig = {
             custom: ignore(),
             default: ignore()
         };
 
-        /*
-         * Add a way to keep track of ignored files.  This was present in node-ignore
-         * 2.x, but dropped for now as of 3.0.10.
-         */
-        this.ig.custom.ignoreFiles = [];
-        this.ig.default.ignoreFiles = [];
-
+        this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern));
         if (options.dotfiles !== true) {
 
             /*
              * ignore files beginning with a dot, but not files in a parent or
              * ancestor directory (which in relative format will begin with `../`).
              */
-            addPattern(this.ig.default, [".*", "!../"]);
+            this.addPatternRelativeToCwd(this.ig.default, ".*");
+            this.addPatternRelativeToCwd(this.ig.default, "!../");
         }
 
-        addPattern(this.ig.default, this.defaultPatterns);
+        /*
+         * Add a way to keep track of ignored files.  This was present in node-ignore
+         * 2.x, but dropped for now as of 3.0.10.
+         */
+        this.ig.custom.ignoreFiles = [];
+        this.ig.default.ignoreFiles = [];
 
         if (options.ignore !== false) {
             let ignorePath;
@@ -154,13 +169,11 @@ class IgnoredPaths {
                     debug(`Loaded ignore file ${ignorePath}`);
                 } catch (e) {
                     debug("Could not find ignore file in cwd");
-                    this.options = options;
                 }
             }
 
             if (ignorePath) {
                 debug(`Adding ${ignorePath}`);
-                this.baseDir = path.dirname(path.resolve(options.cwd, ignorePath));
                 this.addIgnoreFile(this.ig.custom, ignorePath);
                 this.addIgnoreFile(this.ig.default, ignorePath);
             } else {
@@ -187,8 +200,8 @@ class IgnoredPaths {
                         if (packageJSONOptions.eslintIgnore) {
                             if (Array.isArray(packageJSONOptions.eslintIgnore)) {
                                 packageJSONOptions.eslintIgnore.forEach(pattern => {
-                                    addPattern(this.ig.custom, pattern);
-                                    addPattern(this.ig.default, pattern);
+                                    this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern);
+                                    this.addPatternRelativeToIgnoreFile(this.ig.default, pattern);
                                 });
                             } else {
                                 throw new TypeError("Package.json eslintIgnore property requires an array of paths");
@@ -202,12 +215,37 @@ class IgnoredPaths {
             }
 
             if (options.ignorePattern) {
-                addPattern(this.ig.custom, options.ignorePattern);
-                addPattern(this.ig.default, options.ignorePattern);
+                this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern);
+                this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern);
             }
         }
+    }
 
-        this.options = options;
+    /*
+     * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`.
+     * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`.
+     * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in
+     * ignore rules.
+     */
+
+    addPatternRelativeToCwd(ig, pattern) {
+        ig.addPattern(
+            this.getBaseDir() === this.options.cwd
+                ? pattern
+                : relativize(pattern, path.relative(this.ignoreFileDir, this.options.cwd))
+        );
+    }
+
+    addPatternRelativeToIgnoreFile(ig, pattern) {
+        ig.addPattern(
+            this.getBaseDir() === this.ignoreFileDir
+                ? pattern
+                : relativize(pattern, path.relative(this.options.cwd, this.ignoreFileDir))
+        );
+    }
+
+    getBaseDir() {
+        return this.ignoreFileDir.startsWith(this.options.cwd) ? this.options.cwd : this.ignoreFileDir;
     }
 
     /**
@@ -217,7 +255,7 @@ class IgnoredPaths {
      */
     readIgnoreFile(filePath) {
         if (typeof this.cache[filePath] === "undefined") {
-            this.cache[filePath] = fs.readFileSync(filePath, "utf8");
+            this.cache[filePath] = fs.readFileSync(filePath, "utf8").split("\n").filter(Boolean);
         }
         return this.cache[filePath];
     }
@@ -226,11 +264,13 @@ class IgnoredPaths {
      * add ignore file to node-ignore instance
      * @param {Object} ig, instance of node-ignore
      * @param {string} filePath, file to add to ig
-     * @returns {array} raw ignore rules
+     * @returns {void}
      */
     addIgnoreFile(ig, filePath) {
         ig.ignoreFiles.push(filePath);
-        return ig.add(this.readIgnoreFile(filePath));
+        this
+            .readIgnoreFile(filePath)
+            .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule));
     }
 
     /**
@@ -243,7 +283,7 @@ class IgnoredPaths {
 
         let result = false;
         const absolutePath = path.resolve(this.options.cwd, filepath);
-        const relativePath = pathUtil.getRelativePath(absolutePath, this.baseDir);
+        const relativePath = pathUtil.getRelativePath(absolutePath, this.getBaseDir());
 
         if (typeof category === "undefined") {
             result = (this.ig.default.filter([relativePath]).length === 0) ||
@@ -261,8 +301,10 @@ class IgnoredPaths {
      * @returns {function()} method to check whether a folder should be ignored by glob.
      */
     getIgnoredFoldersGlobChecker() {
+        const baseDir = this.getBaseDir();
+        const ig = ignore();
 
-        const ig = ignore().add(DEFAULT_IGNORE_DIRS);
+        DEFAULT_IGNORE_DIRS.forEach(ignoreDir => this.addPatternRelativeToCwd(ig, ignoreDir));
 
         if (this.options.dotfiles !== true) {
 
@@ -276,10 +318,8 @@ class IgnoredPaths {
 
         const filter = ig.createFilter();
 
-        const base = this.baseDir;
-
         return function(absolutePath) {
-            const relative = pathUtil.getRelativePath(absolutePath, base);
+            const relative = pathUtil.getRelativePath(absolutePath, baseDir);
 
             if (!relative) {
                 return false;
diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js
index b1208e19e..e5242ff5b 100644
--- a/lib/rules/key-spacing.js
+++ b/lib/rules/key-spacing.js
@@ -540,7 +540,7 @@ module.exports = {
             const length = properties.length,
                 widths = properties.map(getKeyWidth), // Width of keys, including quotes
                 align = alignmentOptions.on; // "value" or "colon"
-            let targetWidth = Math.max.apply(null, widths),
+            let targetWidth = Math.max(...widths),
                 beforeColon, afterColon, mode;
 
             if (alignmentOptions && length > 1) { // When aligning values within a group, use the alignment configuration.
diff --git a/lib/rules/no-unmodified-loop-condition.js b/lib/rules/no-unmodified-loop-condition.js
index 623144dc0..a02366bad 100644
--- a/lib/rules/no-unmodified-loop-condition.js
+++ b/lib/rules/no-unmodified-loop-condition.js
@@ -16,7 +16,6 @@ const Traverser = require("../util/traverser"),
 // Helpers
 //------------------------------------------------------------------------------
 
-const pushAll = Function.apply.bind(Array.prototype.push);
 const SENTINEL_PATTERN = /(?:(?:Call|Class|Function|Member|New|Yield)Expression|Statement|Declaration)$/;
 const LOOP_PATTERN = /^(?:DoWhile|For|While)Statement$/; // for-in/of statements don't have `test` property.
 const GROUP_PATTERN = /^(?:BinaryExpression|ConditionalExpression)$/;
@@ -356,7 +355,7 @@ module.exports = {
                 let scope;
 
                 while ((scope = queue.pop())) {
-                    pushAll(queue, scope.childScopes);
+                    queue.push(...scope.childScopes);
                     scope.variables.forEach(checkReferences);
                 }
 
diff --git a/lib/rules/no-useless-return.js b/lib/rules/no-useless-return.js
index d801c0e46..731ca4dff 100644
--- a/lib/rules/no-useless-return.js
+++ b/lib/rules/no-useless-return.js
@@ -15,15 +15,6 @@ const astUtils = require("../ast-utils"),
 // Helpers
 //------------------------------------------------------------------------------
 
-/**
- * Adds all elements of 2nd argument into 1st argument.
- *
- * @param {Array} array - The destination array to add.
- * @param {Array} elements - The source array to add.
- * @returns {void}
- */
-const pushAll = Function.apply.bind(Array.prototype.push);
-
 /**
  * Removes the given element from the array.
  *
@@ -137,7 +128,7 @@ module.exports = {
                     continue;
                 }
 
-                pushAll(uselessReturns, segmentInfoMap.get(segment).uselessReturns);
+                uselessReturns.push(...segmentInfoMap.get(segment).uselessReturns);
             }
 
             return uselessReturns;
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index a784e2e31..a70c98554 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -15,15 +15,6 @@ const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRest
 const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/;
 const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/;
 
-/**
- * Adds multiple items to the tail of an array.
- *
- * @param {any[]} array - A destination to add.
- * @param {any[]} values - Items to be added.
- * @returns {void}
- */
-const pushAll = Function.apply.bind(Array.prototype.push);
-
 /**
  * Checks whether a given node is located at `ForStatement.init` or not.
  *
@@ -364,7 +355,7 @@ module.exports = {
 
             VariableDeclaration(node) {
                 if (node.kind === "let" && !isInitOfForStatement(node)) {
-                    pushAll(variables, context.getDeclaredVariables(node));
+                    variables.push(...context.getDeclaredVariables(node));
                 }
             }
         };
diff --git a/lib/rules/prefer-object-spread.js b/lib/rules/prefer-object-spread.js
index 6ddb66540..3df16b7ae 100644
--- a/lib/rules/prefer-object-spread.js
+++ b/lib/rules/prefer-object-spread.js
@@ -180,8 +180,14 @@ function defineFixer(node, sourceCode) {
                 for (const innerParen of innerParens) {
                     yield fixer.remove(innerParen);
                 }
-                yield fixer.removeRange([left.range[0], getEndWithSpaces(left, sourceCode)]);
-                yield fixer.removeRange([getStartWithSpaces(right, sourceCode), right.range[1]]);
+                const leftRange = [left.range[0], getEndWithSpaces(left, sourceCode)];
+                const rightRange = [
+                    Math.max(getStartWithSpaces(right, sourceCode), leftRange[1]), // Ensure ranges don't overlap
+                    right.range[1]
+                ];
+
+                yield fixer.removeRange(leftRange);
+                yield fixer.removeRange(rightRange);
 
                 // Remove the comma of this argument if it's duplication.
                 if (
diff --git a/lib/util/node-event-generator.js b/lib/util/node-event-generator.js
index 9d477bbb4..1ae566431 100644
--- a/lib/util/node-event-generator.js
+++ b/lib/util/node-event-generator.js
@@ -45,8 +45,8 @@ function getPossibleTypes(parsedSelector) {
         case "matches": {
             const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
 
-            if (typesForComponents.every(typesForComponent => typesForComponent)) {
-                return lodash.union.apply(null, typesForComponents);
+            if (typesForComponents.every(Boolean)) {
+                return lodash.union(...typesForComponents);
             }
             return null;
         }
@@ -63,7 +63,7 @@ function getPossibleTypes(parsedSelector) {
              * If at least one of the components could only match a particular type, the compound could only match
              * the intersection of those types.
              */
-            return lodash.intersection.apply(null, typesForComponents);
+            return lodash.intersection(...typesForComponents);
         }
 
         case "child":


Test patch:
diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index a02f0b9be..d7e4c2bfa 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -140,7 +140,7 @@ const IT = Symbol("it");
  */
 function itDefaultHandler(text, method) {
     try {
-        return method.apply(this);
+        return method.call(this);
     } catch (err) {
         if (err instanceof assert.AssertionError) {
             err.message += ` (${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)})`;
@@ -157,7 +157,7 @@ function itDefaultHandler(text, method) {
  * @returns {any} Returned value of `method`.
  */
 function describeDefaultHandler(text, method) {
-    return method.apply(this);
+    return method.call(this);
 }
 
 class RuleTester {
diff --git a/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir b/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir
new file mode 100644
index 000000000..8c7a20132
--- /dev/null
+++ b/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir
@@ -0,0 +1,5 @@
+/undef.js
+foo.js
+*.txt
+!/bar.txt
+!baz.txt
diff --git a/tests/lib/ignored-paths.js b/tests/lib/ignored-paths.js
index 010488931..dfcf310e5 100644
--- a/tests/lib/ignored-paths.js
+++ b/tests/lib/ignored-paths.js
@@ -123,7 +123,7 @@ describe("IgnoredPaths", () => {
             const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() });
             const ignorePatterns = getIgnorePatterns(ignoredPaths);
 
-            assert.isNotNull(ignoredPaths.baseDir);
+            assert.isNotNull(ignoredPaths.ignoreFileDir);
             assert.deepStrictEqual(getIgnoreFiles(ignoredPaths), [expectedIgnoreFile]);
             assert.include(ignorePatterns, "sampleignorepattern");
         });
@@ -131,7 +131,7 @@ describe("IgnoredPaths", () => {
         it("should set baseDir to cwd when no ignore file was loaded", () => {
             const ignoredPaths = new IgnoredPaths({ cwd: getFixturePath("no-ignore-file") });
 
-            assert.strictEqual(ignoredPaths.baseDir, getFixturePath("no-ignore-file"));
+            assert.strictEqual(ignoredPaths.ignoreFileDir, getFixturePath("no-ignore-file"));
         });
 
         it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", () => {
@@ -241,7 +241,7 @@ describe("IgnoredPaths", () => {
         it("should set baseDir to directory containing ignorePath if provided", () => {
             const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() });
 
-            assert.strictEqual(ignoredPaths.baseDir, path.dirname(ignoreFilePath));
+            assert.strictEqual(ignoredPaths.ignoreFileDir, path.dirname(ignoreFilePath));
         });
 
     });
@@ -401,6 +401,40 @@ describe("IgnoredPaths", () => {
             assert.isFalse(ignoredPaths.contains(getFixturePath("subdir/undef.js")));
             assert.isTrue(ignoredPaths.contains(getFixturePath("undef.js")));
         });
+
+        it("should resolve relative paths from the ignorePath when it's in a child directory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/undef.js")));
+            assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js")));
+            assert.isTrue(ignoredPaths.contains(getFixturePath("foo.js")));
+            assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/foo.js")));
+
+            assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules/bar.js")));
+        });
+
+        it("should resolve relative paths from the ignorePath when it contains negated globs", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains("subdir/blah.txt"));
+            assert.isTrue(ignoredPaths.contains("blah.txt"));
+            assert.isFalse(ignoredPaths.contains("subdir/bar.txt"));
+            assert.isTrue(ignoredPaths.contains("bar.txt"));
+            assert.isFalse(ignoredPaths.contains("subdir/baz.txt"));
+            assert.isFalse(ignoredPaths.contains("baz.txt"));
+        });
+
+        it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains("node_modules/blah.js"));
+        });
+
+        it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignoreForDifferentCwd"), cwd: getFixturePath("subdir") });
+
+            assert.isTrue(ignoredPaths.contains("node_modules/blah.js"));
+        });
     });
 
     describe("initialization with ignorePath containing commented lines", () => {
diff --git a/tests/lib/rules/no-invalid-this.js b/tests/lib/rules/no-invalid-this.js
index a87a9aa44..f1d11030c 100644
--- a/tests/lib/rules/no-invalid-this.js
+++ b/tests/lib/rules/no-invalid-this.js
@@ -83,7 +83,7 @@ function extractPatterns(patterns, type) {
     }));
 
     // Flatten.
-    return Array.prototype.concat.apply([], patternsList);
+    return [].concat(...patternsList);
 }
 
 
diff --git a/tests/lib/rules/prefer-object-spread.js b/tests/lib/rules/prefer-object-spread.js
index bfe43e0f4..acac665e5 100644
--- a/tests/lib/rules/prefer-object-spread.js
+++ b/tests/lib/rules/prefer-object-spread.js
@@ -12,9 +12,6 @@ const RuleTester = require("../../../lib/testers/rule-tester");
 
 const parserOptions = {
     ecmaVersion: 2018,
-    ecmaFeatures: {
-        experimentalObjectRestSpread: true
-    },
     sourceType: "module"
 };
 
@@ -811,6 +808,32 @@ ruleTester.run("prefer-object-spread", rule, {
                     column: 29
                 }
             ]
+        },
+
+        // https://github.com/eslint/eslint/issues/10646
+        {
+            code: "Object.assign({ });",
+            output: "({});",
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 1,
+                    column: 1
+                }
+            ]
+        },
+        {
+            code: "Object.assign({\n});",
+            output: "({});",
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 1,
+                    column: 1
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 4265ab1b9..4a8608906 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -44,12 +44,12 @@ const ruleTesterTestEmitter = new EventEmitter();
 
 RuleTester.describe = function(text, method) {
     ruleTesterTestEmitter.emit("describe", text, method);
-    return method.apply(this);
+    return method.call(this);
 };
 
 RuleTester.it = function(text, method) {
     ruleTesterTestEmitter.emit("it", text, method);
-    return method.apply(this);
+    return method.call(this);
 };
 
 //------------------------------------------------------------------------------