Repository: eslint/eslint
Pull request #11476 (2019-03-05 01:08:32)

Fix: Empty glob pattern incorrectly expands to "/**"
<!--
    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.)
-->

**Tell us about your environment**

* **ESLint Version:** 5.15.0
* **Node Version:** 10.15.2
* **npm Version:** 6.4.1

**What parser (default, Babel-ESLint, etc.) are you using?**

Any; this bug occurs before ESLint parses anything.

**Please show your full configuration:**

This works without a configuration (e.g. `npx eslint ""` in any folder)

**What did you do? Please include the actual source code causing the issue.**

I passed an empty string to ESLint on the command line.

**What did you expect to happen?**

The empty string would be ignored.

**What actually happened? Please include the actual, raw output from ESLint.**

The empty string got expanded to `/**` and ESLint tried to lint my whole filesystem.

[See a live example on `repl.it`](https://repl.it/@bdchauvette/ESLint-Empty-glob-pattern) ([download as zip](https://repl.it/@bdchauvette/ESLint-Empty-glob-pattern.zip))

<!--
    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)**

In `lib/util/glob-utils.js`, empty strings are now filtered out before mapping over the glob patterns.


[See a live example on `repl.it`](https://repl.it/@bdchauvette/ESLint-Empty-glob-pattern-fix) ([download as zip](https://repl.it/@bdchauvette/ESLint-Empty-glob-pattern-fix.zip))

**Is there anything you'd like reviewers to focus on?**

Nope, thanks for all the hard work on ESLint! :bowing_man: 


bdchauvette: Thanks for the quick feedback! I tried to search for related issues on github, but didn't see anything. My issue-fu is weak :man_facepalming: 

I'll update the PR :+1: 

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 780368769..86cc48017 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,18 +1,13 @@
 "use strict";
 
-const path = require("path");
-const rulesDirPlugin = require("eslint-plugin-rulesdir");
-
-rulesDirPlugin.RULES_DIR = path.join(__dirname, "tools/internal-rules");
-
 module.exports = {
     root: true,
     plugins: [
         "eslint-plugin",
-        "rulesdir"
+        "internal-rules"
     ],
     extends: [
-        "./packages/eslint-config-eslint/default.yml",
+        "eslint",
         "plugin:eslint-plugin/recommended"
     ],
     rules: {
@@ -24,24 +19,30 @@ module.exports = {
         "eslint-plugin/require-meta-type": "error",
         "eslint-plugin/test-case-property-ordering": "error",
         "eslint-plugin/test-case-shorthand-strings": "error",
-        "rulesdir/multiline-comment-style": "error"
+        "internal-rules/multiline-comment-style": "error"
     },
     overrides: [
         {
             files: ["lib/rules/*", "tools/internal-rules/*"],
+            excludedFiles: ["tools/internal-rules/index.js"],
             rules: {
-                "rulesdir/no-invalid-meta": "error",
-                "rulesdir/consistent-docs-description": "error"
+                "internal-rules/no-invalid-meta": "error",
+                "internal-rules/consistent-docs-description": "error"
 
                 /*
                  * TODO: enable it when all the rules using meta.messages
-                 * "rulesdir/consistent-meta-messages": "error"
+                 * "internal-rules/consistent-meta-messages": "error"
                  */
             }
+        }, {
+            files: ["tools/internal-rules/*"],
+            rules: {
+                "node/no-unpublished-require": "off"
+            }
         }, {
             files: ["lib/rules/*"],
             rules: {
-                "rulesdir/consistent-docs-url": "error"
+                "internal-rules/consistent-docs-url": "error"
             }
         }, {
             files: ["tests/**/*"],
diff --git a/.travis.yml b/.travis.yml
index 22decd59f..baf4d28fd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ node_js:
     - "9"
     - "10"
     - "11"
-sudo: false
+
 branches:
     only:
     - master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e35e10bd..115a7c3c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+v5.15.1 - March 4, 2019
+
+* [`fe1a892`](https://github.com/eslint/eslint/commit/fe1a892f85b09c3d2fea05bef011530a678a6af5) Build: bundle espree (fixes eslint/eslint.github.io#546) (#11467) (薛定谔的猫)
+* [`458053b`](https://github.com/eslint/eslint/commit/458053b0b541f857bf233dacbde5ba80681820f8) Fix: avoid creating invalid regex in no-warning-comments (fixes #11471) (#11472) (Teddy Katz)
+
 v5.15.0 - March 1, 2019
 
 * [`4088c6c`](https://github.com/eslint/eslint/commit/4088c6c9d4578cd581ce8ff4385d90b58a75b755) Build: Remove path.resolve in webpack build (#11462) (Kevin Partington)
diff --git a/Makefile.js b/Makefile.js
index 003796383..bd6f46524 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -25,7 +25,8 @@ const lodash = require("lodash"),
     semver = require("semver"),
     ejs = require("ejs"),
     loadPerf = require("load-perf"),
-    yaml = require("js-yaml");
+    yaml = require("js-yaml"),
+    CLIEngine = require("./lib/cli-engine");
 
 const { cat, cd, cp, echo, exec, exit, find, ls, mkdir, pwd, rm, test } = require("shelljs");
 
@@ -440,8 +441,7 @@ function lintMarkdown(files) {
  * @returns {Object} Output from each formatter
  */
 function getFormatterResults() {
-    const CLIEngine = require("./lib/cli-engine"),
-        stripAnsi = require("strip-ansi");
+    const stripAnsi = require("strip-ansi");
 
     const formatterFiles = fs.readdirSync("./lib/formatters/"),
         cli = new CLIEngine({
@@ -580,6 +580,19 @@ target.test = function() {
 
     target.webpack();
 
+    const browserFileLintOutput = new CLIEngine({
+        useEslintrc: false,
+        ignore: false,
+        allowInlineConfig: false,
+        baseConfig: { parserOptions: { ecmaVersion: 5 } }
+    }).executeOnFiles([`${BUILD_DIR}/eslint.js`]);
+
+    if (browserFileLintOutput.errorCount > 0) {
+        echo(`error: Failed to lint ${BUILD_DIR}/eslint.js as ES5 code`);
+        echo(CLIEngine.getFormatter("stylish")(browserFileLintOutput.results));
+        errors++;
+    }
+
     lastReturn = exec(`${getBinFile("karma")} start karma.conf.js`);
     if (lastReturn.code !== 0) {
         errors++;
@@ -783,6 +796,7 @@ target.gensite = function(prereleaseVersion) {
         echo("> Updating the demos (Step 13)");
         target.webpack("production");
         cp("-f", "build/eslint.js", `${SITE_DIR}js/app/eslint.js`);
+        cp("-f", "build/espree.js", `${SITE_DIR}js/app/espree.js`);
     } else {
         echo("> Skipped updating the demos (Step 13)");
     }
diff --git a/README.md b/README.md
index 8ac9fbe4d..437794dc6 100644
--- a/README.md
+++ b/README.md
@@ -268,7 +268,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <h3>Gold Sponsors</h3>
 <p><a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F68ed8b70-ebf2-11e6-9958-cb7e79408c56.png&height=96" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a></p>
+<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a></p>
 <!--sponsorsend-->
 
 ## Technology Sponsors
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index 7b9ecb6ec..3d41f25b9 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -677,6 +677,7 @@ Retrieves a formatter, which you can then use to format a report object. The arg
 * "[table](../user-guide/formatters#table)"
 * "[tap](../user-guide/formatters#tap)"
 * "[unix](../user-guide/formatters#unix)"
+* "[visualstudio](../user-guide/formatters#visualstudio)"
 
 or the full path to a JavaScript file containing a custom formatter. You can also omit the argument to retrieve the default formatter.
 
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index 38c2a8319..d189d53ad 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -472,7 +472,7 @@ module.exports = {
 
 In the preceding example, the error level is assumed to be the first argument. It is followed by the first optional argument, a string which may be either `"always"` or `"never"`. The final optional argument is an object, which may have a Boolean property named `exceptRange`.
 
-To learn more about JSON Schema, we recommend looking at some [examples](http://json-schema.org/examples.html) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook).
+To learn more about JSON Schema, we recommend looking at some examples in [website](http://json-schema.org/learn/) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook).
 
 **Note:** Currently you need to use full JSON Schema object rather than array in case your schema has references ($ref), because in case of array format ESLint transforms this array into a single schema without updating references that makes them incorrect (they are ignored).
 
diff --git a/docs/maintainer-guide/README.md b/docs/maintainer-guide/README.md
index 19c1fb9ee..941ad5599 100644
--- a/docs/maintainer-guide/README.md
+++ b/docs/maintainer-guide/README.md
@@ -17,3 +17,7 @@ Describes how to do an ESLint project release.
 ## [Governance](governance.md)
 
 Describes the governance policy for ESLint, including the rights and privileges of individuals inside the project.
+
+## [Working Groups](working-groups.md)
+
+Describes how working groups are created and how they function within the ESLint project.
diff --git a/docs/maintainer-guide/working-groups.md b/docs/maintainer-guide/working-groups.md
new file mode 100644
index 000000000..d6d22fb6c
--- /dev/null
+++ b/docs/maintainer-guide/working-groups.md
@@ -0,0 +1,22 @@
+# Working Groups
+
+The ESLint TSC may form working groups to focus on a specific area of the project.
+
+## Creating a Working Group
+
+Working groups are created by sending an email to the team mailing list. Each working group:
+
+1. Must have a GitHub team under the "ESLint Team" top-level GitHub team. (The GitHub team name should end with "WG" to distinguish it from other types of teams.)
+1. Must have at least one TSC member.
+1. May have any number of Committers and Reviewers.
+1. Must have at least two members.
+
+Active working groups are listed on the [team page](https://eslint.org/team).
+
+## How Working Groups Work
+
+Each working group is responsible for its own inner working. Working groups can decide how large or small they should be (so long as there is at least two members), when and who to add or remove from the working group, and how to accomplish their objectives.
+
+Working groups may be temporary or permanent.
+
+If working groups intend to make a significant change to the ESLint project, the proposal must still be approved by the TSC.
diff --git a/docs/rules/func-name-matching.md b/docs/rules/func-name-matching.md
index 870510290..53be2a099 100644
--- a/docs/rules/func-name-matching.md
+++ b/docs/rules/func-name-matching.md
@@ -91,7 +91,7 @@ This rule takes an optional string of "always" or "never" (when omitted, it defa
 
 ### considerPropertyDescriptor
 
-A boolean value that defaults to `false`. If `considerPropertyDescriptor` is set to true, the check will take into account the use `Object.create`, `Object.defineProperty`, `Object.defineProperties`, and `Reflect.defineProperty`.
+A boolean value that defaults to `false`. If `considerPropertyDescriptor` is set to true, the check will take into account the use of `Object.create`, `Object.defineProperty`, `Object.defineProperties`, and `Reflect.defineProperty`.
 
 Examples of **correct** code for the `{ considerPropertyDescriptor: true }` option:
 
diff --git a/docs/rules/no-restricted-modules.md b/docs/rules/no-restricted-modules.md
index 78ce092ba..7f9c926a1 100644
--- a/docs/rules/no-restricted-modules.md
+++ b/docs/rules/no-restricted-modules.md
@@ -35,10 +35,10 @@ It can also take an object with lists of `paths` and gitignore-style `patterns`
 You may also specify a custom message for any paths you want to restrict as follows:
 
 ```json
-"no-restricted-modules": ["error", [{
+"no-restricted-modules": ["error", {
   "name": "foo-module",
   "message": "Please use bar-module instead."
-  }]
+  }
 ]
 ```
 
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 87906837c..ec91ed774 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -23,7 +23,7 @@ Please note that supporting JSX syntax is not the same as supporting React. Reac
 By the same token, supporting ES6 syntax is not the same as supporting new ES6 globals (e.g., new types such as
 `Set`).
 For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 global variables, use `{ "env":
-{ "es6": true } }`. `{ env: { es6: true } }` enables ES6 syntax automatically, but `{ parserOptions: { ecmaVersion: 6 } }` does not enable ES6 globals automatically.
+{ "es6": true } }`. `{ "env": { "es6": true } }` enables ES6 syntax automatically, but `{ "parserOptions": { "ecmaVersion": 6 } }` does not enable ES6 globals automatically.
 Parser options are set in your `.eslintrc.*` file by using the `parserOptions` property. The available options are:
 
 * `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, or 10 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), or 2019 (same as 10) to use the year-based naming.
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 492800b7a..80344f509 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -541,7 +541,7 @@ function loadFromDisk(resolvedPath, configContext) {
         const ruleMap = configContext.linterContext.getRules();
 
         // validate the configuration before continuing
-        validator.validate(config, resolvedPath.configFullName, ruleMap.get.bind(ruleMap), configContext.linterContext.environments);
+        validator.validate(config, ruleMap.get.bind(ruleMap), configContext.linterContext.environments, resolvedPath.configFullName);
 
         /*
          * If an `extends` property is defined, it represents a configuration file to use as
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 2345b2889..386db7775 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -116,7 +116,7 @@ function validateRuleSchema(rule, localOptions) {
  * no source is prepended to the message.
  * @returns {void}
  */
-function validateRuleOptions(rule, ruleId, options, source) {
+function validateRuleOptions(rule, ruleId, options, source = null) {
     if (!rule) {
         return;
     }
@@ -140,11 +140,11 @@ function validateRuleOptions(rule, ruleId, options, source) {
 /**
  * Validates an environment object
  * @param {Object} environment The environment config object to validate.
- * @param {string} source The name of the configuration source to report in any errors.
  * @param {Environments} envContext Env context
+ * @param {string} source The name of the configuration source to report in any errors.
  * @returns {void}
  */
-function validateEnvironment(environment, source, envContext) {
+function validateEnvironment(environment, envContext, source = null) {
 
     // not having an environment is ok
     if (!environment) {
@@ -163,11 +163,11 @@ function validateEnvironment(environment, source, envContext) {
 /**
  * Validates a rules config object
  * @param {Object} rulesConfig The rules config object to validate.
- * @param {string} source The name of the configuration source to report in any errors.
  * @param {function(string): {create: Function}} ruleMapper A mapper function from strings to loaded rules
+ * @param {string} source The name of the configuration source to report in any errors.
  * @returns {void}
  */
-function validateRules(rulesConfig, source, ruleMapper) {
+function validateRules(rulesConfig, ruleMapper, source = null) {
     if (!rulesConfig) {
         return;
     }
@@ -228,7 +228,7 @@ const emitDeprecationWarning = lodash.memoize((source, errorCode) => {
  * @param {string} source The name of the configuration source to report in any errors.
  * @returns {void}
  */
-function validateConfigSchema(config, source) {
+function validateConfigSchema(config, source = null) {
     validateSchema = validateSchema || ajv.compile(configSchema);
 
     if (!validateSchema(config)) {
@@ -252,19 +252,19 @@ function validateConfigSchema(config, source) {
 /**
  * Validates an entire config object.
  * @param {Object} config The config object to validate.
- * @param {string} source The name of the configuration source to report in any errors.
  * @param {function(string): {create: Function}} ruleMapper A mapper function from rule IDs to defined rules
  * @param {Environments} envContext The env context
+ * @param {string} source The name of the configuration source to report in any errors.
  * @returns {void}
  */
-function validate(config, source, ruleMapper, envContext) {
+function validate(config, ruleMapper, envContext, source = null) {
     validateConfigSchema(config, source);
-    validateRules(config.rules, source, ruleMapper);
-    validateEnvironment(config.env, source, envContext);
+    validateRules(config.rules, ruleMapper, source);
+    validateEnvironment(config.env, envContext, source);
 
     for (const override of config.overrides || []) {
-        validateRules(override.rules, source, ruleMapper);
-        validateEnvironment(override.env, source, envContext);
+        validateRules(override.rules, ruleMapper, source);
+        validateEnvironment(override.env, envContext, source);
     }
 }
 
diff --git a/lib/linter.js b/lib/linter.js
index 88448d90f..be38b9908 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -747,10 +747,15 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
     nodeQueue.forEach(traversalInfo => {
         currentNode = traversalInfo.node;
 
-        if (traversalInfo.isEntering) {
-            eventGenerator.enterNode(currentNode);
-        } else {
-            eventGenerator.leaveNode(currentNode);
+        try {
+            if (traversalInfo.isEntering) {
+                eventGenerator.enterNode(currentNode);
+            } else {
+                eventGenerator.leaveNode(currentNode);
+            }
+        } catch (err) {
+            err.currentNode = currentNode;
+            throw err;
         }
     });
 
@@ -901,8 +906,15 @@ module.exports = class Linter {
                 options.filename
             );
         } catch (err) {
+            err.message += `\nOccurred while linting ${options.filename}`;
             debug("An error occurred while traversing");
             debug("Filename:", options.filename);
+            if (err.currentNode) {
+                const { line } = err.currentNode.loc.start;
+
+                debug("Line:", line);
+                err.message += `:${line}`;
+            }
             debug("Parser Options:", parserOptions);
             debug("Parser Path:", parserName);
             debug("Settings:", settings);
diff --git a/lib/rules/no-warning-comments.js b/lib/rules/no-warning-comments.js
index 0dd0a26c1..1d1b55460 100644
--- a/lib/rules/no-warning-comments.js
+++ b/lib/rules/no-warning-comments.js
@@ -95,7 +95,7 @@ module.exports = {
                  * ^\s*TERM\b.  This checks the word boundary
                  * at the beginning of the comment.
                  */
-                return new RegExp(prefix + escaped + suffix, "iu");
+                return new RegExp(prefix + escaped + suffix, "i"); // eslint-disable-line require-unicode-regexp
             }
 
             /*
@@ -103,7 +103,7 @@ module.exports = {
              * \bTERM\b|\bTERM\b, this checks the entire comment
              * for the term.
              */
-            return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "iu");
+            return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i"); // eslint-disable-line require-unicode-regexp
         }
 
         const warningRegExps = warningTerms.map(convertToRegExp);
diff --git a/lib/util/glob-utils.js b/lib/util/glob-utils.js
index b05c35443..33cb8e7c8 100644
--- a/lib/util/glob-utils.js
+++ b/lib/util/glob-utils.js
@@ -70,6 +70,10 @@ function processPath(options) {
      * @private
      */
     return function(pathname) {
+        if (pathname === "") {
+            return "";
+        }
+
         let newPath = pathname;
         const resolvedPath = path.resolve(cwd, pathname);
 
@@ -201,6 +205,13 @@ function listFilesToProcess(globPatterns, providedOptions) {
 
     debug("Creating list of files to process.");
     const resolvedPathsByGlobPattern = resolvedGlobPatterns.map(pattern => {
+        if (pattern === "") {
+            return [{
+                filename: "",
+                behavior: SILENTLY_IGNORE
+            }];
+        }
+
         const file = path.resolve(cwd, pattern);
 
         if (options.globInputPaths === false || (fs.existsSync(file) && fs.statSync(file).isFile())) {
@@ -240,7 +251,7 @@ function listFilesToProcess(globPatterns, providedOptions) {
     });
 
     const allPathDescriptors = resolvedPathsByGlobPattern.reduce((pathsForAllGlobs, pathsForCurrentGlob, index) => {
-        if (pathsForCurrentGlob.every(pathDescriptor => pathDescriptor.behavior === SILENTLY_IGNORE)) {
+        if (pathsForCurrentGlob.every(pathDescriptor => pathDescriptor.behavior === SILENTLY_IGNORE && pathDescriptor.filename !== "")) {
             throw new (pathsForCurrentGlob.length ? AllFilesIgnoredError : NoFilesFoundError)(globPatterns[index]);
         }
 
diff --git a/package.json b/package.json
index 64ef8a683..2d9f0d0be 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "5.15.0",
+  "version": "5.15.1",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {
@@ -85,11 +85,11 @@
     "coveralls": "^3.0.1",
     "dateformat": "^3.0.3",
     "ejs": "^2.6.1",
+    "eslint-config-eslint": "file:packages/eslint-config-eslint",
     "eslint-plugin-eslint-plugin": "^2.0.1",
+    "eslint-plugin-internal-rules": "file:tools/internal-rules",
     "eslint-plugin-node": "^8.0.0",
-    "eslint-plugin-rulesdir": "^0.1.0",
     "eslint-release": "^1.2.0",
-    "eslint-rule-composer": "^0.3.0",
     "eslump": "^2.0.0",
     "esprima": "^4.0.1",
     "istanbul": "^0.4.5",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index 0cfe3506b..cad812907 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -13,7 +13,7 @@ rules:
     camelcase: "error"
     callback-return: ["error", ["cb", "callback", "next"]]
     class-methods-use-this: "error"
-    comma-dangle: "error"
+    comma-dangle: ["error", { functions: "never" }]
     comma-spacing: "error"
     comma-style: ["error", "last"]
     computed-property-spacing: "error"
diff --git a/tools/internal-rules/index.js b/tools/internal-rules/index.js
new file mode 100644
index 000000000..a3a3cd199
--- /dev/null
+++ b/tools/internal-rules/index.js
@@ -0,0 +1,13 @@
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+
+module.exports = {
+    rules: Object.assign(
+        {},
+        ...fs.readdirSync(__dirname)
+            .filter(filename => filename.endsWith(".js") && filename !== "index.js")
+            .map(filename => ({ [filename.replace(/\.js$/u, "")]: require(path.resolve(__dirname, filename)) }))
+    )
+};
diff --git a/tools/internal-rules/multiline-comment-style.js b/tools/internal-rules/multiline-comment-style.js
index f0d5755e8..b0a0c9d85 100644
--- a/tools/internal-rules/multiline-comment-style.js
+++ b/tools/internal-rules/multiline-comment-style.js
@@ -13,7 +13,7 @@ const multilineCommentStyle = require("../../lib/rules/multiline-comment-style")
 //------------------------------------------------------------------------------
 
 // The `no-invalid-meta` internal rule has a false positive here.
-// eslint-disable-next-line rulesdir/no-invalid-meta
+// eslint-disable-next-line internal-rules/no-invalid-meta
 module.exports = ruleComposer.filterReports(
     multilineCommentStyle,
     (problem, metadata) => {
diff --git a/tools/internal-rules/package.json b/tools/internal-rules/package.json
new file mode 100644
index 000000000..0d80ed277
--- /dev/null
+++ b/tools/internal-rules/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "eslint-plugin-internal-rules",
+  "version": "0.0.0",
+  "private": true,
+  "dependencies": {
+    "eslint-rule-composer": "^0.3.0"
+  }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 134a657e7..1a6a2986a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -2,22 +2,25 @@
 
 module.exports = {
     mode: "none",
-    entry: ["@babel/polyfill", "./lib/linter.js"],
+    entry: {
+        eslint: ["@babel/polyfill", "./lib/linter.js"],
+        espree: ["@babel/polyfill", "espree"]
+    },
     output: {
-        filename: "eslint.js",
-        library: "eslint",
+        filename: "[name].js",
+        library: "[name]",
         libraryTarget: "umd",
         globalObject: "this"
     },
     module: {
         rules: [
             {
-                test: /\.js$/u,
+                test: /\.m?js$/u,
                 loader: "babel-loader",
                 options: {
                     presets: ["@babel/preset-env"]
                 },
-                exclude: /node_modules/u
+                exclude: /node_modules\/lodash/u
             }
         ]
     },


Test patch:
diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index 6d1bba989..f1d915531 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -379,7 +379,7 @@ class RuleTester {
                 }
             }
 
-            validator.validate(config, "rule-tester", ruleMap.get.bind(ruleMap), new Environments());
+            validator.validate(config, ruleMap.get.bind(ruleMap), new Environments(), "rule-tester");
 
             return {
                 messages: linter.verify(code, config, filename, true),
diff --git a/tests/lib/code-path-analysis/code-path.js b/tests/lib/code-path-analysis/code-path.js
index 6b90b57bf..be1039fb4 100644
--- a/tests/lib/code-path-analysis/code-path.js
+++ b/tests/lib/code-path-analysis/code-path.js
@@ -66,7 +66,7 @@ function getOrderOfTraversing(codePath, options, callback) {
 describe("CodePathAnalyzer", () => {
     describe(".traverseSegments()", () => {
         describe("should traverse segments from the first to the end:", () => {
-            /* eslint-disable rulesdir/multiline-comment-style */
+            /* eslint-disable internal-rules/multiline-comment-style */
             it("simple", () => {
                 const codePath = parseCodePaths("foo(); bar(); baz();")[0];
                 const order = getOrderOfTraversing(codePath);
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index 721533c10..9d4c39baf 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -108,7 +108,7 @@ describe("Validator", () => {
     describe("validate", () => {
 
         it("should do nothing with an empty config", () => {
-            validator.validate({}, "tests", ruleMapper, linter.environments);
+            validator.validate({}, ruleMapper, linter.environments, "tests");
         });
 
         it("should do nothing with a valid eslint config", () => {
@@ -124,9 +124,9 @@ describe("Validator", () => {
                     parserOptions: { foo: "bar" },
                     rules: {}
                 },
-                "tests",
                 ruleMapper,
-                linter.environments
+                linter.environments,
+                "tests"
             );
         });
 
@@ -136,9 +136,9 @@ describe("Validator", () => {
                 {
                     foo: true
                 },
-                "tests",
                 ruleMapper,
-                linter.environments
+                linter.environments,
+                "tests"
             );
 
             assert.throws(fn, "Unexpected top-level property \"foo\".");
@@ -146,13 +146,13 @@ describe("Validator", () => {
 
         describe("root", () => {
             it("should throw with a string value", () => {
-                const fn = validator.validate.bind(null, { root: "true" }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { root: "true" }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `\"true\"`).");
             });
 
             it("should throw with a numeric value", () => {
-                const fn = validator.validate.bind(null, { root: 0 }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { root: 0 }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `0`).");
             });
@@ -160,13 +160,13 @@ describe("Validator", () => {
 
         describe("globals", () => {
             it("should throw with a string value", () => {
-                const fn = validator.validate.bind(null, { globals: "jQuery" }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { globals: "jQuery" }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `\"jQuery\"`).");
             });
 
             it("should throw with an array value", () => {
-                const fn = validator.validate.bind(null, { globals: ["jQuery"] }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { globals: ["jQuery"] }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `[\"jQuery\"]`).");
             });
@@ -174,32 +174,32 @@ describe("Validator", () => {
 
         describe("parser", () => {
             it("should not throw with a null value", () => {
-                validator.validate({ parser: null }, null, ruleMapper, linter.environments);
+                validator.validate({ parser: null }, ruleMapper, linter.environments, null);
             });
         });
 
         describe("env", () => {
 
             it("should throw with an array environment", () => {
-                const fn = validator.validate.bind(null, { env: [] }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { env: [] }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `[]`).");
             });
 
             it("should throw with a primitive environment", () => {
-                const fn = validator.validate.bind(null, { env: 1 }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { env: 1 }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `1`).");
             });
 
             it("should catch invalid environments", () => {
-                const fn = validator.validate.bind(null, { env: { browser: true, invalid: true } }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { env: { browser: true, invalid: true } }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Environment key \"invalid\" is unknown\n");
             });
 
             it("should catch disabled invalid environments", () => {
-                const fn = validator.validate.bind(null, { env: { browser: true, invalid: false } }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { env: { browser: true, invalid: false } }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Environment key \"invalid\" is unknown\n");
             });
@@ -212,11 +212,11 @@ describe("Validator", () => {
 
         describe("plugins", () => {
             it("should not throw with an empty array", () => {
-                validator.validate({ plugins: [] }, null, ruleMapper, linter.environments);
+                validator.validate({ plugins: [] }, ruleMapper, linter.environments, null);
             });
 
             it("should throw with a string", () => {
-                const fn = validator.validate.bind(null, { plugins: "react" }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { plugins: "react" }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"plugins\" is the wrong type (expected array but got `\"react\"`).");
             });
@@ -224,11 +224,11 @@ describe("Validator", () => {
 
         describe("settings", () => {
             it("should not throw with an empty object", () => {
-                validator.validate({ settings: {} }, null, ruleMapper, linter.environments);
+                validator.validate({ settings: {} }, ruleMapper, linter.environments, null);
             });
 
             it("should throw with an array", () => {
-                const fn = validator.validate.bind(null, { settings: ["foo"] }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { settings: ["foo"] }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"settings\" is the wrong type (expected object but got `[\"foo\"]`).");
             });
@@ -236,15 +236,15 @@ describe("Validator", () => {
 
         describe("extends", () => {
             it("should not throw with an empty array", () => {
-                validator.validate({ extends: [] }, null, ruleMapper, linter.environments);
+                validator.validate({ extends: [] }, ruleMapper, linter.environments, null);
             });
 
             it("should not throw with a string", () => {
-                validator.validate({ extends: "react" }, null, ruleMapper, linter.environments);
+                validator.validate({ extends: "react" }, ruleMapper, linter.environments, null);
             });
 
             it("should throw with an object", () => {
-                const fn = validator.validate.bind(null, { extends: {} }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { extends: {} }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"extends\" is the wrong type (expected string/array but got `{}`).");
             });
@@ -252,11 +252,11 @@ describe("Validator", () => {
 
         describe("parserOptions", () => {
             it("should not throw with an empty object", () => {
-                validator.validate({ parserOptions: {} }, null, ruleMapper, linter.environments);
+                validator.validate({ parserOptions: {} }, ruleMapper, linter.environments, null);
             });
 
             it("should throw with an array", () => {
-                const fn = validator.validate.bind(null, { parserOptions: ["foo"] }, null, ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { parserOptions: ["foo"] }, ruleMapper, linter.environments, null);
 
                 assert.throws(fn, "Property \"parserOptions\" is the wrong type (expected object but got `[\"foo\"]`).");
             });
@@ -265,47 +265,47 @@ describe("Validator", () => {
         describe("rules", () => {
 
             it("should do nothing with an empty rules object", () => {
-                validator.validate({ rules: {} }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: {} }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config with rules", () => {
-                validator.validate({ rules: { "mock-rule": [2, "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": [2, "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is off", () => {
-                validator.validate({ rules: { "mock-rule": ["off", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["off", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with an invalid config when severity is off", () => {
-                validator.validate({ rules: { "mock-required-options-rule": "off" } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-required-options-rule": "off" } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with an invalid config when severity is an array with 'off'", () => {
-                validator.validate({ rules: { "mock-required-options-rule": ["off"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-required-options-rule": ["off"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is warn", () => {
-                validator.validate({ rules: { "mock-rule": ["warn", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["warn", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is error", () => {
-                validator.validate({ rules: { "mock-rule": ["error", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["error", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is Off", () => {
-                validator.validate({ rules: { "mock-rule": ["Off", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["Off", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is Warn", () => {
-                validator.validate({ rules: { "mock-rule": ["Warn", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["Warn", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should do nothing with a valid config when severity is Error", () => {
-                validator.validate({ rules: { "mock-rule": ["Error", "second"] } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-rule": ["Error", "second"] } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should catch invalid rule options", () => {
-                const fn = validator.validate.bind(null, { rules: { "mock-rule": [3, "third"] } }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { rules: { "mock-rule": [3, "third"] } }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
             });
@@ -313,13 +313,13 @@ describe("Validator", () => {
             it("should allow for rules with no options", () => {
                 linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
 
-                validator.validate({ rules: { "mock-no-options-rule": 2 } }, "tests", ruleMapper, linter.environments);
+                validator.validate({ rules: { "mock-no-options-rule": 2 } }, ruleMapper, linter.environments, "tests");
             });
 
             it("should not allow options for rules with no options", () => {
                 linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
 
-                const fn = validator.validate.bind(null, { rules: { "mock-no-options-rule": [2, "extra"] } }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { rules: { "mock-no-options-rule": [2, "extra"] } }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n");
             });
@@ -327,39 +327,39 @@ describe("Validator", () => {
 
         describe("overrides", () => {
             it("should not throw with an empty overrides array", () => {
-                validator.validate({ overrides: [] }, "tests", ruleMapper, linter.environments);
+                validator.validate({ overrides: [] }, ruleMapper, linter.environments, "tests");
             });
 
             it("should not throw with a valid overrides array", () => {
-                validator.validate({ overrides: [{ files: "*", rules: {} }] }, "tests", ruleMapper, linter.environments);
+                validator.validate({ overrides: [{ files: "*", rules: {} }] }, ruleMapper, linter.environments, "tests");
             });
 
             it("should throw if override does not specify files", () => {
-                const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
             });
 
             it("should throw if override has an empty files array", () => {
-                const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have fewer than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
             });
 
             it("should throw if override has nested overrides", () => {
-                const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n");
             });
 
             it("should throw if override extends", () => {
-                const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n");
             });
 
             it("should throw if override tries to set root", () => {
-                const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, "tests", ruleMapper, linter.environments);
+                const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, ruleMapper, linter.environments, "tests");
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
             });
@@ -367,13 +367,13 @@ describe("Validator", () => {
             describe("env", () => {
 
                 it("should catch invalid environments", () => {
-                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, null, ruleMapper, linter.environments);
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, ruleMapper, linter.environments, null);
 
                     assert.throws(fn, "Environment key \"invalid\" is unknown\n");
                 });
 
                 it("should catch disabled invalid environments", () => {
-                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, null, ruleMapper, linter.environments);
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, ruleMapper, linter.environments, null);
 
                     assert.throws(fn, "Environment key \"invalid\" is unknown\n");
                 });
@@ -383,7 +383,7 @@ describe("Validator", () => {
             describe("rules", () => {
 
                 it("should catch invalid rule options", () => {
-                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, "tests", ruleMapper, linter.environments);
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, ruleMapper, linter.environments, "tests");
 
                     assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
                 });
@@ -391,7 +391,7 @@ describe("Validator", () => {
                 it("should not allow options for rules with no options", () => {
                     linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
 
-                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, "tests", ruleMapper, linter.environments);
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, ruleMapper, linter.environments, "tests");
 
                     assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n");
                 });
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index 3771ceb26..e31b61d95 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -99,7 +99,7 @@ describe("Linter", () => {
 
             assert.throws(() => {
                 linter.verify(code, config, filename);
-            }, "Intentional error.");
+            }, `Intentional error.\nOccurred while linting ${filename}:1`);
         });
 
         it("does not call rule listeners with a `this` value", () => {
@@ -4381,7 +4381,7 @@ describe("Linter", () => {
 
             assert.throws(() => {
                 linter.verify("0", { rules: { "test-rule": "error" } });
-            }, /Fixable rules should export a `meta\.fixable` property.$/u);
+            }, /Fixable rules should export a `meta\.fixable` property.\nOccurred while linting <input>:1$/u);
         });
 
         it("should not throw an error if fix is passed and there is no metadata", () => {
diff --git a/tests/lib/rules/no-warning-comments.js b/tests/lib/rules/no-warning-comments.js
index 3484d0a45..8a9b2c5e2 100644
--- a/tests/lib/rules/no-warning-comments.js
+++ b/tests/lib/rules/no-warning-comments.js
@@ -36,7 +36,8 @@ ruleTester.run("no-warning-comments", rule, {
         { code: "// comments containing terms as substrings like TodoMVC", options: [{ terms: ["todo"], location: "anywhere" }] },
         { code: "// special regex characters don't cause problems", options: [{ terms: ["[aeiou]"], location: "anywhere" }] },
         "/*eslint no-warning-comments: [2, { \"terms\": [\"todo\", \"fixme\", \"any other term\"], \"location\": \"anywhere\" }]*/\n\nvar x = 10;\n",
-        { code: "/*eslint no-warning-comments: [2, { \"terms\": [\"todo\", \"fixme\", \"any other term\"], \"location\": \"anywhere\" }]*/\n\nvar x = 10;\n", options: [{ location: "anywhere" }] }
+        { code: "/*eslint no-warning-comments: [2, { \"terms\": [\"todo\", \"fixme\", \"any other term\"], \"location\": \"anywhere\" }]*/\n\nvar x = 10;\n", options: [{ location: "anywhere" }] },
+        { code: "foo", options: [{ terms: ["foo-bar"] }] }
     ],
     invalid: [
         { code: "// fixme", errors: [{ message: "Unexpected 'fixme' comment." }] },
diff --git a/tests/lib/util/glob-utils.js b/tests/lib/util/glob-utils.js
index 6b61f8b40..9bafcfa07 100644
--- a/tests/lib/util/glob-utils.js
+++ b/tests/lib/util/glob-utils.js
@@ -329,6 +329,13 @@ describe("globUtils", () => {
             }, `No files matching '${patterns[0]}' were found.`);
         });
 
+        it("should ignore empty patterns", () => {
+            const patterns = [""];
+            const result = globUtils.listFilesToProcess(patterns);
+
+            assert.deepStrictEqual(result, []);
+        });
+
         it("should return an ignored file, if ignore option is turned off", () => {
             const options = { ignore: false };
             const patterns = [getFixturePath("glob-util", "ignored", "**/*.js")];