Repository: eslint/eslint
Pull request #12230 (2019-09-06 00:03:21)

Fix: use async reading of stdin in bin/eslint.js
closes eslint/eslint#12212

<!--
    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.)
-->

See https://github.com/eslint/eslint/issues/12212

<!--
    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)**

Applied nodejs recommendations for processing stdin as per https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin

**Is there anything you'd like reviewers to focus on?**

Compatibility with other operating systems.
I've only tested this on Windows.

mysticatea: Thank you for your contribution.

Would you add a test for this?
CI has multiplatform tests, it's great if we run tests on there.
baerrach: I can, it'll take me a day or so to clear my work to find the time for this.

I'll have a look in the test cases, but all I need is a test that uses a large file (anything over 4k in size).

Does anyone know which test I can look at off the top of their heads?

jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=12230) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you all sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint?pullRequest=12230) before we can accept your contribution.<br/>**1** out of **2** committers have signed the CLA.<br/><br/>:white_check_mark: baerrach<br/>:x: Barrie Treloar<hr/>**Barrie Treloar** seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please [add the email address used for this commit to your account](https://help.github.com/articles/why-are-my-commits-linked-to-the-wrong-user/#commits-are-not-linked-to-any-user).
mysticatea: Ah, @jsf-clabot seems to report a problem. Would you check the e-mail address of your commits?
baerrach: @mysticatea that is because I pushed my changes from my work machine with a work email.

I then fixed that to my gmail account and force pushed to remove that work email.

Its now saying all checks passed, including licece/cla.


Patch:
diff --git a/Makefile.js b/Makefile.js
index a01d13bde..d11ca75a0 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -473,12 +473,12 @@ target.all = function() {
     target.test();
 };
 
-target.lint = function() {
+target.lint = function([fix = false] = []) {
     let errors = 0,
         lastReturn;
 
     echo("Validating JavaScript files");
-    lastReturn = exec(`${ESLINT} .`);
+    lastReturn = exec(`${ESLINT}${fix ? "--fix" : ""} .`);
     if (lastReturn.code !== 0) {
         errors++;
     }
diff --git a/README.md b/README.md
index 969547200..c553cc810 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
 
 ### What ECMAScript versions does ESLint support?
 
-ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, and 2018. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
+ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, and 2019. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
 
 ### What about experimental features?
 
diff --git a/bin/eslint.js b/bin/eslint.js
index 82bcc1e03..a9f51f1d7 100755
--- a/bin/eslint.js
+++ b/bin/eslint.js
@@ -59,13 +59,40 @@ process.once("uncaughtException", err => {
 if (useStdIn) {
 
     /*
-     * Note: `process.stdin.fd` is not used here due to https://github.com/nodejs/node/issues/7439.
-     * Accessing the `process.stdin` property seems to modify the behavior of file descriptor 0, resulting
-     * in an error when stdin is piped in asynchronously.
+     * Note: See
+     * - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin
+     * - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io
+     * - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html
+     * - https://github.com/nodejs/node/issues/7439 (historical)
+     *
+     * On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems
+     * to read 4096 bytes before blocking and never drains to read further data.
+     *
+     * The investigation on the Emacs thread indicates:
+     *
+     * > Emacs on MS-Windows uses pipes to communicate with subprocesses; a
+     * > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than
+     * > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for
+     * > the subprocess to read its end of the pipe, at which time Emacs will
+     * > write the rest of the stuff.
+     *
+     * Using the nodejs code example for reading from stdin.
      */
-    const STDIN_FILE_DESCRIPTOR = 0;
+    let contents = "",
+        chunk = "";
 
-    process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8"));
+    process.stdin.setEncoding("utf8");
+    process.stdin.on("readable", () => {
+
+        // Use a loop to make sure we read all available data.
+        while ((chunk = process.stdin.read()) !== null) {
+            contents += chunk;
+        }
+    });
+
+    process.stdin.on("end", () => {
+        process.exitCode = cli.execute(process.argv, contents, "utf8");
+    });
 } else if (init) {
     const configInit = require("../lib/init/config-initializer");
 
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 8f5fd286d..172e79736 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -138,6 +138,8 @@ An environment defines global variables that are predefined. The available envir
 * `commonjs` - CommonJS global variables and CommonJS scoping (use this for browser-only code that uses Browserify/WebPack).
 * `shared-node-browser` - Globals common to both Node.js and Browser.
 * `es6` - enable all ECMAScript 6 features except for modules (this automatically sets the `ecmaVersion` parser option to 6).
+* `es2017` - adds all ECMAScript 2017 globals and automatically sets the `ecmaVersion` parser option to 8.
+* `es2020` - adds all ECMAScript 2020 globals and automatically sets the `ecmaVersion` parser option to 11.
 * `worker` - web workers global variables.
 * `amd` - defines `require()` and `define()` as global variables as per the [amd](https://github.com/amdjs/amdjs-api/wiki/AMD) spec.
 * `mocha` - adds all of the Mocha testing global variables.
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index 7f80887f0..3c67d33da 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -85,8 +85,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
  * @property {number} warningCount Number of warnings for the result.
  * @property {number} fixableErrorCount Number of fixable errors for the result.
  * @property {number} fixableWarningCount Number of fixable warnings for the result.
- * @property {string=} [source] The source code of the file that was linted.
- * @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
+ * @property {string} [source] The source code of the file that was linted.
+ * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
  */
 
 /**
@@ -329,7 +329,6 @@ function getRule(ruleId, configArrays) {
 /**
  * Collect used deprecated rules.
  * @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
- * @param {Map<string, Object>} ruleMap The rule definitions which were used (built-ins).
  * @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
  */
 function *iterateRuleDeprecationWarnings(usedConfigArrays) {
@@ -530,7 +529,6 @@ class CLIEngine {
     /**
      * Creates a new instance of the core CLI engine.
      * @param {CLIEngineOptions} providedOptions The options for this instance.
-     * @constructor
      */
     constructor(providedOptions) {
         const options = Object.assign(
diff --git a/lib/cli-engine/ignored-paths.js b/lib/cli-engine/ignored-paths.js
index 137d156e9..12dfce066 100644
--- a/lib/cli-engine/ignored-paths.js
+++ b/lib/cli-engine/ignored-paths.js
@@ -78,7 +78,7 @@ function mergeDefaultOptions(options) {
     return Object.assign({}, DEFAULT_OPTIONS, options);
 }
 
-/* eslint-disable valid-jsdoc */
+/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */
 /**
  * Normalize the path separators in a given string.
  * On Windows environment, this replaces `\` by `/`.
@@ -89,7 +89,7 @@ function mergeDefaultOptions(options) {
 const normalizePathSeps = path.sep === "/"
     ? (str => str)
     : ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu"));
-/* eslint-enable valid-jsdoc */
+/* eslint-enable jsdoc/check-param-names, jsdoc/require-param */
 
 /**
  * Converts a glob pattern to a new glob pattern relative to a different directory
@@ -298,7 +298,7 @@ class IgnoredPaths {
 
     /**
      * read ignore filepath
-     * @param {string} filePath, file to add to ig
+     * @param {string} filePath file to add to ig
      * @returns {Array} raw ignore rules
      */
     readIgnoreFile(filePath) {
diff --git a/lib/cli-engine/lint-result-cache.js b/lib/cli-engine/lint-result-cache.js
index 9408780fb..14e19d9e5 100644
--- a/lib/cli-engine/lint-result-cache.js
+++ b/lib/cli-engine/lint-result-cache.js
@@ -47,7 +47,6 @@ class LintResultCache {
 
     /**
      * Creates a new LintResultCache instance.
-     * @constructor
      * @param {string} cacheFileLocation The cache file location.
      *   configuration lookup by file path).
      */
diff --git a/lib/init/config-rule.js b/lib/init/config-rule.js
index fbe9b779a..e40feb714 100644
--- a/lib/init/config-rule.js
+++ b/lib/init/config-rule.js
@@ -99,7 +99,7 @@ function groupByProperty(objects) {
  * Configs may also have one or more additional elements to specify rule
  * configuration or options.
  *
- * @typedef {array|number} ruleConfig
+ * @typedef {Array|number} ruleConfig
  * @param {number}  0  The rule's severity (0, 1, 2).
  */
 
@@ -185,7 +185,7 @@ class RuleConfigSet {
 
         /**
          * Stored valid rule configurations for this instance
-         * @type {array}
+         * @type {Array}
          */
         this.ruleConfigs = configs || [];
     }
diff --git a/lib/init/npm-utils.js b/lib/init/npm-utils.js
index 7fe7c6e7d..3d4a896b6 100644
--- a/lib/init/npm-utils.js
+++ b/lib/init/npm-utils.js
@@ -163,7 +163,7 @@ function checkDevDeps(packages) {
 /**
  * Check whether package.json is found in current path.
  *
- * @param   {string=} startDir Starting directory
+ * @param   {string} [startDir] Starting directory
  * @returns {boolean} Whether a package.json is found in current path.
  */
 function checkPackageJson(startDir) {
diff --git a/lib/linter/code-path-analysis/debug-helpers.js b/lib/linter/code-path-analysis/debug-helpers.js
index 9af985ce8..2ca6dbc1e 100644
--- a/lib/linter/code-path-analysis/debug-helpers.js
+++ b/lib/linter/code-path-analysis/debug-helpers.js
@@ -21,7 +21,7 @@ const debug = require("debug")("eslint:code-path");
  * @returns {string} Id of the segment.
  */
 /* istanbul ignore next */
-function getId(segment) { // eslint-disable-line require-jsdoc
+function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
     return segment.id + (segment.reachable ? "" : "!");
 }
 
diff --git a/lib/linter/linter.js b/lib/linter/linter.js
index 7d1dc8c8c..534181da9 100644
--- a/lib/linter/linter.js
+++ b/lib/linter/linter.js
@@ -472,7 +472,6 @@ function normalizeFilename(filename) {
     return index === -1 ? filename : parts.slice(index).join(path.sep);
 }
 
-// eslint-disable-next-line valid-jsdoc
 /**
  * Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
  * consistent shape.
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index fb84b732e..f1c024c36 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -303,7 +303,7 @@ module.exports = {
          * @param {int} needed Expected indentation character count
          * @param {int} gottenSpaces Indentation space count in the actual node/code
          * @param {int} gottenTabs Indentation tab count in the actual node/code
-         * @param {Object=} loc Error line and column location
+         * @param {Object} [loc] Error line and column location
          * @param {boolean} isLastNodeCheck Is the error for last node check
          * @returns {void}
          */
diff --git a/lib/rules/new-parens.js b/lib/rules/new-parens.js
index 4b9958fe6..405ec1b51 100644
--- a/lib/rules/new-parens.js
+++ b/lib/rules/new-parens.js
@@ -65,7 +65,11 @@ module.exports = {
 
                 const lastToken = sourceCode.getLastToken(node);
                 const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken);
-                const hasParens = hasLastParen && astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken));
+
+                // `hasParens` is true only if the new expression ends with its own parens, e.g., new new foo() does not end with its own parens
+                const hasParens = hasLastParen &&
+                    astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken)) &&
+                    node.callee.range[1] < node.range[1];
 
                 if (always) {
                     if (!hasParens) {
diff --git a/lib/rules/no-useless-rename.js b/lib/rules/no-useless-rename.js
index c1860645e..67af97c8b 100644
--- a/lib/rules/no-useless-rename.js
+++ b/lib/rules/no-useless-rename.js
@@ -36,7 +36,8 @@ module.exports = {
     },
 
     create(context) {
-        const options = context.options[0] || {},
+        const sourceCode = context.getSourceCode(),
+            options = context.options[0] || {},
             ignoreDestructuring = options.ignoreDestructuring === true,
             ignoreImport = options.ignoreImport === true,
             ignoreExport = options.ignoreExport === true;
@@ -64,10 +65,18 @@ module.exports = {
                     type
                 },
                 fix(fixer) {
+                    if (sourceCode.commentsExistBetween(initial, result)) {
+                        return null;
+                    }
+
+                    const replacementText = result.type === "AssignmentPattern"
+                        ? sourceCode.getText(result)
+                        : name;
+
                     return fixer.replaceTextRange([
                         initial.range[0],
                         result.range[1]
-                    ], name);
+                    ], replacementText);
                 }
             });
         }
@@ -82,26 +91,21 @@ module.exports = {
                 return;
             }
 
-            const properties = node.properties;
-
-            for (let i = 0; i < properties.length; i++) {
-                if (properties[i].shorthand) {
-                    continue;
-                }
+            for (const property of node.properties) {
 
                 /**
-                 * If an ObjectPattern property is computed, we have no idea
-                 * if a rename is useless or not. If an ObjectPattern property
-                 * lacks a key, it is likely an ExperimentalRestProperty and
-                 * so there is no "renaming" occurring here.
+                 * Properties using shorthand syntax and rest elements can not be renamed.
+                 * If the property is computed, we have no idea if a rename is useless or not.
                  */
-                if (properties[i].computed || !properties[i].key) {
+                if (property.shorthand || property.type === "RestElement" || property.computed) {
                     continue;
                 }
 
-                if (properties[i].key.type === "Identifier" && properties[i].key.name === properties[i].value.name ||
-                        properties[i].key.type === "Literal" && properties[i].key.value === properties[i].value.name) {
-                    reportError(properties[i], properties[i].key, properties[i].value, "Destructuring assignment");
+                const key = (property.key.type === "Identifier" && property.key.name) || (property.key.type === "Literal" && property.key.value);
+                const renamedKey = property.value.type === "AssignmentPattern" ? property.value.left.name : property.value.name;
+
+                if (key === renamedKey) {
+                    reportError(property, property.key, property.value, "Destructuring assignment");
                 }
             }
         }
diff --git a/lib/shared/logging.js b/lib/shared/logging.js
index 3eb898c34..6aa1f5a1e 100644
--- a/lib/shared/logging.js
+++ b/lib/shared/logging.js
@@ -12,6 +12,7 @@ module.exports = {
 
     /**
      * Cover for console.log
+     * @param {...any} args The elements to log.
      * @returns {void}
      */
     info(...args) {
@@ -20,6 +21,7 @@ module.exports = {
 
     /**
      * Cover for console.error
+     * @param {...any} args The elements to log.
      * @returns {void}
      */
     error(...args) {
diff --git a/lib/shared/runtime-info.js b/lib/shared/runtime-info.js
index 324f457d8..169bbc58f 100644
--- a/lib/shared/runtime-info.js
+++ b/lib/shared/runtime-info.js
@@ -20,110 +20,121 @@ const packageJson = require("../../package.json");
 //------------------------------------------------------------------------------
 
 /**
- * Checks if a path is a child of a directory.
- * @param {string} parentPath - The parent path to check.
- * @param {string} childPath - The path to check.
- * @returns {boolean} Whether or not the given path is a child of a directory.
- */
-function isChildOfDirectory(parentPath, childPath) {
-    return !path.relative(parentPath, childPath).startsWith("..");
-}
-
-/**
- * Synchronously executes a shell command and formats the result.
- * @param {string} cmd - The command to execute.
- * @param {Array} args - The arguments to be executed with the command.
- * @returns {string} The version returned by the command.
+ * Generates and returns execution environment information.
+ * @returns {string} A string that contains execution environment information.
  */
-function execCommand(cmd, args) {
-    const process = spawn.sync(cmd, args, { encoding: "utf8" });
-
-    if (process.error) {
-        throw process.error;
+function environment() {
+    const cache = new Map();
+
+    /**
+     * Checks if a path is a child of a directory.
+     * @param {string} parentPath - The parent path to check.
+     * @param {string} childPath - The path to check.
+     * @returns {boolean} Whether or not the given path is a child of a directory.
+     */
+    function isChildOfDirectory(parentPath, childPath) {
+        return !path.relative(parentPath, childPath).startsWith("..");
     }
 
-    return process.stdout.trim();
-}
+    /**
+     * Synchronously executes a shell command and formats the result.
+     * @param {string} cmd - The command to execute.
+     * @param {Array} args - The arguments to be executed with the command.
+     * @returns {string} The version returned by the command.
+     */
+    function execCommand(cmd, args) {
+        const key = [cmd, ...args].join(" ");
+
+        if (cache.has(key)) {
+            return cache.get(key);
+        }
 
-/**
- * Normalizes a version number.
- * @param {string} versionStr - The string to normalize.
- * @returns {string} The normalized version number.
- */
-function normalizeVersionStr(versionStr) {
-    return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
-}
+        const process = spawn.sync(cmd, args, { encoding: "utf8" });
 
-/**
- * Gets bin version.
- * @param {string} bin - The bin to check.
- * @returns {string} The normalized version returned by the command.
- */
-function getBinVersion(bin) {
-    const binArgs = ["--version"];
-
-    try {
-        return normalizeVersionStr(execCommand(bin, binArgs));
-    } catch (e) {
-        log.error(`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``);
-        throw e;
-    }
-}
+        if (process.error) {
+            throw process.error;
+        }
 
-/**
- * Gets installed npm package version.
- * @param {string} pkg - The package to check.
- * @param {boolean} global - Whether to check globally or not.
- * @returns {string} The normalized version returned by the command.
- */
-function getNpmPackageVersion(pkg, { global = false } = {}) {
-    const npmBinArgs = ["bin", "-g"];
-    const npmLsArgs = ["ls", "--depth=0", "--json", "eslint"];
+        const result = process.stdout.trim();
 
-    if (global) {
-        npmLsArgs.push("-g");
+        cache.set(key, result);
+        return result;
     }
 
-    try {
-        const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));
-
-        /*
-         * Checking globally returns an empty JSON object, while local checks
-         * include the name and version of the local project.
-         */
-        if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
-            return "Not found";
-        }
+    /**
+     * Normalizes a version number.
+     * @param {string} versionStr - The string to normalize.
+     * @returns {string} The normalized version number.
+     */
+    function normalizeVersionStr(versionStr) {
+        return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
+    }
 
-        const [, processBinPath] = process.argv;
-        let npmBinPath;
+    /**
+     * Gets bin version.
+     * @param {string} bin - The bin to check.
+     * @returns {string} The normalized version returned by the command.
+     */
+    function getBinVersion(bin) {
+        const binArgs = ["--version"];
 
         try {
-            npmBinPath = execCommand("npm", npmBinArgs);
+            return normalizeVersionStr(execCommand(bin, binArgs));
         } catch (e) {
-            log.error(`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``);
+            log.error(`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``);
             throw e;
         }
+    }
 
-        const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
-        let pkgVersion = parsedStdout.dependencies.eslint.version;
-
-        if ((global && isGlobal) || (!global && !isGlobal)) {
-            pkgVersion += " (Currently used)";
+    /**
+     * Gets installed npm package version.
+     * @param {string} pkg - The package to check.
+     * @param {boolean} global - Whether to check globally or not.
+     * @returns {string} The normalized version returned by the command.
+     */
+    function getNpmPackageVersion(pkg, { global = false } = {}) {
+        const npmBinArgs = ["bin", "-g"];
+        const npmLsArgs = ["ls", "--depth=0", "--json", "eslint"];
+
+        if (global) {
+            npmLsArgs.push("-g");
         }
 
-        return normalizeVersionStr(pkgVersion);
-    } catch (e) {
-        log.error(`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``);
-        throw e;
+        try {
+            const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));
+
+            /*
+             * Checking globally returns an empty JSON object, while local checks
+             * include the name and version of the local project.
+             */
+            if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
+                return "Not found";
+            }
+
+            const [, processBinPath] = process.argv;
+            let npmBinPath;
+
+            try {
+                npmBinPath = execCommand("npm", npmBinArgs);
+            } catch (e) {
+                log.error(`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``);
+                throw e;
+            }
+
+            const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
+            let pkgVersion = parsedStdout.dependencies.eslint.version;
+
+            if ((global && isGlobal) || (!global && !isGlobal)) {
+                pkgVersion += " (Currently used)";
+            }
+
+            return normalizeVersionStr(pkgVersion);
+        } catch (e) {
+            log.error(`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``);
+            throw e;
+        }
     }
-}
 
-/**
- * Generates and returns execution environment information.
- * @returns {string} A string that contains execution environment information.
- */
-function environment() {
     return [
         "Environment Info:",
         "",
diff --git a/lib/source-code/source-code.js b/lib/source-code/source-code.js
index 88a9c0a4e..42e7b0c2f 100644
--- a/lib/source-code/source-code.js
+++ b/lib/source-code/source-code.js
@@ -93,7 +93,6 @@ class SourceCode extends TokenStore {
      * @param {ScopeManager|null} textOrConfig.scopeManager - The scope of this source code.
      * @param {Object|null} textOrConfig.visitorKeys - The visitor keys to traverse AST.
      * @param {ASTNode} [astIfNoConfig] - The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
-     * @constructor
      */
     constructor(textOrConfig, astIfNoConfig) {
         let text, ast, parserServices, scopeManager, visitorKeys;
@@ -206,9 +205,9 @@ class SourceCode extends TokenStore {
 
     /**
      * Gets the source code for the given node.
-     * @param {ASTNode=} node The AST node to get the text for.
-     * @param {int=} beforeCount The number of characters before the node to retrieve.
-     * @param {int=} afterCount The number of characters after the node to retrieve.
+     * @param {ASTNode} [node] The AST node to get the text for.
+     * @param {int} [beforeCount] The number of characters before the node to retrieve.
+     * @param {int} [afterCount] The number of characters after the node to retrieve.
      * @returns {string} The text representing the AST node.
      * @public
      */
diff --git a/package.json b/package.json
index 9ce1c59b0..e1fb731c1 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
   "scripts": {
     "test": "node Makefile.js test",
     "lint": "node Makefile.js lint",
+    "fix": "node Makefile.js lint -- fix",
     "fuzz": "node Makefile.js fuzz",
     "generate-release": "node Makefile.js generateRelease",
     "generate-alpharelease": "node Makefile.js generatePrerelease -- alpha",
@@ -96,6 +97,7 @@
     "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-jsdoc": "^15.9.5",
     "eslint-plugin-node": "^9.0.0",
     "eslint-release": "^1.2.0",
     "eslump": "^2.0.0",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index 526ce5600..1bbc1c576 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -1,6 +1,16 @@
 extends:
     - "eslint:recommended"
     - "plugin:node/recommended"
+plugins:
+    - "jsdoc"
+settings:
+    jsdoc:
+        tagNamePreference:
+            file: "fileoverview"
+            augments: "extends"
+            class: "constructor"
+        preferredTypes:
+            object: "Object"
 rules:
     array-bracket-spacing: "error"
     array-callback-return: "error"
@@ -31,6 +41,20 @@ rules:
     generator-star-spacing: "error"
     guard-for-in: "error"
     handle-callback-err: ["error", "err"]
+    jsdoc/check-alignment: "error"
+    jsdoc/check-param-names: "error"
+    jsdoc/check-syntax: "error"
+    jsdoc/check-tag-names: "error"
+    jsdoc/check-types: "error"
+    jsdoc/implements-on-classes: "error"
+    jsdoc/require-jsdoc: "error"
+    jsdoc/require-param: "error"
+    jsdoc/require-param-description: "error"
+    jsdoc/require-param-name: "error"
+    jsdoc/require-param-type: "error"
+    jsdoc/require-returns: ["error", { forceRequireReturn: true, forceReturnsWithAsync: true }]
+    jsdoc/require-returns-description: "error"
+    jsdoc/require-returns-type: "error"
     key-spacing: ["error", { beforeColon: false, afterColon: true }]
     keyword-spacing: "error"
     lines-around-comment: ["error", {
@@ -154,7 +178,6 @@ rules:
     quotes: ["error", "double", {avoidEscape: true}]
     quote-props: ["error", "as-needed"]
     radix: "error"
-    require-jsdoc: "error"
     require-unicode-regexp: "error"
     rest-spread-spacing: "error"
     semi: "error"
@@ -172,17 +195,6 @@ rules:
     template-curly-spacing: ["error", "never"]
     template-tag-spacing: "error"
     unicode-bom: "error"
-    valid-jsdoc: ["error", {
-        prefer: { "return": "returns"},
-        preferType: {
-            "String": "string",
-            "Number": "number",
-            "Boolean": "boolean",
-            "array": "Array",
-            "object": "Object",
-            "function": "Function"
-        }
-    }]
     wrap-iife: "error"
     yield-star-spacing: "error"
     yoda: ["error", "never"]
diff --git a/packages/eslint-config-eslint/package.json b/packages/eslint-config-eslint/package.json
index 514dbb7ca..48bfb65f5 100644
--- a/packages/eslint-config-eslint/package.json
+++ b/packages/eslint-config-eslint/package.json
@@ -20,6 +20,7 @@
   "homepage": "https://eslint.org",
   "bugs": "https://github.com/eslint/eslint/issues/",
   "peerDependencies": {
+    "eslint-plugin-jsdoc": "^15.9.5",
     "eslint-plugin-node": "^9.0.0"
   },
   "keywords": [
diff --git a/tools/code-sample-minimizer.js b/tools/code-sample-minimizer.js
index 63a687e56..2954bf08f 100644
--- a/tools/code-sample-minimizer.js
+++ b/tools/code-sample-minimizer.js
@@ -30,6 +30,7 @@ function isStatement(node) {
  * Given "bad" source text (e.g. an code sample that causes a rule to crash), tries to return a smaller
  * piece of source text which is also "bad", to make it easier for a human to figure out where the
  * problem is.
+ * @param {Object} options Options to process
  * @param {string} options.sourceText Initial piece of "bad" source text
  * @param {function(string): boolean} options.predicate A predicate that returns `true` for bad source text and `false` for good source text
  * @param {Parser} [options.parser] The parser used to parse the source text. Defaults to a modified
diff --git a/tools/internal-rules/no-invalid-meta.js b/tools/internal-rules/no-invalid-meta.js
index aeb7862fa..016643a8d 100644
--- a/tools/internal-rules/no-invalid-meta.js
+++ b/tools/internal-rules/no-invalid-meta.js
@@ -104,7 +104,6 @@ function hasMetaSchema(metaPropertyNode) {
  *
  * @param {RuleContext} context The ESLint rule context.
  * @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
- * @param {boolean} ruleIsFixable whether the rule is fixable or not.
  * @returns {void}
  */
 function checkMetaValidity(context, exportsNode) {


Test patch:
diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js
index 0d1319c11..83b91c45d 100644
--- a/lib/rule-tester/rule-tester.js
+++ b/lib/rule-tester/rule-tester.js
@@ -177,7 +177,6 @@ class RuleTester {
     /**
      * Creates a new instance of RuleTester.
      * @param {Object} [testerConfig] Optional, extra configuration for the tester
-     * @constructor
      */
     constructor(testerConfig) {
 
diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js
index b6254fa5f..932dd9ec4 100644
--- a/tests/bin/eslint.js
+++ b/tests/bin/eslint.js
@@ -163,6 +163,15 @@ describe("bin/eslint.js", () => {
                 return assertExitCode(child, 0);
             });
         });
+
+        it("successfully handles more than 4k data via stdin", () => {
+            const child = runESLint(["--stdin", "--no-eslintrc"]);
+            const large = fs.createReadStream(`${__dirname}/../bench/large.js`, "utf8");
+
+            large.pipe(child.stdin);
+
+            return assertExitCode(child, 0);
+        });
     });
 
     describe("running on files", () => {
diff --git a/tests/lib/_utils.js b/tests/lib/_utils.js
index 3251a6023..2ac119fb7 100644
--- a/tests/lib/_utils.js
+++ b/tests/lib/_utils.js
@@ -25,7 +25,6 @@ function unIndent(strings, ...values) {
     return lines.map(line => line.slice(minLineIndent)).join("\n");
 }
 
-// eslint-disable-next-line valid-jsdoc
 /**
  * Add support of `recursive` option.
  * @param {import("fs")} fs The in-memory file system.
@@ -56,7 +55,6 @@ function supportMkdirRecursiveOption(fs, cwd) {
     };
 }
 
-// eslint-disable-next-line valid-jsdoc
 /**
  * Define in-memory file system.
  * @param {Object} options The options.
diff --git a/tests/lib/cli-engine/_utils.js b/tests/lib/cli-engine/_utils.js
index a3c56484c..bff1801f3 100644
--- a/tests/lib/cli-engine/_utils.js
+++ b/tests/lib/cli-engine/_utils.js
@@ -53,9 +53,6 @@
  */
 "use strict";
 
-// To use TypeScript type annotations for VSCode intellisense.
-/* eslint-disable valid-jsdoc */
-
 const path = require("path");
 const vm = require("vm");
 const Proxyquire = require("proxyquire/lib/proxyquire");
diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js
index e0ca4f063..22c9b956d 100644
--- a/tests/lib/cli-engine/cascading-config-array-factory.js
+++ b/tests/lib/cli-engine/cascading-config-array-factory.js
@@ -85,6 +85,7 @@ describe("CascadingConfigArrayFactory", () => {
 
             /**
              * Returns the path inside of the fixture directory.
+             * @param {...string} args file path segments.
              * @returns {string} The path inside the fixture directory.
              * @private
              */
@@ -671,6 +672,7 @@ describe("CascadingConfigArrayFactory", () => {
 
                 /**
                  * Returns the path inside of the fixture directory.
+                 * @param {...string} args file path segments.
                  * @returns {string} The path inside the fixture directory.
                  * @private
                  */
@@ -766,6 +768,7 @@ describe("CascadingConfigArrayFactory", () => {
 
                 /**
                  * Returns the path inside of the fixture directory.
+                 * @param {...string} args file path segments.
                  * @returns {string} The path inside the fixture directory.
                  * @private
                  */
diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js
index b3039e743..223cb6607 100644
--- a/tests/lib/cli-engine/cli-engine.js
+++ b/tests/lib/cli-engine/cli-engine.js
@@ -50,6 +50,7 @@ describe("CLIEngine", () => {
 
     /**
      * Returns the path inside of the fixture directory.
+     * @param {...string} args file path segments.
      * @returns {string} The path inside the fixture directory.
      * @private
      */
@@ -1543,7 +1544,11 @@ describe("CLIEngine", () => {
             engine = new CLIEngine({
                 cwd: originalDir,
                 configFile: ".eslintrc.js",
-                rules: { "indent-legacy": 1 }
+                rules: {
+                    "indent-legacy": 1,
+                    "require-jsdoc": 1,
+                    "valid-jsdoc": 1
+                }
             });
 
             const report = engine.executeOnFiles(["lib/cli*.js"]);
diff --git a/tests/lib/cli-engine/file-enumerator.js b/tests/lib/cli-engine/file-enumerator.js
index 13ff479db..d379ac520 100644
--- a/tests/lib/cli-engine/file-enumerator.js
+++ b/tests/lib/cli-engine/file-enumerator.js
@@ -173,6 +173,7 @@ describe("FileEnumerator", () => {
 
             /**
              * Returns the path inside of the fixture directory.
+             * @param {...string} args file path segments.
              * @returns {string} The path inside the fixture directory.
              * @private
              */
diff --git a/tests/lib/cli-engine/ignored-paths.js b/tests/lib/cli-engine/ignored-paths.js
index 487024427..7fd8e8408 100644
--- a/tests/lib/cli-engine/ignored-paths.js
+++ b/tests/lib/cli-engine/ignored-paths.js
@@ -92,6 +92,7 @@ function countDefaultPatterns(ignoredPaths) {
 
 /**
  * Returns the path inside of the fixture directory.
+ * @param {...string} args file path segments.
  * @returns {string} The path inside the fixture directory.
  * @private
  */
diff --git a/tests/lib/cli.js b/tests/lib/cli.js
index 1b98fb137..e0f10d5ae 100644
--- a/tests/lib/cli.js
+++ b/tests/lib/cli.js
@@ -71,6 +71,7 @@ describe("cli", () => {
 
     /**
      * Returns the path inside of the fixture directory.
+     * @param {...string} args file path segments.
      * @returns {string} The path inside the fixture directory.
      * @private
      */
diff --git a/tests/lib/init/config-initializer.js b/tests/lib/init/config-initializer.js
index ed2388fd5..3393ae2be 100644
--- a/tests/lib/init/config-initializer.js
+++ b/tests/lib/init/config-initializer.js
@@ -57,6 +57,7 @@ describe("configInitializer", () => {
 
     /**
      * Returns the path inside of the fixture directory.
+     * @param {...string} args file path segments.
      * @returns {string} The path inside the fixture directory.
      * @private
      */
diff --git a/tests/lib/init/source-code-utils.js b/tests/lib/init/source-code-utils.js
index a5c9e90a1..8c821de78 100644
--- a/tests/lib/init/source-code-utils.js
+++ b/tests/lib/init/source-code-utils.js
@@ -31,6 +31,7 @@ describe("SourceCodeUtil", () => {
 
     /**
      * Returns the path inside of the fixture directory.
+     * @param {...string} args file path segments.
      * @returns {string} The path inside the fixture directory.
      * @private
      */
diff --git a/tests/lib/rules/new-parens.js b/tests/lib/rules/new-parens.js
index c13cc7150..3508ace8e 100644
--- a/tests/lib/rules/new-parens.js
+++ b/tests/lib/rules/new-parens.js
@@ -119,6 +119,12 @@ ruleTester.run("new-parens", rule, {
             options: ["always"],
             errors: [error]
         },
+        {
+            code: "var a = new new Foo()",
+            output: "var a = new new Foo()()",
+            options: ["always"],
+            errors: [error]
+        },
 
         // Never
         {
@@ -168,6 +174,12 @@ ruleTester.run("new-parens", rule, {
             output: "var a = ((new Foo)).bar;",
             options: ["never"],
             errors: [neverError]
+        },
+        {
+            code: "var a = new new Foo()",
+            output: "var a = new (new Foo)",
+            options: ["never"],
+            errors: [neverError]
         }
     ]
 });
diff --git a/tests/lib/rules/no-useless-rename.js b/tests/lib/rules/no-useless-rename.js
index 1fcc843db..c9be46d69 100644
--- a/tests/lib/rules/no-useless-rename.js
+++ b/tests/lib/rules/no-useless-rename.js
@@ -182,6 +182,21 @@ ruleTester.run("no-useless-rename", rule, {
             output: "let {'foo': {bar}, baz} = obj;",
             errors: ["Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
         },
+        {
+            code: "let {foo: foo = 1, 'bar': bar = 1, baz: baz} = obj;",
+            output: "let {foo = 1, bar = 1, baz} = obj;",
+            errors: ["Destructuring assignment foo unnecessarily renamed.", "Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "let {foo: {bar: bar = 1, 'baz': baz = 1}} = obj;",
+            output: "let {foo: {bar = 1, baz = 1}} = obj;",
+            errors: ["Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "let {foo: {bar: bar = {}} = {}} = obj;",
+            output: "let {foo: {bar = {}} = {}} = obj;",
+            errors: ["Destructuring assignment bar unnecessarily renamed."]
+        },
         {
             code: "function func({foo: foo}) {}",
             output: "function func({foo}) {}",
@@ -202,6 +217,21 @@ ruleTester.run("no-useless-rename", rule, {
             output: "function func({foo, bar}) {}",
             errors: ["Destructuring assignment foo unnecessarily renamed.", "Destructuring assignment bar unnecessarily renamed."]
         },
+        {
+            code: "function func({foo: foo = 1, 'bar': bar = 1, baz: baz}) {}",
+            output: "function func({foo = 1, bar = 1, baz}) {}",
+            errors: ["Destructuring assignment foo unnecessarily renamed.", "Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "function func({foo: {bar: bar = 1, 'baz': baz = 1}}) {}",
+            output: "function func({foo: {bar = 1, baz = 1}}) {}",
+            errors: ["Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "function func({foo: {bar: bar = {}} = {}}) {}",
+            output: "function func({foo: {bar = {}} = {}}) {}",
+            errors: ["Destructuring assignment bar unnecessarily renamed."]
+        },
         {
             code: "({foo: foo}) => {}",
             output: "({foo}) => {}",
@@ -222,6 +252,21 @@ ruleTester.run("no-useless-rename", rule, {
             output: "({foo, bar}) => {}",
             errors: ["Destructuring assignment foo unnecessarily renamed.", "Destructuring assignment bar unnecessarily renamed."]
         },
+        {
+            code: "({foo: foo = 1, 'bar': bar = 1, baz: baz}) => {}",
+            output: "({foo = 1, bar = 1, baz}) => {}",
+            errors: ["Destructuring assignment foo unnecessarily renamed.", "Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "({foo: {bar: bar = 1, 'baz': baz = 1}}) => {}",
+            output: "({foo: {bar = 1, baz = 1}}) => {}",
+            errors: ["Destructuring assignment bar unnecessarily renamed.", "Destructuring assignment baz unnecessarily renamed."]
+        },
+        {
+            code: "({foo: {bar: bar = {}} = {}}) => {}",
+            output: "({foo: {bar = {}} = {}}) => {}",
+            errors: ["Destructuring assignment bar unnecessarily renamed."]
+        },
         {
             code: "const {foo: foo, ...stuff} = myObject;",
             output: "const {foo, ...stuff} = myObject;",
@@ -299,6 +344,133 @@ ruleTester.run("no-useless-rename", rule, {
             code: "export {foo as foo, bar as bar} from 'foo';",
             output: "export {foo, bar} from 'foo';",
             errors: ["Export foo unnecessarily renamed.", "Export bar unnecessarily renamed."]
+        },
+
+        // Should not autofix if it would remove comments
+        {
+            code: "({/* comment */foo: foo} = {});",
+            output: "({/* comment */foo} = {});",
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo, /* comment */bar: bar} = {});",
+            output: "({foo, /* comment */bar} = {});",
+            errors: ["Destructuring assignment bar unnecessarily renamed."]
+        },
+        {
+            code: "({foo/**/ : foo} = {});",
+            output: null,
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo /**/: foo} = {});",
+            output: null,
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo://\nfoo} = {});",
+            output: null,
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo: /**/foo} = {});",
+            output: null,
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo: foo/* comment */} = {});",
+            output: "({foo/* comment */} = {});",
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "({foo: foo//comment\n,bar} = {});",
+            output: "({foo//comment\n,bar} = {});",
+            errors: ["Destructuring assignment foo unnecessarily renamed."]
+        },
+        {
+            code: "import {/* comment */foo as foo} from 'foo';",
+            output: "import {/* comment */foo} from 'foo';",
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo,/* comment */bar as bar} from 'foo';",
+            output: "import {foo,/* comment */bar} from 'foo';",
+            errors: ["Import bar unnecessarily renamed."]
+        },
+        {
+            code: "import {foo/**/ as foo} from 'foo';",
+            output: null,
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo /**/as foo} from 'foo';",
+            output: null,
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo //\nas foo} from 'foo';",
+            output: null,
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo as/**/foo} from 'foo';",
+            output: null,
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo as foo/* comment */} from 'foo';",
+            output: "import {foo/* comment */} from 'foo';",
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "import {foo as foo/* comment */,bar} from 'foo';",
+            output: "import {foo/* comment */,bar} from 'foo';",
+            errors: ["Import foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {/* comment */foo as foo};",
+            output: "let foo; export {/* comment */foo};",
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo, bar; export {foo,/* comment */bar as bar};",
+            output: "let foo, bar; export {foo,/* comment */bar};",
+            errors: ["Export bar unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {foo/**/as foo};",
+            output: null,
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {foo as/**/ foo};",
+            output: null,
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {foo as /**/foo};",
+            output: null,
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {foo as//comment\n foo};",
+            output: null,
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo; export {foo as foo/* comment*/};",
+            output: "let foo; export {foo/* comment*/};",
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo, bar; export {foo as foo/* comment*/,bar};",
+            output: "let foo, bar; export {foo/* comment*/,bar};",
+            errors: ["Export foo unnecessarily renamed."]
+        },
+        {
+            code: "let foo, bar; export {foo as foo//comment\n,bar};",
+            output: "let foo, bar; export {foo//comment\n,bar};",
+            errors: ["Export foo unnecessarily renamed."]
         }
     ]
 });
diff --git a/tests/lib/shared/runtime-info.js b/tests/lib/shared/runtime-info.js
index 77ada8bc7..c82d416df 100644
--- a/tests/lib/shared/runtime-info.js
+++ b/tests/lib/shared/runtime-info.js
@@ -86,8 +86,7 @@ describe("RuntimeInfo", () => {
                             }
                         }
                     }
-                `,
-                NPM_BIN_PATH
+                `
             ];
         });
 
@@ -138,6 +137,7 @@ describe("RuntimeInfo", () => {
                     "version": "1.0.0"
                 }
             `);
+            spawnSyncStubArgs.push(NPM_BIN_PATH);
             setupSpawnSyncStubReturnVals(spawnSyncStub, spawnSyncStubArgs);
             process.argv[1] = GLOBAL_ESLINT_BIN_PATH;
 
diff --git a/tests/tools/loose-parser.js b/tests/tools/loose-parser.js
index abb3aeb5b..98e97cc36 100644
--- a/tests/tools/loose-parser.js
+++ b/tests/tools/loose-parser.js
@@ -9,7 +9,6 @@
 const acorn = require("acorn");
 const espree = require("espree/lib/espree");
 
-// eslint-disable-next-line valid-jsdoc
 /**
  * Define the parser which ignores recoverable errors.
  * @returns {(parser:acorn.Parser) => acorn.Parser} The function that defines loose parser.