Repository: eslint/eslint (js)

Issue:
Remove fixer from no-debugger rule
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for requesting a rule change. If you are here for another reason, please see below:

    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
    2. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**What rule do you want to change?**
no-debugger

**Does this change cause the rule to produce more or fewer warnings?**

Fixer for this rule forces me to put `eslint-disable-next-line` comment before `debugger` statement rending this rule useless.

With fixer for no-debugger my IDE's (VS Code and vim) fix-on-save feature removes debugger statement the moment I save the file. I have to use 
```js
// eslint-disable-next-line
debugger
```
construction and I already have debugger statement slipped into production code at least twice.

**How will the change be implemented? (New option, new default behavior, etc.)?**

Please remove the fixer completely or make it optional. I've read the comment https://github.com/eslint/eslint/pull/8660#issuecomment-315806299 and I still think that for no-debugger rule fixer is kind of useless. You'll never have more that one or two accidentally forgotten statements to force IDE creators to implement such mechanism.

Pull request:
Fix: Remove autofixer for no-debugger (fixes #10242)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Bug fix (see https://github.com/eslint/eslint/issues/10242)

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

This removes the autofixer for `no-debugger`, as discussed in https://github.com/eslint/eslint/issues/10242.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97fa0bcb9..97bca968c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+v5.0.1 - June 25, 2018
+
+* 196c102 Fix: valid-jsdoc should allow optional returns for async (fixes #10386) (#10480) (Mark Banner)
+* 4c823bd Docs: Fix max-lines-per-function correct code's max value (#10513) (Rhys Bower)
+
+v5.0.0 - June 22, 2018
+
+* 0feedfd New: Added max-lines-per-function rule (fixes #9842) (#10188) (peteward44)
+* daefbdb Upgrade: eslint-scope and espree to 4.0.0 (refs #10458) (#10500) (Brandon Mills)
+* 077358b Docs: no-process-exit: recommend process.exitCode (#10478) (Andres Kalle)
+* f93d6ff Fix: do not fail on unknown operators from custom parsers (fixes #10475) (#10476) (Rubén Norte)
+* 05343fd Fix: add parens for yield statement (fixes #10432) (#10468) (Pig Fang)
+* d477c5e Fix: check destructuring for "no-shadow-restricted-names" (fixes #10467) (#10470) (Pig Fang)
+* 7a7580b Update: Add considerPropertyDescriptor option to func-name-matching (#9078) (Dieter Luypaert)
+* e0a0418 Fix: crash on optional catch binding (#10429) (Toru Nagashima)
+* de4dba9 Docs: styling team members (#10460) (薛定谔的猫)
+* 5e453a3 Docs: display team members in tables. (#10433) (薛定谔的猫)
+* b1895eb Docs: Restore intentional spelling mistake (#10459) (Wilfred Hughes)
+
 v5.0.0-rc.0 - June 9, 2018
 
 * abf400d Update: Add ignoreDestructing option to camelcase rule (fixes #9807) (#10373) (Andrew Lunny)
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 4b4ed0ae8..28d7aa4f9 100644
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -69,6 +69,7 @@ module.exports = {
         "max-depth": "off",
         "max-len": "off",
         "max-lines": "off",
+        "max-lines-per-function": "off",
         "max-nested-callbacks": "off",
         "max-params": "off",
         "max-statements": "off",
diff --git a/docs/rules/complexity.md b/docs/rules/complexity.md
index 4f4654b9d..e70a8989a 100644
--- a/docs/rules/complexity.md
+++ b/docs/rules/complexity.md
@@ -80,6 +80,8 @@ If you can't determine an appropriate complexity limit for your code, then it's
 
 * [max-depth](max-depth.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-params](max-params.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-depth.md b/docs/rules/max-depth.md
index 32f2a4a6d..8eed0912f 100644
--- a/docs/rules/max-depth.md
+++ b/docs/rules/max-depth.md
@@ -58,6 +58,8 @@ function foo() {
 
 * [complexity](complexity.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-params](max-params.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-lines-per-function.md b/docs/rules/max-lines-per-function.md
new file mode 100644
index 000000000..8ce0e313e
--- /dev/null
+++ b/docs/rules/max-lines-per-function.md
@@ -0,0 +1,191 @@
+# enforce a maximum function length (max-lines-per-function)
+
+Some people consider large functions a code smell. Large functions tend to do a lot of things and can make it hard following what's going on. Many coding style guides dictate a limit of the number of lines that a function can comprise of. This rule can help enforce that style.
+
+## Rule Details
+
+This rule enforces a maximum number of lines per function, in order to aid in maintainability and reduce complexity.
+
+## Why not use `max-statements` or other complexity measurement rules instead?
+
+Nested long method chains like the below example are often broken onto separate lines for readability:
+
+```
+function() {
+    return m("div", [
+        m("table", {className: "table table-striped latest-data"}, [
+            m("tbody",
+                data.map(function(db) {
+                    return m("tr", {key: db.dbname}, [
+                        m("td", {className: "dbname"}, db.dbname),
+                        m("td", {className: "query-count"},  [
+                            m("span", {className: db.lastSample.countClassName}, db.lastSample.nbQueries)
+                        ])
+                    ])
+                })
+            )
+        ])
+    ])
+}
+```
+
+* `max-statements` will only report this as 1 statement, despite being 16 lines of code.
+* `complexity` will only report a complexity of 1
+* `max-nested-callbacks` will only report 1
+* `max-depth` will report a depth of 0
+
+## Options
+
+This rule has the following options that can be specified using an object:
+
+* `"max"` (default `50`) enforces a maximum number of lines in a function.
+
+* `"skipBlankLines": true` ignore lines made up purely of whitespace.
+
+* `"skipComments": true` ignore lines containing just comments.
+
+* `"IIFEs": true` include any code included in IIFEs.
+
+Alternatively, you may specify a single integer for the `max` option:
+
+```json
+"max-lines-per-function": ["error", 20]
+```
+
+is equivalent to
+
+```json
+"max-lines-per-function": ["error", { "max": 20 }]
+```
+
+### code
+
+Examples of **incorrect** code for this rule with a max value of `2`:
+
+```js
+/*eslint max-lines-per-function: ["error", 2]*/
+function foo() {
+    var x = 0;
+}
+```
+
+```js
+/*eslint max-lines-per-function: ["error", 2]*/
+function foo() {
+    // a comment
+    var x = 0;
+}
+```
+
+```js
+/*eslint max-lines-per-function: ["error", 2]*/
+function foo() {
+    // a comment followed by a blank line
+
+    var x = 0;
+}
+```
+
+Examples of **correct** code for this rule with a max value of `3`:
+
+```js
+/*eslint max-lines-per-function: ["error", 3]*/
+function foo() {
+    var x = 0;
+}
+```
+
+```js
+/*eslint max-lines-per-function: ["error", 3]*/
+function foo() {
+    // a comment
+    var x = 0;
+}
+```
+
+```js
+/*eslint max-lines-per-function: ["error", 3]*/
+function foo() {
+    // a comment followed by a blank line
+
+    var x = 0;
+}
+```
+
+### skipBlankLines
+
+Examples of **incorrect** code for this rule with the `{ "skipBlankLines": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 2, "skipBlankLines": true}]*/
+function foo() {
+
+    var x = 0;
+}
+```
+
+Examples of **correct** code for this rule with the `{ "skipBlankLines": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 3, "skipBlankLines": true}]*/
+function foo() {
+
+    var x = 0;
+}
+```
+
+### skipComments
+
+Examples of **incorrect** code for this rule with the `{ "skipComments": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 2, "skipComments": true}]*/
+function foo() {
+    // a comment
+    var x = 0;
+}
+```
+
+Examples of **correct** code for this rule with the `{ "skipComments": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 3, "skipComments": true}]*/
+function foo() {
+    // a comment
+    var x = 0;
+}
+```
+
+### IIFEs
+
+Examples of **incorrect** code for this rule with the `{ "IIFEs": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 2, "IIFEs": true}]*/
+(function(){
+    var x = 0;
+}());
+```
+
+Examples of **correct** code for this rule with the `{ "IIFEs": true }` option:
+
+```js
+/*eslint max-lines-per-function: ["error", {"max": 3, "IIFEs": true}]*/
+(function(){
+    var x = 0;
+}());
+```
+
+## When Not To Use It
+
+You can turn this rule off if you are not concerned with the number of lines in your functions.
+
+## Related Rules
+
+* [complexity](complexity.md)
+* [max-depth](max-depth.md)
+* [max-lines](max-lines.md)
+* [max-nested-callbacks](max-nested-callbacks.md)
+* [max-params](max-params.md)
+* [max-statements](max-statements.md)
+* [max-statements-per-line](max-statements-per-line.md)
diff --git a/docs/rules/max-lines.md b/docs/rules/max-lines.md
index d0a6f2ffb..097804570 100644
--- a/docs/rules/max-lines.md
+++ b/docs/rules/max-lines.md
@@ -116,6 +116,7 @@ You can turn this rule off if you are not concerned with the number of lines in
 
 * [complexity](complexity.md)
 * [max-depth](max-depth.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-params](max-params.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-nested-callbacks.md b/docs/rules/max-nested-callbacks.md
index 8c1f9c230..24e4b907f 100644
--- a/docs/rules/max-nested-callbacks.md
+++ b/docs/rules/max-nested-callbacks.md
@@ -79,5 +79,7 @@ function handleFoo4() {
 * [complexity](complexity.md)
 * [max-depth](max-depth.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-params](max-params.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-params.md b/docs/rules/max-params.md
index 5c12b9d7d..050d2a348 100644
--- a/docs/rules/max-params.md
+++ b/docs/rules/max-params.md
@@ -57,5 +57,7 @@ let foo = (bar, baz, qux) => {
 * [complexity](complexity.md)
 * [max-depth](max-depth.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-statements-per-line.md b/docs/rules/max-statements-per-line.md
index d7f51ec53..e8462b664 100644
--- a/docs/rules/max-statements-per-line.md
+++ b/docs/rules/max-statements-per-line.md
@@ -80,6 +80,8 @@ You can turn this rule off if you are not concerned with the number of statement
 
 * [max-depth](max-depth.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-params](max-params.md)
 * [max-statements](max-statements.md)
diff --git a/docs/rules/max-statements.md b/docs/rules/max-statements.md
index 3d03e1d57..6ef87c2f8 100644
--- a/docs/rules/max-statements.md
+++ b/docs/rules/max-statements.md
@@ -139,5 +139,7 @@ function foo() {
 * [complexity](complexity.md)
 * [max-depth](max-depth.md)
 * [max-len](max-len.md)
+* [max-lines](max-lines.md)
+* [max-lines-per-function](max-lines-per-function.md)
 * [max-nested-callbacks](max-nested-callbacks.md)
 * [max-params](max-params.md)
diff --git a/lib/rules/max-lines-per-function.js b/lib/rules/max-lines-per-function.js
new file mode 100644
index 000000000..b40c7ee88
--- /dev/null
+++ b/lib/rules/max-lines-per-function.js
@@ -0,0 +1,218 @@
+/**
+ * @fileoverview A rule to set the maximum number of line of code in a function.
+ * @author Pete Ward <peteward44@gmail.com>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+const OPTIONS_SCHEMA = {
+    type: "object",
+    properties: {
+        max: {
+            type: "integer",
+            minimum: 0
+        },
+        skipComments: {
+            type: "boolean"
+        },
+        skipBlankLines: {
+            type: "boolean"
+        },
+        IIFEs: {
+            type: "boolean"
+        }
+    },
+    additionalProperties: false
+};
+
+const OPTIONS_OR_INTEGER_SCHEMA = {
+    oneOf: [
+        OPTIONS_SCHEMA,
+        {
+            type: "integer",
+            minimum: 1
+        }
+    ]
+};
+
+/**
+ * Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
+ * @param {Array} comments An array of comment nodes.
+ * @returns {Map.<string,Node>} A map with numeric keys (source code line numbers) and comment token values.
+ */
+function getCommentLineNumbers(comments) {
+    const map = new Map();
+
+    if (!comments) {
+        return map;
+    }
+    comments.forEach(comment => {
+        for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
+            map.set(i, comment);
+        }
+    });
+    return map;
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "enforce a maximum number of line of code in a function",
+            category: "Stylistic Issues",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/max-lines-per-function"
+        },
+
+        schema: [
+            OPTIONS_OR_INTEGER_SCHEMA
+        ]
+    },
+
+    create(context) {
+        const sourceCode = context.getSourceCode();
+        const lines = sourceCode.lines;
+
+        const option = context.options[0];
+        let maxLines = 50;
+        let skipComments = false;
+        let skipBlankLines = false;
+        let IIFEs = false;
+
+        if (typeof option === "object") {
+            if (typeof option.max === "number") {
+                maxLines = option.max;
+            }
+            if (typeof option.skipComments === "boolean") {
+                skipComments = option.skipComments;
+            }
+            if (typeof option.skipBlankLines === "boolean") {
+                skipBlankLines = option.skipBlankLines;
+            }
+            if (typeof option.IIFEs === "boolean") {
+                IIFEs = option.IIFEs;
+            }
+        } else if (typeof option === "number") {
+            maxLines = option;
+        }
+
+        const commentLineNumbers = getCommentLineNumbers(sourceCode.getAllComments());
+
+        //--------------------------------------------------------------------------
+        // Helpers
+        //--------------------------------------------------------------------------
+
+        /**
+         * Tells if a comment encompasses the entire line.
+         * @param {string} line The source line with a trailing comment
+         * @param {number} lineNumber The one-indexed line number this is on
+         * @param {ASTNode} comment The comment to remove
+         * @returns {boolean} If the comment covers the entire line
+         */
+        function isFullLineComment(line, lineNumber, comment) {
+            const start = comment.loc.start,
+                end = comment.loc.end,
+                isFirstTokenOnLine = start.line === lineNumber && !line.slice(0, start.column).trim(),
+                isLastTokenOnLine = end.line === lineNumber && !line.slice(end.column).trim();
+
+            return comment &&
+                (start.line < lineNumber || isFirstTokenOnLine) &&
+                (end.line > lineNumber || isLastTokenOnLine);
+        }
+
+        /**
+         * Identifies is a node is a FunctionExpression which is part of an IIFE
+         * @param {ASTNode} node Node to test
+         * @returns {boolean} True if it's an IIFE
+         */
+        function isIIFE(node) {
+            return node.type === "FunctionExpression" && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
+        }
+
+        /**
+         * Identifies is a node is a FunctionExpression which is embedded within a MethodDefinition or Property
+         * @param {ASTNode} node Node to test
+         * @returns {boolean} True if it's a FunctionExpression embedded within a MethodDefinition or Property
+         */
+        function isEmbedded(node) {
+            if (!node.parent) {
+                return false;
+            }
+            if (node !== node.parent.value) {
+                return false;
+            }
+            if (node.parent.type === "MethodDefinition") {
+                return true;
+            }
+            if (node.parent.type === "Property") {
+                return node.parent.method === true || node.parent.kind === "get" || node.parent.kind === "set";
+            }
+            return false;
+        }
+
+        /**
+         * Count the lines in the function
+         * @param {ASTNode} funcNode Function AST node
+         * @returns {void}
+         * @private
+         */
+        function processFunction(funcNode) {
+            const node = isEmbedded(funcNode) ? funcNode.parent : funcNode;
+
+            if (!IIFEs && isIIFE(node)) {
+                return;
+            }
+            let lineCount = 0;
+
+            for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
+                const line = lines[i];
+
+                if (skipComments) {
+                    if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
+                        continue;
+                    }
+                }
+
+                if (skipBlankLines) {
+                    if (line.match(/^\s*$/)) {
+                        continue;
+                    }
+                }
+
+                lineCount++;
+            }
+
+            if (lineCount > maxLines) {
+                const name = astUtils.getFunctionNameWithKind(funcNode);
+
+                context.report({
+                    node,
+                    message: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}.",
+                    data: { name, lineCount, maxLines }
+                });
+            }
+        }
+
+        //--------------------------------------------------------------------------
+        // Public API
+        //--------------------------------------------------------------------------
+
+        return {
+            FunctionDeclaration: processFunction,
+            FunctionExpression: processFunction,
+            ArrowFunctionExpression: processFunction
+        };
+    }
+};
diff --git a/lib/rules/no-debugger.js b/lib/rules/no-debugger.js
index f00e819df..5d63bb7e1 100644
--- a/lib/rules/no-debugger.js
+++ b/lib/rules/no-debugger.js
@@ -5,8 +5,6 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
-
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -19,7 +17,7 @@ module.exports = {
             recommended: true,
             url: "https://eslint.org/docs/rules/no-debugger"
         },
-        fixable: "code",
+        fixable: null,
         schema: [],
         messages: {
             unexpected: "Unexpected 'debugger' statement."
@@ -32,13 +30,7 @@ module.exports = {
             DebuggerStatement(node) {
                 context.report({
                     node,
-                    messageId: "unexpected",
-                    fix(fixer) {
-                        if (astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
-                            return fixer.remove(node);
-                        }
-                        return null;
-                    }
+                    messageId: "unexpected"
                 });
             }
         };
diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js
index a8466135b..44c0f9999 100644
--- a/lib/rules/valid-jsdoc.js
+++ b/lib/rules/valid-jsdoc.js
@@ -408,7 +408,7 @@ module.exports = {
                 if (!isOverride && !hasReturns && !hasConstructor && !isInterface &&
                     node.parent.kind !== "get" && node.parent.kind !== "constructor" &&
                     node.parent.kind !== "set" && !isTypeClass(node)) {
-                    if (requireReturn || functionData.returnPresent) {
+                    if (requireReturn || (functionData.returnPresent && !node.async)) {
                         context.report({
                             node: jsdocNode,
                             message: "Missing JSDoc @{{returns}} for function.",
diff --git a/package.json b/package.json
index 65e3da925..d1b7f605e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "5.0.0-rc.0",
+  "version": "5.0.1",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {


Test patch:
diff --git a/tests/lib/rules/max-lines-per-function.js b/tests/lib/rules/max-lines-per-function.js
new file mode 100644
index 000000000..8bf8250f3
--- /dev/null
+++ b/tests/lib/rules/max-lines-per-function.js
@@ -0,0 +1,431 @@
+/**
+ * @fileoverview Tests for max-lines-per-function rule.
+ * @author Pete Ward <peteward44@gmail.com>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+const rule = require("../../../lib/rules/max-lines-per-function");
+const RuleTester = require("../../../lib/testers/rule-tester");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+
+ruleTester.run("max-lines-per-function", rule, {
+    valid: [
+
+        // Test code in global scope doesn't count
+        {
+            code: "var x = 5;\nvar x = 2;\n",
+            options: [1]
+        },
+
+        // Test single line standlone function
+        {
+            code: "function name() {}",
+            options: [1]
+        },
+
+        // Test standalone function with lines of code
+        {
+            code: "function name() {\nvar x = 5;\nvar x = 2;\n}",
+            options: [4]
+        },
+
+        // Test inline arrow function
+        {
+            code: "const bar = () => 2",
+            options: [1]
+        },
+
+        // Test arrow function
+        {
+            code: "const bar = () => {\nconst x = 2 + 1;\nreturn x;\n}",
+            options: [4]
+        },
+
+        // skipBlankLines: false with simple standalone function
+        {
+            code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 7, skipComments: false, skipBlankLines: false }]
+        },
+
+        // skipBlankLines: true with simple standalone function
+        {
+            code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 4, skipComments: false, skipBlankLines: true }]
+        },
+
+        // skipComments: true with an individual single line comment
+        {
+            code: "function name() {\nvar x = 5;\nvar x = 2; // end of line comment\n}",
+            options: [{ max: 4, skipComments: true, skipBlankLines: false }]
+        },
+
+        // skipComments: true with an individual single line comment
+        {
+            code: "function name() {\nvar x = 5;\n// a comment on it's own line\nvar x = 2; // end of line comment\n}",
+            options: [{ max: 4, skipComments: true, skipBlankLines: false }]
+        },
+
+        // skipComments: true with single line comments
+        {
+            code: "function name() {\nvar x = 5;\n// a comment on it's own line\n// and another line comment\nvar x = 2; // end of line comment\n}",
+            options: [{ max: 4, skipComments: true, skipBlankLines: false }]
+        },
+
+        // skipComments: true test with multiple different comment types
+        {
+            code: "function name() {\nvar x = 5;\n/* a \n multi \n line \n comment \n*/\n\nvar x = 2; // end of line comment\n}",
+            options: [{ max: 5, skipComments: true, skipBlankLines: false }]
+        },
+
+        // skipComments: true with multiple different comment types, including trailing and leading whitespace
+        {
+            code: "function name() {\nvar x = 5;\n\t/* a comment with leading whitespace */\n/* a comment with trailing whitespace */\t\t\n\t/* a comment with trailing and leading whitespace */\t\t\n/* a \n multi \n line \n comment \n*/\t\t\n\nvar x = 2; // end of line comment\n}",
+            options: [{ max: 5, skipComments: true, skipBlankLines: false }]
+        },
+
+        // Multiple params on seperate lines test
+        {
+            code: `function foo(
+    aaa = 1,
+    bbb = 2,
+    ccc = 3
+) {
+    return aaa + bbb + ccc
+}`,
+            options: [{ max: 7, skipComments: true, skipBlankLines: false }]
+        },
+
+        // IIFE validity test
+        {
+            code: `(
+function
+()
+{
+}
+)
+()`,
+            options: [{ max: 4, skipComments: true, skipBlankLines: false, IIFEs: true }]
+        },
+
+        // Nested function validity test
+        {
+            code: `function parent() {
+var x = 0;
+function nested() {
+    var y = 0;
+    x = 2;
+}
+if ( x === y ) {
+    x++;
+}
+}`,
+            options: [{ max: 10, skipComments: true, skipBlankLines: false }]
+        },
+
+        // Class method validity test
+        {
+            code: `class foo {
+    method() {
+        let y = 10;
+        let x = 20;
+        return y + x;
+    }
+}`,
+            options: [{ max: 5, skipComments: true, skipBlankLines: false }]
+        },
+
+        // IIFEs should be recognised if IIFEs: true
+        {
+            code: `(function(){
+    let x = 0;
+    let y = 0;
+    let z = x + y;
+    let foo = {};
+    return bar;
+}());`,
+            options: [{ max: 7, skipComments: true, skipBlankLines: false, IIFEs: true }]
+        },
+
+        // IIFEs should not be recognised if IIFEs: false
+        {
+            code: `(function(){
+    let x = 0;
+    let y = 0;
+    let z = x + y;
+    let foo = {};
+    return bar;
+}());`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: false }]
+        }
+    ],
+
+    invalid: [
+
+        // Test simple standalone function is recognised
+        {
+            code: "function name() {\n}",
+            options: [1],
+            errors: [
+                "function 'name' has too many lines (2). Maximum allowed is 1."
+            ]
+        },
+
+        // Test anonymous function assigned to variable is recognised
+        {
+            code: "var func = function() {\n}",
+            options: [1],
+            errors: [
+                "function has too many lines (2). Maximum allowed is 1."
+            ]
+        },
+
+        // Test arrow functions are recognised
+        {
+            code: "const bar = () => {\nconst x = 2 + 1;\nreturn x;\n}",
+            options: [3],
+            errors: [
+                "arrow function has too many lines (4). Maximum allowed is 3."
+            ]
+        },
+
+        // Test inline arrow functions are recognised
+        {
+            code: "const bar = () =>\n 2",
+            options: [1],
+            errors: [
+                "arrow function has too many lines (2). Maximum allowed is 1."
+            ]
+        },
+
+        // Test skipBlankLines: false
+        {
+            code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 6, skipComments: false, skipBlankLines: false }],
+            errors: [
+                "function 'name' has too many lines (7). Maximum allowed is 6."
+            ]
+        },
+
+        // Test skipBlankLines: false with CRLF line endings
+        {
+            code: "function name() {\r\nvar x = 5;\r\n\t\r\n \r\n\r\nvar x = 2;\r\n}",
+            options: [{ max: 6, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "function 'name' has too many lines (7). Maximum allowed is 6."
+            ]
+        },
+
+        // Test skipBlankLines: true
+        {
+            code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 2, skipComments: true, skipBlankLines: true }],
+            errors: [
+                "function 'name' has too many lines (4). Maximum allowed is 2."
+            ]
+        },
+
+        // Test skipBlankLines: true with CRLF line endings
+        {
+            code: "function name() {\r\nvar x = 5;\r\n\t\r\n \r\n\r\nvar x = 2;\r\n}",
+            options: [{ max: 2, skipComments: true, skipBlankLines: true }],
+            errors: [
+                "function 'name' has too many lines (4). Maximum allowed is 2."
+            ]
+        },
+
+        // Test skipComments: true and skipBlankLines: false for multiple types of comment
+        {
+            code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 6, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "function 'name' has too many lines (7). Maximum allowed is 6."
+            ]
+        },
+
+        // Test skipComments: true and skipBlankLines: true for multiple types of comment
+        {
+            code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 1, skipComments: true, skipBlankLines: true }],
+            errors: [
+                "function 'name' has too many lines (4). Maximum allowed is 1."
+            ]
+        },
+
+        // Test skipComments: false and skipBlankLines: true for multiple types of comment
+        {
+            code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}",
+            options: [{ max: 1, skipComments: false, skipBlankLines: true }],
+            errors: [
+                "function 'name' has too many lines (5). Maximum allowed is 1."
+            ]
+        },
+
+        // Test simple standalone function with params on separate lines
+        {
+            code: `function foo(
+    aaa = 1,
+    bbb = 2,
+    ccc = 3
+) {
+    return aaa + bbb + ccc
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "function 'foo' has too many lines (7). Maximum allowed is 2."
+            ]
+        },
+
+        // Test IIFE "function" keyword is included in the count
+        {
+            code: `(
+function
+()
+{
+}
+)
+()`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: true }],
+            errors: [
+                "function has too many lines (4). Maximum allowed is 2."
+            ]
+        },
+
+        // Test nested functions are included in it's parent's function count.
+        {
+            code: `function parent() {
+var x = 0;
+function nested() {
+    var y = 0;
+    x = 2;
+}
+if ( x === y ) {
+    x++;
+}
+}`,
+            options: [{ max: 9, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "function 'parent' has too many lines (10). Maximum allowed is 9."
+            ]
+        },
+
+        // Test nested functions are included in it's parent's function count.
+        {
+            code: `function parent() {
+var x = 0;
+function nested() {
+    var y = 0;
+    x = 2;
+}
+if ( x === y ) {
+    x++;
+}
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "function 'parent' has too many lines (10). Maximum allowed is 2.",
+                "function 'nested' has too many lines (4). Maximum allowed is 2."
+            ]
+        },
+
+        // Test regular methods are recognised
+        {
+            code: `class foo {
+    method() {
+        let y = 10;
+        let x = 20;
+        return y + x;
+    }
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "method 'method' has too many lines (5). Maximum allowed is 2."
+            ]
+        },
+
+        // Test static methods are recognised
+        {
+            code: `class A {
+    static
+    foo
+    (a) {
+        return a
+    }
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "static method 'foo' has too many lines (5). Maximum allowed is 2."
+            ]
+        },
+
+        // Test getters are recognised as properties
+        {
+            code: `var obj = {
+    get
+    foo
+    () {
+        return 1
+    }
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "getter 'foo' has too many lines (5). Maximum allowed is 2."
+            ]
+        },
+
+        // Test setters are recognised as properties
+        {
+            code: `var obj = {
+    set
+    foo
+    ( val ) {
+        this._foo = val;
+    }
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "setter 'foo' has too many lines (5). Maximum allowed is 2."
+            ]
+        },
+
+        // Test computed property names
+        {
+            code: `class A {
+    static
+    [
+        foo +
+            bar
+    ]
+    (a) {
+        return a
+    }
+}`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false }],
+            errors: [
+                "static method has too many lines (8). Maximum allowed is 2."
+            ]
+        },
+
+        // Test the IIFEs option includes IIFEs
+        {
+            code: `(function(){
+    let x = 0;
+    let y = 0;
+    let z = x + y;
+    let foo = {};
+    return bar;
+}());`,
+            options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: true }],
+            errors: [
+                "function has too many lines (7). Maximum allowed is 2."
+            ]
+        }
+    ]
+});
diff --git a/tests/lib/rules/no-debugger.js b/tests/lib/rules/no-debugger.js
index d97750599..3d991fc3e 100644
--- a/tests/lib/rules/no-debugger.js
+++ b/tests/lib/rules/no-debugger.js
@@ -23,11 +23,6 @@ ruleTester.run("no-debugger", rule, {
         "var test = { debugger: 1 }; test.debugger;"
     ],
     invalid: [
-        {
-            code: "debugger",
-            output: "",
-            errors: [{ messageId: "unexpected", type: "DebuggerStatement" }]
-        },
         {
             code: "if (foo) debugger",
             output: null,
diff --git a/tests/lib/rules/valid-jsdoc.js b/tests/lib/rules/valid-jsdoc.js
index 6962598ab..c300d08ea 100644
--- a/tests/lib/rules/valid-jsdoc.js
+++ b/tests/lib/rules/valid-jsdoc.js
@@ -296,6 +296,17 @@ ruleTester.run("valid-jsdoc", rule, {
                 ecmaVersion: 2017
             }
         },
+        {
+            code:
+              "/**\n" +
+              " * An async function. Options do not require return.\n" +
+              " */\n" +
+              "async function a() {}",
+            options: [{ requireReturn: false }],
+            parserOptions: {
+                ecmaVersion: 2017
+            }
+        },
 
         // type validations
         {
@@ -1677,6 +1688,24 @@ ruleTester.run("valid-jsdoc", rule, {
             }]
         },
 
+        // async function
+        {
+            code:
+              "/**\n" +
+              " * An async function. Options requires return.\n" +
+              " */\n" +
+              "async function a() {}",
+            output: null,
+            options: [{ requireReturn: true }],
+            parserOptions: {
+                ecmaVersion: 2017
+            },
+            errors: [{
+                message: "Missing JSDoc @returns for function.",
+                type: "Block"
+            }]
+        },
+
         // type validations
         {
             code: