Repository: eslint/eslint
Pull request #9502 (2017-10-22 19:23:35)

Chore: Add tests to cover array and object values and leading commas.
<!--
    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
[ ] 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
[X] Other, please explain:
Add tests to an existing rule.
<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**
Added tests to improve coverage and relocated 2 existing tests into the appropriate section.
This PR implements step 2 of my plan to handle issue 6251 (refs #6251).
**Is there anything you'd like reviewers to focus on?**




eslintbot: LGTM

Patch:
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index e8ad7ffbe..d33ca1105 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -32,7 +32,7 @@
 
 </details>
 
-**What did you do? Please include the actual source code causing the issue.**
+**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**
 
 <!-- Paste the source code below: -->
 ```js
@@ -41,6 +41,11 @@
 
 
 
+```
+
+<!-- Paste the command you used to run ESLint: -->
+```bash
+
 ```
 
 **What did you expect to happen?**
diff --git a/Makefile.js b/Makefile.js
index 01b0cc2b1..30220062d 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -85,9 +85,6 @@ const NODE = "node ", // intentional extra space
     PERF_MULTIFILES_TARGET_DIR = path.join(PERF_TMP_DIR, "eslint"),
     PERF_MULTIFILES_TARGETS = `"${PERF_MULTIFILES_TARGET_DIR + path.sep}{lib,tests${path.sep}lib}${path.sep}**${path.sep}*.js"`,
 
-    // Regex
-    TAG_REGEX = /^(?:Breaking|Build|Chore|Docs|Fix|New|Update|Upgrade):/,
-
     // Settings
     MOCHA_TIMEOUT = 10000;
 
@@ -106,7 +103,7 @@ function getTestFilePatterns() {
             initialValue.push(`"tests/lib/${currentValues}/**/*.js"`);
         }
         return initialValue;
-    }, ["tests/lib/rules/**/*.js", "tests/lib/*.js", "tests/templates/*.js", "tests/bin/**/*.js", "tests/tools/**/*.js"]).join(" ");
+    }, ["tests/lib/rules/**/*.js", "tests/lib/*.js", "tests/bin/**/*.js", "tests/tools/**/*.js"]).join(" ");
 }
 
 /**
@@ -383,24 +380,6 @@ function getFirstVersionOfDeletion(filePath) {
         .sort(semver.compare)[0];
 }
 
-
-/**
- * Returns all the branch names
- * @returns {string[]} branch names
- * @private
- */
-function getBranches() {
-    const branchesRaw = splitCommandResultToLines(execSilent("git branch --list")),
-        branches = [];
-
-    for (let i = 0; i < branchesRaw.length; i++) {
-        const branchName = branchesRaw[i].replace(/^\*(.*)/, "$1").trim();
-
-        branches.push(branchName);
-    }
-    return branches;
-}
-
 /**
  * Lints Markdown files.
  * @param {array} files Array of file names to lint.
@@ -445,18 +424,6 @@ function lintMarkdown(files) {
     return { code: returnCode };
 }
 
-/**
- * Check if the branch name is valid
- * @param {string} branchName Branch name to check
- * @returns {boolean} true is branch exists
- * @private
- */
-function hasBranch(branchName) {
-    const branches = getBranches();
-
-    return branches.indexOf(branchName) !== -1;
-}
-
 /**
  * Gets linting results from every formatter, based on a hard-coded snippet and config
  * @returns {Object} Output from each formatter
@@ -954,57 +921,6 @@ target.checkLicenses = function() {
     });
 };
 
-target.checkGitCommit = function() {
-    let commitMsgs,
-        failed;
-
-    if (hasBranch("master")) {
-        commitMsgs = splitCommandResultToLines(execSilent("git log HEAD --not master --format=format:%s --no-merges"));
-    } else {
-        commitMsgs = [execSilent("git log -1 --format=format:%s --no-merges")];
-    }
-
-    echo("Validating Commit Message");
-
-    // No commit since master should not cause test to fail
-    if (commitMsgs[0] === "") {
-        return;
-    }
-
-    // Check for more than one commit
-    if (commitMsgs.length > 1) {
-        echo(" - More than one commit found, please squash.");
-        failed = true;
-    }
-
-    // Only check non-release messages
-    if (!semver.valid(commitMsgs[0]) && !/^Revert /.test(commitMsgs[0])) {
-        if (commitMsgs[0].split(/\r?\n/)[0].length > 72) {
-            echo(" - First line of commit message must not exceed 72 characters");
-            failed = true;
-        }
-
-        // Check for tag at start of message
-        if (!TAG_REGEX.test(commitMsgs[0])) {
-            echo([" - Commit summary must start with one of:",
-                "    'Fix:'",
-                "    'Update:'",
-                "    'Breaking:'",
-                "    'Docs:'",
-                "    'Build:'",
-                "    'New:'",
-                "    'Upgrade:'",
-                "    'Chore:'",
-                "   Please refer to the contribution guidelines for more details."].join("\n"));
-            failed = true;
-        }
-    }
-
-    if (failed) {
-        exit(1);
-    }
-};
-
 /**
  * Downloads a repository which has many js files to test performance with multi files.
  * Here, it's eslint@1.10.3 (450 files)
diff --git a/docs/developer-guide/contributing/pull-requests.md b/docs/developer-guide/contributing/pull-requests.md
index efeca5fcd..84990a758 100644
--- a/docs/developer-guide/contributing/pull-requests.md
+++ b/docs/developer-guide/contributing/pull-requests.md
@@ -105,7 +105,7 @@ If there are any failing tests, update your code until all tests pass.
 
 With your code ready to go, this is a good time to double-check your submission to make sure it follows our conventions. Here are the things to check:
 
-* Run `npm run check-commit` to double-check that your commit is formatted correctly.
+* Make sure your commit is formatted correctly.
 * The pull request must have a description. The description should explain what you did and how its effects can be seen.
 * The commit message is properly formatted.
 * The change introduces no functional regression. Be sure to run `npm test` to verify your changes before submitting a pull request.
diff --git a/docs/developer-guide/contributing/working-on-issues.md b/docs/developer-guide/contributing/working-on-issues.md
index 95282aa9c..c1878dbea 100644
--- a/docs/developer-guide/contributing/working-on-issues.md
+++ b/docs/developer-guide/contributing/working-on-issues.md
@@ -4,29 +4,19 @@ Our public [issues tracker](https://github.com/eslint/eslint/issues) lists all o
 
 ## Issue Labels
 
-We use labels to indicate the status of issues. The most important labels are:
+We use labels to indicate the status of issues. The most complete documentation on the labels is found in the [Maintainer Guide](https://eslint.org/docs/maintainer-guide/issues.html#when-an-issue-is-opened), but most contributors should find the information on this page sufficient. The most important questions that labels can help you, as a contributor, answer are:
 
-1. [`triage`](https://github.com/eslint/eslint/issues?labels=triage&milestone=&page=1&state=open) - When an issue is first submitted, it is labeled as `triage`, which means the ESLint team needs to investigate and determine if the request is a bug report, a feature request, or something else. It's best not to work on tickets marked as `triage`, because we're not yet sure if we will accept the issue to work on.
-1. [`accepted`](https://github.com/eslint/eslint/issues?labels=accepted&milestone=&page=1&state=open) - Once an issue has been properly triaged and the team decides it must be addressed, someone will assign the `accepted` label to an issue. When an issue is accepted, anyone is free to work on it.
-1. [`bug`](https://github.com/eslint/eslint/issues?labels=bug&milestone=&page=1&state=open) - Indicates that the issue is reporting a problem. When submitting a pull request to work on this type of issue, be sure to prefix the commit message with "Fix:".
-1. [`feature`](https://github.com/eslint/eslint/issues?labels=feature&milestone=&page=1&state=open) - Indicates that the issue is requesting a new feature. Features are functionality that doesn't already exist in the project. When submitting a pull request to work on this type of issue, be sure to prefix the commit message with "New:".
-1. [`enhancement`](https://github.com/eslint/eslint/issues?labels=enhancement&milestone=&page=1&state=open) - Indicates that the issue is requesting a change to existing functionality. When submitting a pull request to work on this type of issue, be sure to prefix the commit message with "Update:".
-1. [`beginner`](https://github.com/eslint/eslint/issues?labels=beginner&milestone=&page=1&state=open) - Indicates that the issue is simple enough that it would be a good first contribution for a new contributor. If you're looking to get started helping out with ESLint, take a look at the beginner issues.
-1. [`help wanted`](https://github.com/eslint/eslint/issues?labels=help%20wanted&milestone=&page=1&state=open) - Indicates that the core team won't be working on this issue, however, we will accept pull requests from contributors. This basically means the issue isn't on the formal roadmap but it will be accepted if a contributor wants to implement it.
+1. Is this issue available for me to work on? If you have little or no experience contributing to ESLint, the [`beginner`](https://github.com/eslint/eslint/labels/beginner) label marks appropriate issues. Otherwise, the [`help wanted`](https://github.com/eslint/eslint/labels/help%20wanted) label is an invitation to work on the issue. If you have more experience, you can try working on other issues labeled [`accepted`](https://github.com/eslint/eslint/labels/accepted). Conversely, issues not yet ready to work on are labeled `triage`, `evaluating`, and/or `needs bikeshedding`, and issues that cannot currently be worked on because of something else, such as a bug in a dependency, are labeled `blocked`.
+1. What is this issue about? Labels describing the nature of issues include `bug`, `enhancement`, `feature`, `question`, `rule`, `documentation`, `core`, `build`, `cli`, `infrastructure`, `breaking`, and `chore`. These are documented in the [Maintainer Guide](https://eslint.org/docs/maintainer-guide/issues.html#types-of-issues).
+1. What is the priority of this issue? Because we have a lot of issues, we prioritize certain issues above others. The following is the list of priorities, from highest to lowest:
 
-## Bounty Issues
+    1. **Bugs** - problems with the project are actively affecting users. We want to get these resolved as quickly as possible.
+    1. **Documentation** - documentation issues are a type of bug in that they actively affect current users. As such, we want to address documentation issues as quickly as possible.
+    1. **Features** - new functionality that will aid users in the future.
+    1. **Enhancements** - requested improvements for existing functionality.
+    1. **Other** - anything else.
 
-We accept and assign issue bounties using [BountySource](https://www.bountysource.com/teams/eslint/issues).
-
-## Issue Priority
-
-Because we have a lot of issues, we prioritize certain issues above others. The following is the list of priorities, from highest to lowest:
-
-1. **Bugs** - problems with the project are actively affecting users. We want to get these resolved as quickly as possible.
-1. **Documentation** - documentation issues are a type of bug in that they actively affect current users. As such, we want to address documentation issues as quickly as possible.
-1. **Features** - new functionality that will aid users in the future.
-1. **Enhancements** - requested improvements for existing functionality.
-1. **Other** - anything else.
+    Some issues have had monetary rewards attached to them. Those are labeled `bounty`. Bounties are assigned via [BountySource](https://www.bountysource.com/teams/eslint/issues).
 
 ## Starting Work
 
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 38c49cb31..5fc35ae0a 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -240,8 +240,8 @@ function processFile(filename, configHelper, options, linter) {
 function createIgnoreResult(filePath, baseDir) {
     let message;
     const isHidden = /^\./.test(path.basename(filePath));
-    const isInNodeModules = baseDir && /^node_modules/.test(path.relative(baseDir, filePath));
-    const isInBowerComponents = baseDir && /^bower_components/.test(path.relative(baseDir, filePath));
+    const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
+    const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
 
     if (isHidden) {
         message = "File ignored by default.  Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
diff --git a/lib/cli.js b/lib/cli.js
index f04448097..6a5482bf9 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -144,6 +144,8 @@ const cli = {
 
         const files = currentOptions._;
 
+        const useStdin = typeof text === "string";
+
         if (currentOptions.version) { // version from package.json
 
             log.info(`v${require("../package.json").version}`);
@@ -153,7 +155,7 @@ const cli = {
                 log.error("The --print-config option must be used with exactly one file name.");
                 return 1;
             }
-            if (text) {
+            if (useStdin) {
                 log.error("The --print-config option is not available for piped-in code.");
                 return 1;
             }
@@ -164,27 +166,27 @@ const cli = {
 
             log.info(JSON.stringify(fileConfig, null, "  "));
             return 0;
-        } else if (currentOptions.help || (!files.length && !text)) {
+        } else if (currentOptions.help || (!files.length && !useStdin)) {
 
             log.info(options.generateHelp());
 
         } else {
 
-            debug(`Running on ${text ? "text" : "files"}`);
+            debug(`Running on ${useStdin ? "text" : "files"}`);
 
             if (currentOptions.fix && currentOptions.fixDryRun) {
                 log.error("The --fix option and the --fix-dry-run option cannot be used together.");
                 return 1;
             }
 
-            if (text && currentOptions.fix) {
+            if (useStdin && currentOptions.fix) {
                 log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead.");
                 return 1;
             }
 
             const engine = new CLIEngine(translateOptions(currentOptions));
 
-            const report = text ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
+            const report = useStdin ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files);
 
             if (currentOptions.fix) {
                 debug("Fix mode enabled - applying fixes");
diff --git a/lib/rules/multiline-comment-style.js b/lib/rules/multiline-comment-style.js
index 0972b7cc7..db4f76844 100644
--- a/lib/rules/multiline-comment-style.js
+++ b/lib/rules/multiline-comment-style.js
@@ -125,10 +125,12 @@ module.exports = {
                         },
                         message: EXPECTED_BLOCK_ERROR,
                         fix(fixer) {
-                            return fixer.replaceTextRange(
-                                [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]],
-                                `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`
-                            );
+                            const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
+                            const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
+
+                            return commentLines.some(value => value.startsWith("/"))
+                                ? null
+                                : fixer.replaceTextRange(range, starredBlock);
                         }
                     });
                 } else {
@@ -269,10 +271,13 @@ module.exports = {
                         return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
                     })
                     .reduce((commentGroups, comment, index, commentList) => {
+                        const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
+
                         if (
                             comment.type === "Line" &&
                             index && commentList[index - 1].type === "Line" &&
-                            sourceCode.getTokenBefore(comment, { includeComments: true }) === commentList[index - 1]
+                            tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
+                            tokenBefore === commentList[index - 1]
                         ) {
                             commentGroups[commentGroups.length - 1].push(comment);
                         } else {
diff --git a/lib/rules/no-var.js b/lib/rules/no-var.js
index c74e0b9ad..d3c163e55 100644
--- a/lib/rules/no-var.js
+++ b/lib/rules/no-var.js
@@ -15,6 +15,15 @@ const astUtils = require("../ast-utils");
 // Helpers
 //------------------------------------------------------------------------------
 
+/**
+ * Check whether a given variable is a global variable or not.
+ * @param {eslint-scope.Variable} variable The variable to check.
+ * @returns {boolean} `true` if the variable is a global variable.
+ */
+function isGlobal(variable) {
+    return Boolean(variable.scope) && variable.scope.type === "global";
+}
+
 /**
  * Finds the nearest function scope or global scope walking up the scope
  * hierarchy.
@@ -203,6 +212,7 @@ module.exports = {
          * Checks whether it can fix a given variable declaration or not.
          * It cannot fix if the following cases:
          *
+         * - A variable is a global variable.
          * - A variable is declared on a SwitchCase node.
          * - A variable is redeclared.
          * - A variable is used from outside the scope.
@@ -256,6 +266,7 @@ module.exports = {
 
             if (node.parent.type === "SwitchCase" ||
                 node.declarations.some(hasSelfReferenceInTDZ) ||
+                variables.some(isGlobal) ||
                 variables.some(isRedeclared) ||
                 variables.some(isUsedFromOutsideOf(scopeNode))
             ) {
diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js
index d4ee4e073..ea60b115c 100644
--- a/lib/rules/valid-jsdoc.js
+++ b/lib/rules/valid-jsdoc.js
@@ -226,8 +226,10 @@ module.exports = {
         function checkJSDoc(node) {
             const jsdocNode = sourceCode.getJSDocComment(node),
                 functionData = fns.pop(),
-                params = Object.create(null);
+                params = Object.create(null),
+                paramsTags = [];
             let hasReturns = false,
+                returnsTag,
                 hasConstructor = false,
                 isInterface = false,
                 isOverride = false,
@@ -261,43 +263,13 @@ module.exports = {
                         case "param":
                         case "arg":
                         case "argument":
-                            if (!tag.type) {
-                                context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: tag.name } });
-                            }
-
-                            if (!tag.description && requireParamDescription) {
-                                context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: tag.name } });
-                            }
-
-                            if (params[tag.name]) {
-                                context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: tag.name } });
-                            } else if (tag.name.indexOf(".") === -1) {
-                                params[tag.name] = 1;
-                            }
+                            paramsTags.push(tag);
                             break;
 
                         case "return":
                         case "returns":
                             hasReturns = true;
-
-                            if (!requireReturn && !functionData.returnPresent && (tag.type === null || !isValidReturnType(tag)) && !isAbstract) {
-                                context.report({
-                                    node: jsdocNode,
-                                    message: "Unexpected @{{title}} tag; function has no return statement.",
-                                    data: {
-                                        title: tag.title
-                                    }
-                                });
-                            } else {
-                                if (requireReturnType && !tag.type) {
-                                    context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
-                                }
-
-                                if (!isValidReturnType(tag) && !tag.description && requireReturnDescription) {
-                                    context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
-                                }
-                            }
-
+                            returnsTag = tag;
                             break;
 
                         case "constructor":
@@ -333,6 +305,40 @@ module.exports = {
                     }
                 });
 
+                paramsTags.forEach(param => {
+                    if (!param.type) {
+                        context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: param.name } });
+                    }
+                    if (!param.description && requireParamDescription) {
+                        context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: param.name } });
+                    }
+                    if (params[param.name]) {
+                        context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: param.name } });
+                    } else if (param.name.indexOf(".") === -1) {
+                        params[param.name] = 1;
+                    }
+                });
+
+                if (hasReturns) {
+                    if (!requireReturn && !functionData.returnPresent && (returnsTag.type === null || !isValidReturnType(returnsTag)) && !isAbstract) {
+                        context.report({
+                            node: jsdocNode,
+                            message: "Unexpected @{{title}} tag; function has no return statement.",
+                            data: {
+                                title: returnsTag.title
+                            }
+                        });
+                    } else {
+                        if (requireReturnType && !returnsTag.type) {
+                            context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
+                        }
+
+                        if (!isValidReturnType(returnsTag) && !returnsTag.description && requireReturnDescription) {
+                            context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
+                        }
+                    }
+                }
+
                 // check for functions missing @returns
                 if (!isOverride && !hasReturns && !hasConstructor && !isInterface &&
                     node.parent.kind !== "get" && node.parent.kind !== "constructor" &&
diff --git a/package.json b/package.json
index 9bc6d9ed4..6db06e806 100644
--- a/package.json
+++ b/package.json
@@ -20,8 +20,7 @@
     "browserify": "node Makefile.js browserify",
     "perf": "node Makefile.js perf",
     "profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree",
-    "coveralls": "cat ./coverage/lcov.info | coveralls",
-    "check-commit": "node Makefile.js checkGitCommit"
+    "coveralls": "cat ./coverage/lcov.info | coveralls"
   },
   "files": [
     "LICENSE",
diff --git a/templates/issue-create.md.ejs b/templates/issue-create.md.ejs
deleted file mode 100644
index 71296ea5f..000000000
--- a/templates/issue-create.md.ejs
+++ /dev/null
@@ -1,8 +0,0 @@
-@<%= payload.sender.login %> Thanks for the issue! If you're reporting a bug, please be sure to include:
-
-1. The version of ESLint you are using (run `eslint -v`)
-2. What you did (the source code and ESLint configuration)
-3. The actual ESLint output complete with numbers
-4. What you expected to happen instead
-
-Requesting a new rule? Please see [Proposing a New Rule](https://eslint.org/docs/developer-guide/contributing/new-rules) for instructions.
diff --git a/templates/needs-info.md.ejs b/templates/needs-info.md.ejs
deleted file mode 100644
index d1eb6d4a5..000000000
--- a/templates/needs-info.md.ejs
+++ /dev/null
@@ -1,14 +0,0 @@
-Hi @<%= payload.issue.user.login %>, thanks for the issue. It looks like there's not enough information for us to know how to help you.
-
-If you're reporting a bug, please be sure to include:
-
-1. The version of ESLint you are using (run `eslint -v`)
-2. What you did (the source code and ESLint configuration)
-3. The actual ESLint output complete with numbers
-4. What you expected to happen instead
-
-Requesting a new rule? Please see [Proposing a New Rule](https://eslint.org/docs/developer-guide/contributing/new-rules) for instructions.
-
-Requesting a rule change? Please see [Proposing a Rule Change](https://eslint.org/docs/developer-guide/contributing/rule-changes) for instructions.
-
-If it's something else, please just provide as much additional information as possible. Thanks!
diff --git a/templates/pr-create.md.ejs b/templates/pr-create.md.ejs
deleted file mode 100644
index 1ff9e16b7..000000000
--- a/templates/pr-create.md.ejs
+++ /dev/null
@@ -1,39 +0,0 @@
-<%
-
-var FLAG_PATTERN = /^(Breaking|Build|Chore|Docs|Fix|New|Update|Upgrade):/,
-    ISSUE_REF_PATTERN = /\((fixes|refs) #\d+.*?\)$/;
-
-function isValidCommitFlag(log) {
-    var result = log.match(FLAG_PATTERN);
-    return !!result || log.indexOf("Revert \"") === 0;
-}
-
-var problems = [];
-
-// Check for one commit per pull request
-if (meta.commits) {
-    // get just the first line of the commit message
-    var log = meta.commits[0].commit.message.split(/\r?\n/g)[0];
-
-    if (!isValidCommitFlag(log)) {
-        problems.push("The commit summary needs to begin with a tag (such as `Fix:` or `Update:`). Please check out our [guide](https://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) for how to properly format your commit summary and [update](https://eslint.org/docs/developer-guide/contributing/pull-requests#updating-the-commit-message) it on this pull request.")
-    }
-
-    if (log.length > 72) {
-        problems.push("The commit summary must be 72 characters or shorter. Please check out our [guide](https://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) for how to properly format your commit summary and [update](https://eslint.org/docs/developer-guide/contributing/pull-requests#updating-the-commit-message) it on this pull request.");
-    }
-}
-
-if (problems.length) { %>
-Thanks for the pull request, @<%= payload.sender.login %>! I took a look to make sure it's ready for merging and found some changes are needed:
-
-<% problems.forEach(function(problem) { %>
-* <%- problem %>
-<% }); %>
-
-Can you please update the pull request to address these?
-
-(More information can be found in our [pull request guide](https://eslint.org/docs/developer-guide/contributing/pull-requests).)
-<% } else { %>
-LGTM
-<% } %>


Test patch:
diff --git a/tests/lib/cli.js b/tests/lib/cli.js
index b77cfcde7..8a8bc938a 100644
--- a/tests/lib/cli.js
+++ b/tests/lib/cli.js
@@ -101,6 +101,13 @@ describe("cli", () => {
             assert.strictEqual(result, 1);
         });
 
+        it("should not print debug info when passed the empty string as text", () => {
+            const result = cli.execute(["--stdin", "--no-eslintrc"], "");
+
+            assert.strictEqual(result, 0);
+            assert.isTrue(log.info.notCalled);
+        });
+
         it("should return no error when --ext .js2 is specified", () => {
             const filePath = getFixturePath("files");
             const result = cli.execute(`--ext .js2 ${filePath}`);
diff --git a/tests/lib/rules/multiline-comment-style.js b/tests/lib/rules/multiline-comment-style.js
index 9da55a995..7f0cb4250 100644
--- a/tests/lib/rules/multiline-comment-style.js
+++ b/tests/lib/rules/multiline-comment-style.js
@@ -151,6 +151,30 @@ ruleTester.run("multiline-comment-style", rule, {
             `,
             options: ["separate-lines"]
         },
+        {
+            code: `
+                // a comment
+
+                // another comment
+            `,
+            options: ["separate-lines"]
+        },
+        {
+            code: `
+                // a comment
+
+                // another comment
+            `,
+            options: ["bare-block"]
+        },
+        {
+            code: `
+                // a comment
+
+                // another comment
+            `,
+            options: ["starred-block"]
+        },
         {
             code: `
                 /* eslint semi: "error" */
@@ -230,6 +254,35 @@ ruleTester.run("multiline-comment-style", rule, {
             `,
             errors: [{ message: EXPECTED_BLOCK_ERROR, line: 2 }]
         },
+        {
+            code: `
+                //foo
+                ///bar
+            `,
+            output: null,
+            errors: [{ message: EXPECTED_BLOCK_ERROR, line: 2 }]
+        },
+        {
+            code: `
+                // foo
+                // bar
+
+                // baz
+                // qux
+            `,
+            output: `
+                /*
+                 * foo
+                 * bar
+                 */
+
+                /*
+                 * baz
+                 * qux
+                 */
+            `,
+            errors: [{ message: EXPECTED_BLOCK_ERROR, line: 2 }, { message: EXPECTED_BLOCK_ERROR, line: 5 }]
+        },
         {
             code: `
                 /* this block
diff --git a/tests/lib/rules/no-var.js b/tests/lib/rules/no-var.js
index 830104180..48511d825 100644
--- a/tests/lib/rules/no-var.js
+++ b/tests/lib/rules/no-var.js
@@ -21,13 +21,22 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 ruleTester.run("no-var", rule, {
     valid: [
         "const JOE = 'schmoe';",
-        "let moo = 'car';"
+        "let moo = 'car';",
+        {
+            code: "const JOE = 'schmoe';",
+            parserOptions: { ecmaFeatures: { globalReturn: true } }
+        },
+        {
+            code: "let moo = 'car';",
+            parserOptions: { ecmaFeatures: { globalReturn: true } }
+        }
     ],
 
     invalid: [
         {
             code: "var foo = bar;",
             output: "let foo = bar;",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -38,6 +47,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var foo = bar, toast = most;",
             output: "let foo = bar, toast = most;",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -48,6 +58,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var foo = bar; let toast = most;",
             output: "let foo = bar; let toast = most;",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -58,6 +69,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var a of b) { console.log(a); }",
             output: "for (let a of b) { console.log(a); }",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -68,6 +80,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var a in b) { console.log(a); }",
             output: "for (let a in b) { console.log(a); }",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -78,6 +91,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (let a of b) { var c = 1; console.log(c); }",
             output: "for (let a of b) { let c = 1; console.log(c); }",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 {
                     message: "Unexpected var, use let or const instead.",
@@ -88,6 +102,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var i = 0; i < list.length; ++i) { foo(i) }",
             output: "for (let i = 0; i < list.length; ++i) { foo(i) }",
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 { message: "Unexpected var, use let or const instead.", type: "VariableDeclaration" }
             ]
@@ -95,6 +110,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var i = 0, i = 0; false;);",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 { message: "Unexpected var, use let or const instead.", type: "VariableDeclaration" }
             ]
@@ -102,6 +118,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var i = 0; for (var i = 1; false;); console.log(i);",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 { message: "Unexpected var, use let or const instead.", type: "VariableDeclaration" },
                 { message: "Unexpected var, use let or const instead.", type: "VariableDeclaration" }
@@ -112,6 +129,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var a, b, c; var a;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead.",
                 "Unexpected var, use let or const instead."
@@ -120,6 +138,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var a; if (b) { var a; }",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead.",
                 "Unexpected var, use let or const instead."
@@ -128,6 +147,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "if (foo) { var a, b, c; } a;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -135,6 +155,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var i = 0; i < 10; ++i) {} i;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -142,6 +163,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var a in obj) {} a;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -149,6 +171,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var a of list) {} a;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -156,6 +179,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "switch (a) { case 0: var b = 1 }",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -165,6 +189,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (var a of b) { arr.push(() => a); }",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -172,6 +197,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "for (let a of b) { var c; console.log(c); c = 'hello'; }",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -181,6 +207,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var a = a",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -188,7 +215,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var {a = a} = {}",
             output: null,
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -196,7 +223,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var {a = b, b} = {}",
             output: null,
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -204,7 +231,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var {a, b = a} = {}",
             output: "let {a, b = a} = {}",
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -212,7 +239,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var a = b, b = 1",
             output: null,
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -220,7 +247,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "var a = b; var b = 1",
             output: "let a = b; var b = 1",
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead.",
                 "Unexpected var, use let or const instead."
@@ -232,7 +259,7 @@ ruleTester.run("no-var", rule, {
         {
             code: "function foo() { a } var a = 1; foo()",
             output: null,
-            parserOptions: { ecmaVersion: 2015 },
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
             errors: [
                 "Unexpected var, use let or const instead."
             ]
@@ -242,9 +269,33 @@ ruleTester.run("no-var", rule, {
         {
             code: "if (foo) var bar = 1;",
             output: null,
+            parserOptions: { ecmaFeatures: { globalReturn: true } },
             errors: [
                 { message: "Unexpected var, use let or const instead.", type: "VariableDeclaration" }
             ]
+        },
+
+        // https://github.com/eslint/eslint/issues/9520
+        {
+            code: "var foo = 1",
+            output: null,
+            errors: ["Unexpected var, use let or const instead."]
+        },
+        {
+            code: "{ var foo = 1 }",
+            output: null,
+            errors: ["Unexpected var, use let or const instead."]
+        },
+        {
+            code: "if (true) { var foo = 1 }",
+            output: null,
+            errors: ["Unexpected var, use let or const instead."]
+        },
+        {
+            code: "var foo = 1",
+            output: "let foo = 1",
+            parserOptions: { sourceType: "module" },
+            errors: ["Unexpected var, use let or const instead."]
         }
     ]
 });
diff --git a/tests/lib/rules/object-property-newline.js b/tests/lib/rules/object-property-newline.js
index dea82a26a..350ecfb17 100644
--- a/tests/lib/rules/object-property-newline.js
+++ b/tests/lib/rules/object-property-newline.js
@@ -56,7 +56,10 @@ ruleTester.run("object-property-newline", rule, {
         { code: "foo({ bar() {}, baz });", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6 } },
         { code: "foo({ [bar]: 'baz', baz })", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6 } },
         { code: "foo({ 'k1': 'val1', k2: 'val2', ...{} });", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
-        { code: "foo({\n'k1': 'val1', k2: 'val2', ...{}\n});", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } }
+        { code: "foo({\n'k1': 'val1', k2: 'val2', ...{}\n});", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
+        { code: "var obj = {k1: ['foo', 'bar'], k2: 'val1', k3: 'val2'};", options: [{ allowMultiplePropertiesPerLine: true }] },
+        { code: "var obj = {\nk1: ['foo', 'bar'], k2: 'val1', k3: 'val2'\n};", options: [{ allowMultiplePropertiesPerLine: true }] },
+        { code: "var obj = {\nk1: 'val1', k2: {e1: 'foo', e2: 'bar'}, k3: 'val2'\n};", options: [{ allowMultiplePropertiesPerLine: true }] }
     ],
 
     invalid: [
@@ -110,6 +113,66 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var obj = {k1: ['foo', 'bar'], k2: 'val1'};",
+            output: "var obj = {k1: ['foo', 'bar'],\nk2: 'val1'};",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 1,
+                    column: 32
+                }
+            ]
+        },
+        {
+            code: "var obj = {k1: [\n'foo', 'bar'\n], k2: 'val1'};",
+            output: "var obj = {k1: [\n'foo', 'bar'\n],\nk2: 'val1'};",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 3,
+                    column: 4
+                }
+            ]
+        },
+        {
+            code: "var obj = {\nk1: 'val1', k2: {e1: 'foo', e2: 'bar'}, k3: 'val2'\n};",
+            output: "var obj = {\nk1: 'val1',\nk2: {e1: 'foo',\ne2: 'bar'},\nk3: 'val2'\n};",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 2,
+                    column: 13
+                },
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 2,
+                    column: 29
+                },
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 2,
+                    column: 41
+                }
+            ]
+        },
+        {
+            code: "var obj = {\nk1: 'val1',\nk2: {e1: 'foo', e2: 'bar'},\nk3: 'val2'\n};",
+            output: "var obj = {\nk1: 'val1',\nk2: {e1: 'foo',\ne2: 'bar'},\nk3: 'val2'\n};",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
         {
             code: "var obj = { k1: 'val1', [\nk2]: 'val2' };",
             output: "var obj = { k1: 'val1',\n[\nk2]: 'val2' };",
@@ -276,6 +339,30 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "({ foo: 1 /* comment */, bar: 2 })",
+            output: "({ foo: 1 /* comment */,\nbar: 2 })",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 1,
+                    column: 26
+                }
+            ]
+        },
+        {
+            code: "({ foo: 1, /* comment */ bar: 2 })",
+            output: null, // not fixed due to comment
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 1,
+                    column: 26
+                }
+            ]
+        },
 
         // allowMultiplePropertiesPerLine: true
         {
@@ -291,6 +378,51 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var obj = {k1: [\n'foo',\n'bar'\n], k2: 'val1'};",
+            output: "var obj = {k1: [\n'foo',\n'bar'\n],\nk2: 'val1'};",
+            options: [{ allowMultiplePropertiesPerLine: true }],
+            errors: [
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 4,
+                    column: 4
+                }
+            ]
+        },
+        {
+            code: "var obj = {k1: [\n'foo', 'bar'\n], k2: 'val1'};",
+            output: "var obj = {k1: [\n'foo', 'bar'\n],\nk2: 'val1'};",
+            options: [{ allowMultiplePropertiesPerLine: true }],
+            errors: [
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 3,
+                    column: 4
+                }
+            ]
+        },
+        {
+            code: "var obj = {\nk1: 'val1', k2: {\ne1: 'foo', e2: 'bar'\n}, k3: 'val2'\n};",
+            output: "var obj = {\nk1: 'val1',\nk2: {\ne1: 'foo', e2: 'bar'\n},\nk3: 'val2'\n};",
+            options: [{ allowMultiplePropertiesPerLine: true }],
+            errors: [
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 2,
+                    column: 13
+                },
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 4,
+                    column: 4
+                }
+            ]
+        },
         {
             code: "var obj = { [\nk1]: 'val1', k2: 'val2' };",
             output: "var obj = { [\nk1]: 'val1',\nk2: 'val2' };",
@@ -374,30 +506,6 @@ ruleTester.run("object-property-newline", rule, {
                     column: 13
                 }
             ]
-        },
-        {
-            code: "({ foo: 1 /* comment */, bar: 2 })",
-            output: "({ foo: 1 /* comment */,\nbar: 2 })",
-            errors: [
-                {
-                    message: "Object properties must go on a new line.",
-                    type: "ObjectExpression",
-                    line: 1,
-                    column: 26
-                }
-            ]
-        },
-        {
-            code: "({ foo: 1, /* comment */ bar: 2 })",
-            output: null, // not fixed due to comment
-            errors: [
-                {
-                    message: "Object properties must go on a new line.",
-                    type: "ObjectExpression",
-                    line: 1,
-                    column: 26
-                }
-            ]
         }
     ]
 });
