Repository: eslint/eslint
Pull request #9816 (2018-01-07 02:49:19)

Docs: Sync messageId examples' style with other examples
<!--
    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] 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
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    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)**

Small changes to messageId code samples to match style of other examples on the ESLint site.

**Is there anything you'd like reviewers to focus on?**

Not really.


Patch:
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index b72425262..b774d3694 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -820,9 +820,9 @@ In addition to the properties above, invalid test cases can also have the follow
     * `message` (string/regexp): The message for the error
     * `type` (string): The type of the reported AST node
     * `line` (number): The 1-based line number of the reported location
-    * `column` (number): The 0-based column number of the reported location
+    * `column` (number): The 1-based column number of the reported location
     * `endLine` (number): The 1-based line number of the end of the reported location
-    * `endColumn` (number): The 0-based column number of the end of the reported location
+    * `endColumn` (number): The 1-based column number of the end of the reported location
 
     If a string is provided as an error instead of an object, the string is used to assert the `message` of the error.
 * `output` (string, optional): Asserts the output that will be produced when using this rule for a single pass of autofixing (e.g. with the `--fix` command line flag). If this is `null`, asserts that none of the reported problems suggest autofixes.
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index 1d0ba82ee..f345f7cdc 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -208,7 +208,7 @@ module.exports = {
                         messageId: "avoidName",
                         data: {
                             name: "foo",
-                        },
+                        }
                     });
                 }
             }
@@ -218,14 +218,14 @@ module.exports = {
 
 // in the file to lint:
 
-var foo = 2
+var foo = 2;
 //  ^ error: Avoid using variables named 'foo'
 
 // In your tests:
-var rule = require('../../../lib/rules/no-insecure-random')
-var RuleTester = require('eslint').RuleTester
+var rule = require('../../../lib/rules/no-insecure-random');
+var RuleTester = require('eslint').RuleTester;
 
-var ruleTester = new RuleTester()
+var ruleTester = new RuleTester();
 ruleTester.run('my-rule', rule, {
   valid: ['bar', 'baz'],
 
@@ -235,11 +235,11 @@ ruleTester.run('my-rule', rule, {
       errors: [
         {
           messageId: 'foo',
-        },
-      ],
-    },
-  ],
-})
+        }
+      ]
+    }
+  ]
+});
 {% endraw %}
 ```
 
diff --git a/docs/rules/keyword-spacing.md b/docs/rules/keyword-spacing.md
index b8d97a73e..becc9ef32 100644
--- a/docs/rules/keyword-spacing.md
+++ b/docs/rules/keyword-spacing.md
@@ -59,44 +59,44 @@ if (foo) {
     //...
 }
 
-// no conflict with `array-bracket-spacing`
+// Avoid conflict with `array-bracket-spacing`
 let a = [this];
 let b = [function() {}];
 
-// no conflict with `arrow-spacing`
+// Avoid conflict with `arrow-spacing`
 let a = ()=> this.foo;
 
-// no conflict with `block-spacing`
+// Avoid conflict with `block-spacing`
 {function foo() {}}
 
-// no conflict with `comma-spacing`
+// Avoid conflict with `comma-spacing`
 let a = [100,this.foo, this.bar];
 
-// not conflict with `computed-property-spacing`
+// Avoid conflict with `computed-property-spacing`
 obj[this.foo] = 0;
 
-// no conflict with `generator-star-spacing`
+// Avoid conflict with `generator-star-spacing`
 function *foo() {}
 
-// no conflict with `key-spacing`
+// Avoid conflict with `key-spacing`
 let obj = {
     foo:function() {}
 };
 
-// no conflict with `object-curly-spacing`
+// Avoid conflict with `object-curly-spacing`
 let obj = {foo: this};
 
-// no conflict with `semi-spacing`
+// Avoid conflict with `semi-spacing`
 let a = this;function foo() {}
 
-// no conflict with `space-in-parens`
+// Avoid conflict with `space-in-parens`
 (function () {})();
 
-// no conflict with `space-infix-ops`
+// Avoid conflict with `space-infix-ops`
 if ("foo"in {foo: 0}) {}
 if (10+this.foo<= this.bar) {}
 
-// no conflict with `jsx-curly-spacing`
+// Avoid conflict with `jsx-curly-spacing`
 let a = <A foo={this.foo} bar={function(){}} />
 ```
 
@@ -157,57 +157,57 @@ if (foo) {
     //...
 }
 
-// not conflict with `array-bracket-spacing`
+// Avoid conflict with `array-bracket-spacing`
 let a = [this];
 
-// not conflict with `arrow-spacing`
+// Avoid conflict with `arrow-spacing`
 let a = ()=> this.foo;
 
-// not conflict with `comma-spacing`
+// Avoid conflict with `comma-spacing`
 let a = [100, this.foo, this.bar];
 
-// not conflict with `computed-property-spacing`
+// Avoid conflict with `computed-property-spacing`
 obj[this.foo] = 0;
 
-// not conflict with `generator-star-spacing`
+// Avoid conflict with `generator-star-spacing`
 function* foo() {}
 
-// not conflict with `key-spacing`
+// Avoid conflict with `key-spacing`
 let obj = {
     foo:function() {}
 };
 
-// not conflict with `func-call-spacing`
+// Avoid conflict with `func-call-spacing`
 class A {
     constructor() {
         super();
     }
 }
 
-// not conflict with `object-curly-spacing`
+// Avoid conflict with `object-curly-spacing`
 let obj = {foo: this};
 
-// not conflict with `semi-spacing`
+// Avoid conflict with `semi-spacing`
 let a = this;function foo() {}
 
-// not conflict with `space-before-function-paren`
+// Avoid conflict with `space-before-function-paren`
 function() {}
 
-// no conflict with `space-infix-ops`
+// Avoid conflict with `space-infix-ops`
 if ("foo"in{foo: 0}) {}
 if (10+this.foo<= this.bar) {}
 
-// no conflict with `space-unary-ops`
+// Avoid conflict with `space-unary-ops`
 function* foo(a) {
     return yield+a;
 }
 
-// no conflict with `yield-star-spacing`
+// Avoid conflict with `yield-star-spacing`
 function* foo(a) {
     return yield* a;
 }
 