diff --git a/tests/lib/rules/valid-jsdoc.js b/tests/lib/rules/valid-jsdoc.js
index e9519278a..54fea8d8f 100644
--- a/tests/lib/rules/valid-jsdoc.js
+++ b/tests/lib/rules/valid-jsdoc.js
@@ -499,6 +499,434 @@ ruleTester.run("valid-jsdoc", rule, {
             "function foo(){ throw new Error('Not Implemented'); }",
             options: [{ requireReturn: false }]
         },
+
+        // https://github.com/eslint/eslint/issues/9412 - different orders for jsodc tags
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @returns {Number} desc\n" +
+            "* @class \n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @returns {Number} desc\n" +
+            "* @class \n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @argument {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @returns {Number} desc\n" +
+            "* @class \n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @argument {string} hi - desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @class \n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @class \n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @constructor\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @constructor\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @class\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @inheritdoc\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @class \n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor\n" +
+            "* @override\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "* @override\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @override\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor\n" +
+            "* @inheritdoc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "* @inheritdoc\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @inheritdoc\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor\n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @abstract\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @interface\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor\n" +
+            "* @override\n" +
+            "* @virtual\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @interface\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "* @override\n" +
+            "* @virtual\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @virtual\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @param {string} hi - desc\n" +
+            "* @return {Number} desc\n" +
+            "* @constructor \n" +
+            "* @override\n" +
+            "* @abstract\n" +
+            "* @interface\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @arg {string} hi - desc\n" +
+            "* @returns {Number} desc\n" +
+            "* @class\n" +
+            "* @override\n" +
+            "* @virtual\n" +
+            "* @interface\n" +
+            "*/\n" +
+            "function foo(hi){ return 1; }",
+            options: [{ requireReturn: false }]
+        },
+        {
+            code:
+            "/**\n" +
+            "* Description\n" +
+            "* @argument {string} hi - desc\n" +
+            "* @interface\n" +
+            "*/\n" +
+            "function foo(hi){}",
+            options: [{ requireReturn: false }]
+        },
         {
             code:
             "/**\n" +
diff --git a/tests/templates/pr-create.md.ejs.js b/tests/templates/pr-create.md.ejs.js
deleted file mode 100644
index 974fbf188..000000000
--- a/tests/templates/pr-create.md.ejs.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * @fileoverview Tests for pr-create.md.ejs
- * @author Nicholas C. Zakas
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert,
-    fs = require("fs"),
-    path = require("path"),
-    ejs = require("ejs");
-
-//------------------------------------------------------------------------------
-// Data
-//------------------------------------------------------------------------------
-
-const TEMPLATE_TEXT = fs.readFileSync(path.resolve(__dirname, "../../templates/pr-create.md.ejs"), "utf8");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("pr-create.md.ejs", () => {
-
-    it("should say LGTM when there are no problems with the pull request", () => {
-        const result = ejs.render(TEMPLATE_TEXT, {
-            payload: {
-                sender: {
-                    login: "nzakas"
-                },
-                commits: 1
-            },
-            meta: {}
-        });
-
-        assert.strictEqual(result.trim(), "LGTM");
-    });
-
-    it("should mention commit message format when there's one commit and an invalid commit message is found", () => {
-        const result = ejs.render(TEMPLATE_TEXT, {
-            payload: {
-                sender: {
-                    login: "nzakas"
-                },
-                commits: 1
-            },
-            meta: {
-                cla: true,
-                commits: [
-                    {
-                        commit: {
-                            message: "Foo bar"
-                        }
-                    }
-                ]
-            }
-        });
-
-        assert.ok(result.indexOf("begin with a tag") > -1);
-    });
-
-    it("should mention commit message length when there's a message longer than 72 characters", () => {
-        const result = ejs.render(TEMPLATE_TEXT, {
-            payload: {
-                sender: {
-                    login: "nzakas"
-                },
-                commits: 1
-            },
-            meta: {
-                cla: true,
-                commits: [
-                    {
-                        commit: {
-                            message: "Fix:56789012345678901234567890123456789012345678901234567890(fixes #9012)"
-                        }
-                    }
-                ]
-            }
-        });
-
-        assert.ok(result.indexOf("72 characters") > -1);
-    });
-
-    it("should not mention commit message length when there's a multi-line message with first line not over 72 characters", () => {
-        const result = ejs.render(TEMPLATE_TEXT, {
-            payload: {
-                sender: {
-                    login: "nzakas"
-                },
-                commits: 1
-            },
-            meta: {
-                cla: true,
-                commits: [
-                    {
-                        commit: {
-                            message: "Fix:56789012345678901234567890123456789012345678901234567890(fixes #901)\r\n1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
-                        }
-                    }
-                ]
-            }
-        });
-
-        assert.strictEqual(result.trim(), "LGTM");
-    });
-
-    it("should not mention missing issue when there's one documentation commit", () => {
-        const result = ejs.render(TEMPLATE_TEXT, {
-            payload: {
-                sender: {
-                    login: "nzakas"
-                },
-                commits: 1
-            },
-            meta: {
-                cla: true,
-                commits: [
-                    {
-                        commit: {
-                            message: "Docs: Foo bar"
-                        }
-                    }
-                ]
-            }
-        });
-
-        assert.strictEqual(result.trim(), "LGTM");
-    });
-
-    ["Breaking", "Build", "Chore", "Docs", "Fix", "New", "Update", "Upgrade"].forEach(type => {
-        it(`should not mention missing issue or length check when there's one ${type} commit`, () => {
-            const result = ejs.render(TEMPLATE_TEXT, {
-                payload: {
-                    sender: {
-                        login: "nzakas"
-                    },
-                    commits: 1
-                },
-                meta: {
-                    cla: true,
-                    commits: [
-                        {
-                            commit: {
-                                message: `${type}: Foo bar (fixes #1234)\nSome really long string. abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz`
-                            }
-                        }
-                    ]
-                }
-            });
-
-            assert.strictEqual(result.trim(), "LGTM");
-        });
-    });
-
-});