-// no conflict with `jsx-curly-spacing`
+// Avoid conflict with `jsx-curly-spacing`
 let a = <A foo={this.foo} bar={function(){}} />
 ```
 
diff --git a/docs/rules/object-property-newline.md b/docs/rules/object-property-newline.md
index 8cc73b05e..15d8ee0a0 100644
--- a/docs/rules/object-property-newline.md
+++ b/docs/rules/object-property-newline.md
@@ -74,7 +74,7 @@ Another benefit of this rule is specificity of diffs when a property is changed:
 
 ### Optional Exception
 
-The rule offers one object option, `allowMultiplePropertiesPerLine`. If you set it to `true`, object literals such as the first two above, with all property specifications on the same line, will be permitted, but one like
+The rule offers one object option, `allowAllPropertiesOnSameLine` (a deprecated synonym is `allowMultiplePropertiesPerLine`). If you set it to `true`, object literals such as the first two above, with all property specifications on the same line, will be permitted, but one like
 
 ```js
 const newObject = {
@@ -172,7 +172,7 @@ As illustrated above, the `--fix` option, applied to this rule, does not comply
 
 ## Examples
 
-Examples of **incorrect** code for this rule, with no object option or with `allowMultiplePropertiesPerLine` set to `false`:
+Examples of **incorrect** code for this rule, with no object option or with `allowAllPropertiesOnSameLine` set to `false`:
 
 ```js
 /*eslint object-property-newline: "error"*/
@@ -208,7 +208,7 @@ const obj5 = {
 ]: true};
 ```
 
-Examples of **correct** code for this rule, with no object option or with `allowMultiplePropertiesPerLine` set to `false`:
+Examples of **correct** code for this rule, with no object option or with `allowAllPropertiesOnSameLine` set to `false`:
 
 ```js
 /*eslint object-property-newline: "error"*/
@@ -238,10 +238,10 @@ const obj3 = {
 };
 ```
 
-Examples of additional **correct** code for this rule with the `{ "allowMultiplePropertiesPerLine": true }` option:
+Examples of additional **correct** code for this rule with the `{ "allowAllPropertiesOnSameLine": true }` option:
 
 ```js
-/*eslint object-property-newline: ["error", { "allowMultiplePropertiesPerLine": true }]*/
+/*eslint object-property-newline: ["error", { "allowAllPropertiesOnSameLine": true }]*/
 
 const obj = { foo: "foo", bar: "bar", baz: "baz" };
 
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 8b1bcba4a..44b823aeb 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -26,7 +26,7 @@ For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 glo
 { "es6": true } }` (this setting enables ES6 syntax automatically).
 Parser options are set in your `.eslintrc.*` file by using the `parserOptions` property. The available options are:
 
-* `ecmaVersion` - set to 3, 5 (default), 6, 7, or 8 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), or 2017 (same as 8) to use the year-based naming.
+* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, or 9 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), or 2018 (same as 9) to use the year-based naming.
 * `sourceType` - set to `"script"` (default) or `"module"` if your code is in ECMAScript modules.
 * `ecmaFeatures` - an object indicating which additional language features you'd like to use:
     * `globalReturn` - allow `return` statements in the global scope
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 3d98b5104..1a5b3ef13 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -98,7 +98,8 @@ function validateRuleSchema(rule, localOptions) {
  * @param {{create: Function}|null} rule The rule that the config is being validated for
  * @param {string} ruleId The rule's unique name.
  * @param {array|number} options The given options for the rule.
- * @param {string} source The name of the configuration source to report in any errors.
+ * @param {string|null} source The name of the configuration source to report in any errors. If null or undefined,
+ * no source is prepended to the message.
  * @returns {void}
  */
 function validateRuleOptions(rule, ruleId, options, source) {
@@ -112,7 +113,13 @@ function validateRuleOptions(rule, ruleId, options, source) {
             validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
         }
     } catch (err) {
-        throw new Error(`${source}:\n\tConfiguration for rule "${ruleId}" is invalid:\n${err.message}`);
+        const enhancedMessage = `Configuration for rule "${ruleId}" is invalid:\n${err.message}`;
+
+        if (typeof source === "string") {
+            throw new Error(`${source}:\n\t${enhancedMessage}`);
+        } else {
+            throw new Error(enhancedMessage);
+        }
     }
 }
 
diff --git a/lib/linter.js b/lib/linter.js
index 2238c5be0..6cbb25b1c 100755
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -340,7 +340,21 @@ function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
                             Object.keys(parseResult.config).forEach(name => {
                                 const ruleValue = parseResult.config[name];
 
-                                validator.validateRuleOptions(ruleMapper(name), name, ruleValue, `${filename} line ${comment.loc.start.line}`);
+                                try {
+                                    validator.validateRuleOptions(ruleMapper(name), name, ruleValue);
+                                } catch (err) {
+                                    problems.push({
+                                        ruleId: name,
+                                        severity: 2,
+                                        source: null,
+                                        message: err.message,
+                                        line: comment.loc.start.line,
+                                        column: comment.loc.start.column + 1,
+                                        endLine: comment.loc.end.line,
+                                        endColumn: comment.loc.end.column + 1,
+                                        nodeType: null
+                                    });
+                                }
                                 commentRules[name] = ruleValue;
                             });
                         } else {
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 2159880f8..79a0f25ca 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -442,6 +442,7 @@ class OffsetStorage {
                 const offset = (
                     offsetInfo.from &&
                     offsetInfo.from.loc.start.line === token.loc.start.line &&
+                    !/^\s*?\n/.test(token.value) &&
                     !offsetInfo.force
                 ) ? 0 : offsetInfo.offset * this._indentSize;
 
@@ -836,7 +837,7 @@ module.exports = {
                     const previousElement = elements[index - 1];
                     const firstTokenOfPreviousElement = previousElement && getFirstToken(previousElement);
 
-                    if (previousElement && sourceCode.getLastToken(previousElement).loc.start.line > startToken.loc.end.line) {
+                    if (previousElement && sourceCode.getLastToken(previousElement).loc.end.line > startToken.loc.end.line) {
                         offsets.setDesiredOffsets(element.range, firstTokenOfPreviousElement, 0);
                     }
                 }
@@ -1094,8 +1095,8 @@ module.exports = {
                     const questionMarkToken = sourceCode.getFirstTokenBetween(node.test, node.consequent, token => token.type === "Punctuator" && token.value === "?");
                     const colonToken = sourceCode.getFirstTokenBetween(node.consequent, node.alternate, token => token.type === "Punctuator" && token.value === ":");
 
-                    const firstConsequentToken = sourceCode.getTokenAfter(questionMarkToken, { includeComments: true });
-                    const lastConsequentToken = sourceCode.getTokenBefore(colonToken, { includeComments: true });
+                    const firstConsequentToken = sourceCode.getTokenAfter(questionMarkToken);
+                    const lastConsequentToken = sourceCode.getTokenBefore(colonToken);
                     const firstAlternateToken = sourceCode.getTokenAfter(colonToken);
 
                     offsets.setDesiredOffset(questionMarkToken, firstToken, 1);
diff --git a/lib/rules/object-property-newline.js b/lib/rules/object-property-newline.js
index 56ca26940..65baf0a95 100644
--- a/lib/rules/object-property-newline.js
+++ b/lib/rules/object-property-newline.js
@@ -22,7 +22,10 @@ module.exports = {
             {
                 type: "object",
                 properties: {
-                    allowMultiplePropertiesPerLine: {
+                    allowAllPropertiesOnSameLine: {
+                        type: "boolean"
+                    },
+                    allowMultiplePropertiesPerLine: { // Deprecated
                         type: "boolean"
                     }
                 },
@@ -34,7 +37,10 @@ module.exports = {
     },
 
     create(context) {
-        const allowSameLine = context.options[0] && Boolean(context.options[0].allowMultiplePropertiesPerLine);
+        const allowSameLine = context.options[0] && (
+            Boolean(context.options[0].allowAllPropertiesOnSameLine) ||
+            Boolean(context.options[0].allowMultiplePropertiesPerLine) // Deprecated
+        );
         const errorMessage = allowSameLine
             ? "Object properties must go on a new line if they aren't all on the same line."
             : "Object properties must go on a new line.";
diff --git a/lib/rules/prefer-destructuring.js b/lib/rules/prefer-destructuring.js
index e9d02da3a..112ea6461 100644
--- a/lib/rules/prefer-destructuring.js
+++ b/lib/rules/prefer-destructuring.js
@@ -165,8 +165,10 @@ module.exports = {
             if (shouldCheck(reportNode.type, "object")) {
                 const property = rightNode.property;
 
-                if ((property.type === "Literal" && leftNode.name === property.value) || (property.type === "Identifier" &&
-                    leftNode.name === property.name)) {
+                if (
+                    (property.type === "Literal" && leftNode.name === property.value) ||
+                    (property.type === "Identifier" && leftNode.name === property.name && !rightNode.computed)
+                ) {
                     report(reportNode, "object");
                 }
             }
diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js
index 5e1af10de..c213c50f1 100644
--- a/lib/rules/valid-jsdoc.js
+++ b/lib/rules/valid-jsdoc.js
@@ -57,7 +57,9 @@ module.exports = {
                 },
                 additionalProperties: false
             }
-        ]
+        ],
+
+        fixable: "code"
     },
 
     create(context) {
@@ -145,23 +147,35 @@ module.exports = {
         /**
          * Extract the current and expected type based on the input type object
          * @param {Object} type JSDoc tag
-         * @returns {Object} current and expected type object
+         * @returns {{currentType: Doctrine.Type, expectedTypeName: string}} The current type annotation and
+         * the expected name of the annotation
          * @private
          */
         function getCurrentExpectedTypes(type) {
             let currentType;
 
             if (type.name) {
-                currentType = type.name;
+                currentType = type;
             } else if (type.expression) {
-                currentType = type.expression.name;
+                currentType = type.expression;
             }
 
-            const expectedType = currentType && preferType[currentType];
-
             return {
                 currentType,
-                expectedType
+                expectedTypeName: currentType && preferType[currentType.name]
+            };
+        }
+
+        /**
+         * Gets the location of a JSDoc node in a file
+         * @param {Token} jsdocComment The comment that this node is parsed from
+         * @param {{range: number[]}} parsedJsdocNode A tag or other node which was parsed from this comment
+         * @returns {{start: SourceLocation, end: SourceLocation}} The 0-based source location for the tag
+         */
+        function getAbsoluteRange(jsdocComment, parsedJsdocNode) {
+            return {
+                start: sourceCode.getLocFromIndex(jsdocComment.range[0] + 2 + parsedJsdocNode.range[0]),
+                end: sourceCode.getLocFromIndex(jsdocComment.range[0] + 2 + parsedJsdocNode.range[1])
             };
         }
 
@@ -204,14 +218,21 @@ module.exports = {
             elements.forEach(validateType.bind(null, jsdocNode));
 
             typesToCheck.forEach(typeToCheck => {
-                if (typeToCheck.expectedType &&
-                    typeToCheck.expectedType !== typeToCheck.currentType) {
+                if (typeToCheck.expectedTypeName &&
+                    typeToCheck.expectedTypeName !== typeToCheck.currentType.name) {
                     context.report({
                         node: jsdocNode,
-                        message: "Use '{{expectedType}}' instead of '{{currentType}}'.",
+                        message: "Use '{{expectedTypeName}}' instead of '{{currentTypeName}}'.",
+                        loc: getAbsoluteRange(jsdocNode, typeToCheck.currentType),
                         data: {
-                            currentType: typeToCheck.currentType,
-                            expectedType: typeToCheck.expectedType
+                            currentTypeName: typeToCheck.currentType.name,
+                            expectedTypeName: typeToCheck.expectedTypeName
+                        },
+                        fix(fixer) {
+                            return fixer.replaceTextRange(
+                                typeToCheck.currentType.range.map(indexInComment => jsdocNode.range[0] + 2 + indexInComment),
+                                typeToCheck.expectedTypeName
+                            );
                         }
                     });
                 }
@@ -227,8 +248,8 @@ module.exports = {
         function checkJSDoc(node) {
             const jsdocNode = sourceCode.getJSDocComment(node),
                 functionData = fns.pop(),
-                params = Object.create(null),
-                paramsTags = [];
+                paramTagsByName = Object.create(null),
+                paramTags = [];
             let hasReturns = false,
                 returnsTag,
                 hasConstructor = false,
@@ -244,7 +265,8 @@ module.exports = {
                     jsdoc = doctrine.parse(jsdocNode.value, {
                         strict: true,
                         unwrap: true,
-                        sloppy: true
+                        sloppy: true,
+                        range: true
                     });
                 } catch (ex) {
 
@@ -264,7 +286,7 @@ module.exports = {
                         case "param":
                         case "arg":
                         case "argument":
-                            paramsTags.push(tag);
+                            paramTags.push(tag);
                             break;
 
                         case "return":
@@ -297,7 +319,29 @@ module.exports = {
 
                     // check tag preferences
                     if (prefer.hasOwnProperty(tag.title) && tag.title !== prefer[tag.title]) {
-                        context.report({ node: jsdocNode, message: "Use @{{name}} instead.", data: { name: prefer[tag.title] } });
+                        const entireTagRange = getAbsoluteRange(jsdocNode, tag);
+
+                        context.report({
+                            node: jsdocNode,
+                            message: "Use @{{name}} instead.",
+                            loc: {
+                                start: entireTagRange.start,
+                                end: {
+                                    line: entireTagRange.start.line,
+                                    column: entireTagRange.start.column + `@${tag.title}`.length
+                                }
+                            },
+                            data: { name: prefer[tag.title] },
+                            fix(fixer) {
+                                return fixer.replaceTextRange(
+                                    [
+                                        jsdocNode.range[0] + tag.range[0] + 3,
+                                        jsdocNode.range[0] + tag.range[0] + tag.title.length + 3
+                                    ],
+                                    prefer[tag.title]
+                                );
+                            }
+                        });
                     }
 
                     // validate the types
@@ -306,17 +350,32 @@ module.exports = {
                     }
                 });
 
-                paramsTags.forEach(param => {
+                paramTags.forEach(param => {
                     if (!param.type) {
-                        context.report({ node: jsdocNode, message: "Missing JSDoc parameter type for '{{name}}'.", data: { name: param.name } });
+                        context.report({
+                            node: jsdocNode,
+                            message: "Missing JSDoc parameter type for '{{name}}'.",
+                            loc: getAbsoluteRange(jsdocNode, param),
+                            data: { name: param.name }
+                        });
                     }
                     if (!param.description && requireParamDescription) {
-                        context.report({ node: jsdocNode, message: "Missing JSDoc parameter description for '{{name}}'.", data: { name: param.name } });
+                        context.report({
+                            node: jsdocNode,
+                            message: "Missing JSDoc parameter description for '{{name}}'.",
+                            loc: getAbsoluteRange(jsdocNode, param),
+                            data: { name: param.name }
+                        });
                     }
-                    if (params[param.name]) {
-                        context.report({ node: jsdocNode, message: "Duplicate JSDoc parameter '{{name}}'.", data: { name: param.name } });
+                    if (paramTagsByName[param.name]) {
+                        context.report({
+                            node: jsdocNode,
+                            message: "Duplicate JSDoc parameter '{{name}}'.",
+                            loc: getAbsoluteRange(jsdocNode, param),
+                            data: { name: param.name }
+                        });
                     } else if (param.name.indexOf(".") === -1) {
-                        params[param.name] = 1;
+                        paramTagsByName[param.name] = param;
                     }
                 });
 
@@ -325,6 +384,7 @@ module.exports = {
                         context.report({
                             node: jsdocNode,
                             message: "Unexpected @{{title}} tag; function has no return statement.",
+                            loc: getAbsoluteRange(jsdocNode, returnsTag),
                             data: {
                                 title: returnsTag.title
                             }
@@ -356,10 +416,10 @@ module.exports = {
                 }
 
                 // check the parameters
-                const jsdocParams = Object.keys(params);
+                const jsdocParamNames = Object.keys(paramTagsByName);
 
                 if (node.params) {
-                    node.params.forEach((param, i) => {
+                    node.params.forEach((param, paramsIndex) => {
                         if (param.type === "AssignmentPattern") {
                             param = param.left;
                         }
@@ -368,16 +428,17 @@ module.exports = {
 
                         // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
                         if (param.type === "Identifier") {
-                            if (jsdocParams[i] && (name !== jsdocParams[i])) {
+                            if (jsdocParamNames[paramsIndex] && (name !== jsdocParamNames[paramsIndex])) {
                                 context.report({
                                     node: jsdocNode,
                                     message: "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.",
+                                    loc: getAbsoluteRange(jsdocNode, paramTagsByName[jsdocParamNames[paramsIndex]]),
                                     data: {
                                         name,
-                                        jsdocName: jsdocParams[i]
+                                        jsdocName: jsdocParamNames[paramsIndex]
                                     }
                                 });
-                            } else if (!params[name] && !isOverride) {
+                            } else if (!paramTagsByName[name] && !isOverride) {
                                 context.report({
                                     node: jsdocNode,
                                     message: "Missing JSDoc for parameter '{{name}}'.",
diff --git a/package.json b/package.json
index ae3bce395..286e52a63 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
     "concat-stream": "^1.6.0",
     "cross-spawn": "^5.1.0",
     "debug": "^3.1.0",
-    "doctrine": "^2.0.2",
+    "doctrine": "^2.1.0",
     "eslint-scope": "^3.7.1",
     "eslint-visitor-keys": "^1.0.0",
     "espree": "^3.5.2",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index f901651e6..f8f20d9e2 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -129,7 +129,7 @@ rules:
     no-var: "error"
     object-curly-newline: ["error", { "consistent": true, "multiline": true }]
     object-curly-spacing: ["error", "always"]
-    object-property-newline: ["error", { "allowMultiplePropertiesPerLine": true }]
+    object-property-newline: ["error", { "allowAllPropertiesOnSameLine": true }]
     object-shorthand: "error"
     one-var-declaration-per-line: "error"
     operator-assignment: "error"


Test patch:
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index 481a0846f..fc4298d56 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -1615,14 +1615,42 @@ describe("Linter", () => {
     });
 
     describe("when evaluating code with invalid comments to enable rules", () => {
-        const code = "/*eslint no-alert:true*/ alert('test');";
-
-        it("should report a violation", () => {
-            const config = { rules: {} };
-
-            const fn = linter.verify.bind(linter, code, config, filename);
+        it("should report a violation when the config is not a valid rule configuration", () => {
+            assert.deepStrictEqual(
+                linter.verify("/*eslint no-alert:true*/ alert('test');", {}),
+                [
+                    {
+                        severity: 2,
+                        ruleId: "no-alert",
+                        message: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n",
+                        line: 1,
+                        column: 1,
+                        endLine: 1,
+                        endColumn: 25,
+                        source: null,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
 
-            assert.throws(fn, "filename.js line 1:\n\tConfiguration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n");
+        it("should report a violation when the config violates a rule's schema", () => {
+            assert.deepStrictEqual(
+                linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {}),
+                [
+                    {
+                        severity: 2,
+                        ruleId: "no-alert",
+                        message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
+                        line: 1,
+                        column: 1,
+                        endLine: 1,
+                        endColumn: 63,
+                        source: null,
+                        nodeType: null
+                    }
+                ]
+            );
         });
     });
 
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index 4ec4975bc..6ffa72ce4 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -2830,6 +2830,13 @@ ruleTester.run("indent", rule, {
             `,
             options: [2]
         },
+        unIndent`
+            foo(\`
+                bar
+            \`, {
+                baz: 1
+            });
+        `,
         unIndent`
             function foo() {
                 \`foo\${bar}baz\${
@@ -3856,21 +3863,20 @@ ruleTester.run("indent", rule, {
         unIndent`
             foo(\`foo
                     \`, {
-                    ok: true
-                },
-                {
-                    ok: false
-                }
-            )
+                ok: true
+            },
+            {
+                ok: false
+            })
         `,
         unIndent`
             foo(tag\`foo
                     \`, {
-                    ok: true
-                },
-                {
-                    ok: false
-                }
+                ok: true
+            },
+            {
+                ok: false
+            }
             )
         `,
 
@@ -4796,7 +4802,16 @@ ruleTester.run("indent", rule, {
                 }
             `,
             options: [4, { ignoreComments: true }]
-        }
+        },
+        unIndent`
+            const obj = {
+                foo () {
+                    return condition ? // comment
+                        1 :
+                        2
+                }
+            }
+        `
     ],
 
     invalid: [
@@ -9261,6 +9276,27 @@ ruleTester.run("indent", rule, {
             `,
             options: [4, { ignoreComments: false }],
             errors: expectedErrors([4, 4, 0, "Block"])
+        },
+        {
+            code: unIndent`
+                const obj = {
+                    foo () {
+                        return condition ? // comment
+                        1 :
+                            2
+                    }
+                }
+            `,
+            output: unIndent`
+                const obj = {
+                    foo () {
+                        return condition ? // comment
+                            1 :
+                            2
+                    }
+                }
+            `,
+            errors: expectedErrors([4, 12, 8, "Numeric"])
         }
     ]
 });
diff --git a/tests/lib/rules/object-property-newline.js b/tests/lib/rules/object-property-newline.js
index 2d0df640f..ed0e24db2 100644
--- a/tests/lib/rules/object-property-newline.js
+++ b/tests/lib/rules/object-property-newline.js
@@ -44,24 +44,27 @@ ruleTester.run("object-property-newline", rule, {
         { code: "foo({ k1: 'val1',\nk2: 'val2',\n...{} });", parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
         { code: "foo({ ...{} });", parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
 
-        // allowMultiplePropertiesPerLine: true
-        { code: "var obj = { k1: 'val1', k2: 'val2', k3: 'val3' };", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "var obj = {\nk1: 'val1', k2: 'val2', k3: 'val3'\n};", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "var obj = { k1: 'val1' };", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "var obj = {\nk1: 'val1'\n};", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "var obj = {};", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "var obj = { 'k1': 'val1', k2: 'val2', ...{} };", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
-        { code: "var obj = {\n'k1': 'val1', k2: 'val2', ...{}\n};", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
-        { code: "foo({ k1: 'val1', k2: 'val2' });", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "foo({\nk1: 'val1', k2: 'val2'\n});", options: [{ allowMultiplePropertiesPerLine: true }] },
-        { code: "foo({ a, b });", options: [{ allowMultiplePropertiesPerLine: true }], parserOptions: { ecmaVersion: 6 } },
-        { 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: "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 }] }
+        // allowAllPropertiesOnSameLine: true
+        { code: "var obj = { k1: 'val1', k2: 'val2', k3: 'val3' };", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = {\nk1: 'val1', k2: 'val2', k3: 'val3'\n};", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = { k1: 'val1' };", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = {\nk1: 'val1'\n};", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = {};", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = { 'k1': 'val1', k2: 'val2', ...{} };", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
+        { code: "var obj = {\n'k1': 'val1', k2: 'val2', ...{}\n};", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
+        { code: "foo({ k1: 'val1', k2: 'val2' });", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "foo({\nk1: 'val1', k2: 'val2'\n});", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "foo({ a, b });", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "foo({ bar() {}, baz });", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "foo({ [bar]: 'baz', baz })", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "foo({ 'k1': 'val1', k2: 'val2', ...{} });", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
+        { code: "foo({\n'k1': 'val1', k2: 'val2', ...{}\n});", options: [{ allowAllPropertiesOnSameLine: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } } },
+        { code: "var obj = {k1: ['foo', 'bar'], k2: 'val1', k3: 'val2'};", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = {\nk1: ['foo', 'bar'], k2: 'val1', k3: 'val2'\n};", options: [{ allowAllPropertiesOnSameLine: true }] },
+        { code: "var obj = {\nk1: 'val1', k2: {e1: 'foo', e2: 'bar'}, k3: 'val2'\n};", options: [{ allowAllPropertiesOnSameLine: true }] },
+
+        // allowMultiplePropertiesPerLine: true (deprecated)
+        { code: "var obj = { k1: 'val1', k2: 'val2', k3: 'val3' };", options: [{ allowMultiplePropertiesPerLine: true }] }
     ],
 
     invalid: [
@@ -378,11 +381,11 @@ ruleTester.run("object-property-newline", rule, {
             ]
         },
 
-        // allowMultiplePropertiesPerLine: true
+        // allowAllPropertiesOnSameLine: true
         {
             code: "var obj = {\nk1: 'val1',\nk2: 'val2', k3: 'val3'\n};",
             output: "var obj = {\nk1: 'val1',\nk2: 'val2',\nk3: 'val3'\n};",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -395,7 +398,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = {\nk1:\n'val1', k2: 'val2', k3:\n'val3'\n};",
             output: "var obj = {\nk1:\n'val1',\nk2: 'val2',\nk3:\n'val3'\n};",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -414,7 +417,7 @@ 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 }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -427,7 +430,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = {k1: [\n'foo', 'bar'\n], k2: 'val1'};",
             output: "var obj = {k1: [\n'foo', 'bar'\n],\nk2: 'val1'};",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -440,7 +443,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             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 }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -459,7 +462,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n], k3: 'val3' };",
             output: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n],\nk3: 'val3' };",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             errors: [
                 {
                     message: "Object properties must go on a new line if they aren't all on the same line.",
@@ -472,7 +475,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = { [\nk1]: 'val1', k2: 'val2' };",
             output: "var obj = { [\nk1]: 'val1',\nk2: 'val2' };",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 {
@@ -486,7 +489,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = {\nk1: 'val1',\nk2: 'val2', ...{}\n};",
             output: "var obj = {\nk1: 'val1',\nk2: 'val2',\n...{}\n};",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } },
             errors: [
                 {
@@ -500,7 +503,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "var obj = {\n...{},\nk1: 'val1', k2: 'val2'\n};",
             output: "var obj = {\n...{},\nk1: 'val1',\nk2: 'val2'\n};",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } },
             errors: [
                 {
@@ -514,7 +517,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "foo({ [\nk1]: 'val1', k2: 'val2' })",
             output: "foo({ [\nk1]: 'val1',\nk2: 'val2' })",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 {
@@ -528,7 +531,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "foo({\nk1: 'val1',\nk2: 'val2', ...{}\n})",
             output: "foo({\nk1: 'val1',\nk2: 'val2',\n...{}\n})",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } },
             errors: [
                 {
@@ -542,7 +545,7 @@ ruleTester.run("object-property-newline", rule, {
         {
             code: "foo({\n...{},\nk1: 'val1', k2: 'val2'\n})",
             output: "foo({\n...{},\nk1: 'val1',\nk2: 'val2'\n})",
-            options: [{ allowMultiplePropertiesPerLine: true }],
+            options: [{ allowAllPropertiesOnSameLine: true }],
             parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } },
             errors: [
                 {
@@ -552,6 +555,21 @@ ruleTester.run("object-property-newline", rule, {
                     column: 13
                 }
             ]
+        },
+
+        // allowMultiplePropertiesPerLine: true (deprecated)
+        {
+            code: "var obj = {\nk1: 'val1',\nk2: 'val2', k3: 'val3'\n};",
+            output: "var obj = {\nk1: 'val1',\nk2: 'val2',\nk3: 'val3'\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: 3,
+                    column: 13
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/prefer-destructuring.js b/tests/lib/rules/prefer-destructuring.js
index e60535e7e..a0e4aa79b 100644
--- a/tests/lib/rules/prefer-destructuring.js
+++ b/tests/lib/rules/prefer-destructuring.js
@@ -131,7 +131,9 @@ ruleTester.run("prefer-destructuring", rule, {
             code: "var foo = object.foo;",
             options: [{ VariableDeclarator: { object: false }, AssignmentExpression: { object: true } }]
         },
-        "class Foo extends Bar { static foo() {var foo = super.foo} }"
+        "class Foo extends Bar { static foo() {var foo = super.foo} }",
+        "foo = bar[foo];",
+        "var foo = bar[foo];"
     ],
 
     invalid: [
diff --git a/tests/lib/rules/valid-jsdoc.js b/tests/lib/rules/valid-jsdoc.js
index 54fea8d8f..d6e0219c5 100644
--- a/tests/lib/rules/valid-jsdoc.js
+++ b/tests/lib/rules/valid-jsdoc.js
@@ -1044,14 +1044,20 @@ ruleTester.run("valid-jsdoc", rule, {
                 "    return 'the return';\n" +
                 "  }\n" +
                 ");\n",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "Expected JSDoc for 'argName' but found 'bogusName'.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 6,
+                endLine: 4,
+                endColumn: 62
             }]
         },
         {
             code: "/** @@foo */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc syntax error.",
                 type: "Block"
@@ -1070,6 +1076,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 "    return 'bar';\n" +
                 "  }\n" +
                 "});\n",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "Missing JSDoc @returns for function.",
@@ -1078,6 +1085,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** @@returns {void} Foo */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc syntax error.",
                 type: "Block"
@@ -1085,6 +1093,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@returns {void Foo\n */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc type missing brace.",
                 type: "Block"
@@ -1092,25 +1101,36 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@return {void} Foo\n */\nfunction foo(){}",
+            output: "/** Foo \n@returns {void} Foo\n */\nfunction foo(){}",
             options: [{ prefer: { return: "returns" } }],
             errors: [{
                 message: "Use @returns instead.",
-                type: "Block"
+                type: "Block",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 8
             }]
         },
         {
             code: "/** Foo \n@argument {int} bar baz\n */\nfunction foo(bar){}",
+            output: "/** Foo \n@arg {int} bar baz\n */\nfunction foo(bar){}",
             options: [{ prefer: { argument: "arg" } }],
             errors: [{
-                message: "Use @arg instead.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Use @arg instead.",
+                type: "Block",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 10
             }]
         },
         {
             code: "/** Foo \n */\nfunction foo(){}",
+            output: null,
             options: [{ prefer: { returns: "return" } }],
             errors: [{
                 message: "Missing JSDoc @return for function.",
@@ -1119,15 +1139,21 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@return {void} Foo\n */\nfoo.bar = () => {}",
+            output: "/** Foo \n@returns {void} Foo\n */\nfoo.bar = () => {}",
             options: [{ prefer: { return: "returns" } }],
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 message: "Use @returns instead.",
-                type: "Block"
+                type: "Block",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 8
             }]
         },
         {
             code: "/** Foo \n@param {void Foo\n */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc type missing brace.",
                 type: "Block"
@@ -1135,6 +1161,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@param {} p Bar\n */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc syntax error.",
                 type: "Block"
@@ -1142,6 +1169,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@param {void Foo */\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "JSDoc type missing brace.",
                 type: "Block"
@@ -1149,42 +1177,62 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo\n* @param p Desc \n*/\nfunction foo(){}",
+            output: null,
             errors: [{
-                message: "Missing JSDoc parameter type for 'p'.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Missing JSDoc parameter type for 'p'.",
+                type: "Block",
+                line: 2,
+                column: 3,
+                endLine: 2,
+                endColumn: 16
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} p \n*/\nfunction foo(){}",
+            output: null,
             errors: [{
-                message: "Missing JSDoc parameter description for 'p'.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Missing JSDoc parameter description for 'p'.",
+                type: "Block",
+                line: 3,
+                column: 3,
+                endLine: 3,
+                endColumn: 20
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} p \n*/\nvar foo = function(){}",
+            output: null,
             errors: [{
-                message: "Missing JSDoc parameter description for 'p'.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Missing JSDoc parameter description for 'p'.",
+                type: "Block",
+                line: 3,
+                column: 3,
+                endLine: 3,
+                endColumn: 20
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} p \n*/\nvar foo = \nfunction(){}",
+            output: null,
             errors: [{
-                message: "Missing JSDoc parameter description for 'p'.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Missing JSDoc parameter description for 'p'.",
+                type: "Block",
+                line: 3,
+                column: 3,
+                endLine: 3,
+                endColumn: 20
             }]
         },
         {
@@ -1202,6 +1250,7 @@ ruleTester.run("valid-jsdoc", rule, {
             "        this.a = xs;" +
             "    }\n" +
             "};",
+            output: null,
             options: [{
                 requireReturn: true,
                 matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$"
@@ -1222,6 +1271,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @returns {string} \n*/\nfunction foo(){}",
+            output: null,
             errors: [{
                 message: "Missing JSDoc return description.",
                 type: "Block"
@@ -1229,6 +1279,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @returns {string} something \n*/\nfunction foo(p){}",
+            output: null,
             errors: [{
                 message: "Missing JSDoc for parameter 'p'.",
                 type: "Block"
@@ -1236,6 +1287,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @returns {string} something \n*/\nvar foo = \nfunction foo(a = 1){}",
+            output: null,
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 message: "Missing JSDoc for parameter 'a'.",
@@ -1244,49 +1296,79 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @param {string} a Description \n* @param {string} b Description \n* @returns {string} something \n*/\nvar foo = \nfunction foo(b, a = 1){}",
+            output: null,
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 message: "Expected JSDoc for 'b' but found 'a'.",
-                type: "Block"
+                type: "Block",
+                line: 3,
+                column: 3,
+                endLine: 3,
+                endColumn: 32
             },
             {
                 message: "Expected JSDoc for 'a' but found 'b'.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 32
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} p desc\n* @param {string} p desc \n*/\nfunction foo(){}",
+            output: null,
             errors: [{
-                message: "Duplicate JSDoc parameter 'p'.",
-                type: "Block"
-            }, {
                 message: "Missing JSDoc @returns for function.",
                 type: "Block"
+            }, {
+                message: "Duplicate JSDoc parameter 'p'.",
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 25
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} a desc\n@returns {void}*/\nfunction foo(b){}",
+            output: null,
             errors: [{
                 message: "Expected JSDoc for 'b' but found 'a'.",
-                type: "Block"
+                type: "Block",
+                line: 3,
+                column: 3,
+                endLine: 3,
+                endColumn: 25
             }]
         },
         {
             code: "/**\n* Foo\n* @override\n* @param {string} a desc\n */\nfunction foo(b){}",
+            output: null,
             errors: [{
                 message: "Expected JSDoc for 'b' but found 'a'.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 25
             }]
         },
         {
             code: "/**\n* Foo\n* @inheritdoc\n* @param {string} a desc\n */\nfunction foo(b){}",
+            output: null,
             errors: [{
                 message: "Expected JSDoc for 'b' but found 'a'.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 25
             }]
         },
         {
             code: "/**\n* Foo\n* @param {string} a desc\n*/\nfunction foo(a){var t = false; if(t) {return t;}}",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "Missing JSDoc @returns for function.",
@@ -1295,6 +1377,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @param {string} a desc\n*/\nfunction foo(a){var t = false; if(t) {return null;}}",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "Missing JSDoc @returns for function.",
@@ -1303,32 +1386,48 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/**\n* Foo\n* @param {string} a desc\n@returns {MyClass}*/\nfunction foo(a){var t = false; if(t) {process(t);}}",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "Unexpected @returns tag; function has no return statement.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 1,
+                endLine: 4,
+                endColumn: 19
             }]
         },
         {
             code: "/**\n * Does something. \n* @param {string} a - this is a \n* @return {Array<number>} The result of doing it \n*/\n export function doSomething(a) { }",
+            output: "/**\n * Does something. \n* @param {string} a - this is a \n* @returns {Array<number>} The result of doing it \n*/\n export function doSomething(a) { }",
             options: [{ prefer: { return: "returns" } }],
             parserOptions: { sourceType: "module" },
             errors: [{
                 message: "Use @returns instead.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 10
             }]
         },
         {
             code: "/**\n * Does something. \n* @param {string} a - this is a \n* @return {Array<number>} The result of doing it \n*/\n export default function doSomething(a) { }",
+            output: "/**\n * Does something. \n* @param {string} a - this is a \n* @returns {Array<number>} The result of doing it \n*/\n export default function doSomething(a) { }",
             options: [{ prefer: { return: "returns" } }],
             parserOptions: { sourceType: "module" },
             errors: [{
                 message: "Use @returns instead.",
-                type: "Block"
+                type: "Block",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 10
             }]
         },
         {
             code: "/** foo */ var foo = () => bar();",
+            output: null,
             options: [{ requireReturn: false }],
             parserOptions: { ecmaVersion: 6 },
             errors: [{
@@ -1338,6 +1437,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** foo */ var foo = () => { return bar(); };",
+            output: null,
             options: [{ requireReturn: false }],
             parserOptions: { ecmaVersion: 6 },
             errors: [{
@@ -1347,28 +1447,39 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** @returns {object} foo */ var foo = () => { bar(); };",
+            output: null,
             options: [{ requireReturn: false }],
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 message: "Unexpected @returns tag; function has no return statement.",
-                type: "Block"
+                type: "Block",
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
             code: "/**\n* @param fields [Array]\n */\n function foo(){}",
+            output: null,
             errors: [
                 {
-                    message: "Missing JSDoc parameter type for 'fields'.",
+                    message: "Missing JSDoc @returns for function.",
                     type: "Block"
                 },
                 {
-                    message: "Missing JSDoc @returns for function.",
-                    type: "Block"
+                    message: "Missing JSDoc parameter type for 'fields'.",
+                    type: "Block",
+                    line: 2,
+                    column: 3,
+                    endLine: 2,
+                    endColumn: 24
                 }
             ]
         },
         {
             code: "/**\n* Start with caps and end with period\n* @return {void} */\nfunction foo(){}",
+            output: null,
             options: [{
                 matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$"
             }],
@@ -1379,6 +1490,7 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@return Foo\n */\nfunction foo(){}",
+            output: null,
             options: [{ prefer: { return: "return" } }],
             errors: [{
                 message: "Missing JSDoc return type.",
@@ -1387,13 +1499,18 @@ ruleTester.run("valid-jsdoc", rule, {
         },
         {
             code: "/** Foo \n@return sdf\n */\nfunction foo(){}",
+            output: null,
             options: [{
                 prefer: { return: "return" },
                 requireReturn: false
             }],
             errors: [{
                 message: "Unexpected @return tag; function has no return statement.",
-                type: "Block"
+                type: "Block",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 12
             }]
         },
 
@@ -1412,6 +1529,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 "        this.a = xs;" +
                 "    }\n" +
                 "}",
+            output: null,
             options: [{
                 requireReturn: false,
                 matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$"
@@ -1444,6 +1562,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 "        this.a = xs;" +
                 "    }\n" +
                 "};",
+            output: null,
             options: [{
                 requireReturn: true,
                 matchDescription: "^[A-Z][A-Za-z0-9\\s]*[.]$"
@@ -1483,6 +1602,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 "        this.a = xs;" +
                 "    }\n" +
                 "}",
+            output: null,
             options: [],
             parserOptions: {
                 ecmaVersion: 6
@@ -1505,6 +1625,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 " * @this {not.a.valid.type.expression\n" +
                 " */\n" +
                 "function foo() {}",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "JSDoc type missing brace.",
@@ -1518,6 +1639,7 @@ ruleTester.run("valid-jsdoc", rule, {
                 " * @this {Array<string>}\n" +
                 " */\n" +
                 "function foo() {}",
+            output: null,
             options: [{ requireReturn: false }],
             errors: [{
                 message: "JSDoc syntax error.",
@@ -1534,6 +1656,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Astnode} returns a node\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {string} hi - desc\n" +
+            "* @returns {ASTNode} returns a node\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     String: "string",
@@ -1543,11 +1672,19 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 11,
+                    endLine: 3,
+                    endColumn: 17
                 },
                 {
                     message: "Use 'ASTNode' instead of 'Astnode'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 4,
+                    column: 13,
+                    endLine: 4,
+                    endColumn: 20
                 }
             ]
         },
@@ -1559,6 +1696,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Astnode} returns a node\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {{20:string}} hi - desc\n" +
+            "* @returns {ASTNode} returns a node\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     String: "string",
@@ -1568,11 +1712,19 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 15,
+                    endLine: 3,
+                    endColumn: 21
                 },
                 {
                     message: "Use 'ASTNode' instead of 'Astnode'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 4,
+                    column: 13,
+                    endLine: 4,
+                    endColumn: 20
                 }
             ]
         },
@@ -1584,6 +1736,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Astnode} returns a node\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {String|number|Test} hi - desc\n" +
+            "* @returns {Astnode} returns a node\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     test: "Test"
@@ -1592,7 +1751,11 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'Test' instead of 'test'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 25,
+                    endLine: 3,
+                    endColumn: 29
                 }
             ]
         },
@@ -1604,6 +1767,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Astnode} returns a node\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {Array.<string>} hi - desc\n" +
+            "* @returns {Astnode} returns a node\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     String: "string",
@@ -1613,7 +1783,11 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 18,
+                    endLine: 3,
+                    endColumn: 24
                 }
             ]
         },
@@ -1625,6 +1799,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Array.<{summary: String}>} desc\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {Array.<{id: number, votes: number}>} hi - desc\n" +
+            "* @returns {Array.<{summary: string}>} desc\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     Number: "number",
@@ -1634,15 +1815,27 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'number' instead of 'Number'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 23,
+                    endLine: 3,
+                    endColumn: 29
                 },
                 {
                     message: "Use 'number' instead of 'Number'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 38,
+                    endLine: 3,
+                    endColumn: 44
                 },
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 4,
+                    column: 30,
+                    endLine: 4,
+                    endColumn: 36
                 }
             ]
         },
@@ -1654,6 +1847,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Array.<[String, String]>} desc\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {Array.<[string, number]>} hi - desc\n" +
+            "* @returns {Array.<[string, string]>} desc\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     Number: "number",
@@ -1663,19 +1863,35 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 19,
+                    endLine: 3,
+                    endColumn: 25
                 },
                 {
                     message: "Use 'number' instead of 'Number'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 27,
+                    endLine: 3,
+                    endColumn: 33
                 },
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 4,
+                    column: 21,
+                    endLine: 4,
+                    endColumn: 27
                 },
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 4,
+                    column: 29,
+                    endLine: 4,
+                    endColumn: 35
                 }
             ]
         },
@@ -1687,6 +1903,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {Boolean} desc\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {Object<string,Object<string, number>>} hi - because why not\n" +
+            "* @returns {Boolean} desc\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     Number: "number",
@@ -1696,24 +1919,44 @@ ruleTester.run("valid-jsdoc", rule, {
             }],
             errors: [
                 {
-                    message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    message: "Use 'Object' instead of 'object'.",
+                    type: "Block",
+                    line: 3,
+                    column: 11,
+                    endLine: 3,
+                    endColumn: 17
                 },
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 18,
+                    endLine: 3,
+                    endColumn: 24
                 },
                 {
-                    message: "Use 'number' instead of 'Number'.",
-                    type: "Block"
+                    message: "Use 'Object' instead of 'object'.",
+                    type: "Block",
+                    line: 3,
+                    column: 25,
+                    endLine: 3,
+                    endColumn: 31
                 },
                 {
-                    message: "Use 'Object' instead of 'object'.",
-                    type: "Block"
+                    message: "Use 'string' instead of 'String'.",
+                    type: "Block",
+                    line: 3,
+                    column: 32,
+                    endLine: 3,
+                    endColumn: 38
                 },
                 {
-                    message: "Use 'Object' instead of 'object'.",
-                    type: "Block"
+                    message: "Use 'number' instead of 'Number'.",
+                    type: "Block",
+                    line: 3,
+                    column: 40,
+                    endLine: 3,
+                    endColumn: 46
                 }
             ]
         },
@@ -1727,6 +1970,13 @@ ruleTester.run("valid-jsdoc", rule, {
             "* @returns {ASTnode} returns a node\n" +
             "*/\n" +
             "function foo(hi){}",
+            output:
+            "/**\n" +
+            "* Foo\n" +
+            "* @param {{foo:string, astnode:Object, bar}} hi - desc\n" +
+            "* @returns {ASTnode} returns a node\n" +
+            "*/\n" +
+            "function foo(hi){}",
             options: [{
                 preferType: {
                     String: "string",
@@ -1736,7 +1986,11 @@ ruleTester.run("valid-jsdoc", rule, {
             errors: [
                 {
                     message: "Use 'string' instead of 'String'.",
-                    type: "Block"
+                    type: "Block",
+                    line: 3,
+                    column: 16,
+                    endLine: 3,
+                    endColumn: 22
                 }
             ]
         }