Repository: eslint/eslint
Pull request #8748 (2017-06-16 08:15:08)

Fix: Add lastToken check at padding-line-between-statements
**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[X] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

**Tell us about your environment**

* **ESLint Version:** v4.0.0
* **Node Version:** v8.1.0
* **npm Version:** 5.0.3

**What parser (default, Babel-ESLint, etc.) are you using?**
 babel-eslint

**Please show your full configuration:**
```yaml
---
  extends: airbnb-base
  parser: babel-eslint

  settings:
    import/resolver:
      node:
        extensions:
          - .js
          - .ios.js
          - .androis.js
        moduleDirectory:
          - node_modules
          - js

  plugins:
    - babel
    - react
    - import

  parserOptions:
    ecmaVersion: 7
    sourceType: module
    ecmaFeatures:
      jsx: true
      modules: true

  env:
    es6: true
    node: true
    jest: true

  globals:
    __DEV__: true
    fetch: true
    XMLHttpRequest: true

  rules:
    import/no-unresolved:
      - 2
      - { ignore: [ '^[app]' ] }
    import/extensions: 0
    import/no-extraneous-dependencies: 0
    import/prefer-default-export: 0

    strict: [2, 'never']
    no-debugger: 2
    arrow-body-style: 0
    no-return-assign: 0
    no-plusplus: 0
    class-methods-use-this: 0
    no-underscore-dangle: 0

    brace-style:
      - 2
      - stroustrup
      - { allowSingleLine: true }

    template-curly-spacing: [2, 'always']
    array-bracket-spacing: [2, 'always']

    array-element-newline: [2, { multiline: true }]

    key-spacing:
      - 2
      -
        align:
          afterColon: true
          on: value

    padding-line-between-statements:
        - 2
        - { blankLine: 'always', prev: 'block-like', next: '*' }
        - { blankLine: 'always', prev: '*', next: 'block-like' }

        - { blankLine: 'always', prev: '*', next: 'return' }

        - { blankLine: 'always', prev: '*', next: 'function' }
        - { blankLine: 'always', prev: 'function', next: '*' }

        - { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*'}
        - { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var']}
        - { blankLine: 'always', prev: 'expression', next: ['const', 'let', 'var']}

        - { blankLine: 'always', prev: 'import', next: '*'}
        - { blankLine: 'any', prev: 'import', next: 'import'}

    id-length:
      - 2
      - { properties: 'never', exceptions: ['x', 'y', 'i', '_', 'e'] }

    jsx-quotes: [2, 'prefer-single']
    react/jsx-filename-extension: 0
    react/prefer-stateless-function: 0
    react/jsx-uses-react: 2
    react/jsx-uses-vars: 2
    react/jsx-first-prop-new-line: [2, 'multiline']
    react/jsx-indent-props: [2, 2]
    react/react-in-jsx-scope: 2
    react/jsx-curly-spacing: [2, 'always']
    react/jsx-equals-spacing: [2, 'always']
    react/forbid-prop-types:
      - 2
      - forbid: ['any', 'array']
    react/sort-comp:
      - 2
      -
        groups:
          lifecycle:
            - displayName
            - propTypes
            - contextTypes
            - childContextTypes
            - mixins
            - statics
            - defaultProps
            - state
            - constructor
            - getDefaultProps
            - getInitialState
            - getChildContext
            - componentWillMount
            - componentDidMount
            - componentWillReceiveProps
            - shouldComponentUpdate
            - componentWillUpdate
            - componentDidUpdate
            - componentWillLeave
            - componentWillUnmount
```

**What did you do? Please include the actual source code causing the issue.**

```js
import AppNavigator from './AppNavigator';

const initialState = AppNavigator.router.getStateForAction(
  AppNavigator.router.getActionForPathAndParams('Login'),
);

export function reducer(state = initialState, action) {
  const nextState = AppNavigator.router.getStateForAction(action, state);

  return nextState || state;
}
```

**What did you expect to happen?**
I expected eslint to lint my code

**What actually happened? Please include the actual, raw output from ESLint.**

```
$(yarn bin)/eslint js/**/*.js *.js
Cannot read property 'value' of null
TypeError: Cannot read property 'value' of null
    at Object.isClosingBraceToken (/Users/outpunk/Code/App/node_modules/eslint/lib/ast-utils.js:364:17)
    at isBlockLikeStatement (/Users/outpunk/Code/App/node_modules/eslint/lib/rules/padding-line-between-statements.js:96:36)
    at Object.test (/Users/outpunk/Code/App/node_modules/eslint/lib/rules/padding-line-between-statements.js:331:37)
    at match (/Users/outpunk/Code/App/node_modules/eslint/lib/rules/padding-line-between-statements.js:473:41)
    at getPaddingType (/Users/outpunk/Code/App/node_modules/eslint/lib/rules/padding-line-between-statements.js:489:21)
    at Linter.verify (/Users/outpunk/Code/App/node_modules/eslint/lib/rules/padding-line-between-statements.js:551:30)
    at emitOne (events.js:115:13)
    at Linter.emit (events.js:210:7)
    at NodeEventGenerator.applySelector (/Users/outpunk/Code/App/node_modules/eslint/lib/util/node-event-generator.js:265:26)
    at NodeEventGenerator.applySelectors (/Users/outpunk/Code/App/node_modules/eslint/lib/util/node-event-generator.js:292:22)
```


<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (http://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)**

I have added check for null value of `lastToken` at `isBlockLikeStatement` in `lib/rules/padding-line-between-statements.js`


**Is there anything you'd like reviewers to focus on?**
No

jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=8748) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint?pullRequest=8748) before we can accept your contribution.<br/>
eslintbot: Thanks for the pull request, @outpunk! I took a look to make sure it's ready for merging and found some changes are needed:


* The commit summary must be 72 characters or shorter. Please check out our [guide](http://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) for how to properly format your commit summary and [update](http://eslint.org/docs/developer-guide/contributing/pull-requests#updating-the-commit-message) it on this pull request.


Can you please update the pull request to address these?

(More information can be found in our [pull request guide](http://eslint.org/docs/developer-guide/contributing/pull-requests).)
eslintbot: LGTM
eslintbot: LGTM
gyandeeps: Is the issue still present if you just use default parser and not `babel-eslint`?
Asking because I want to makesure its not a issue with babel-eslint.
outpunk: @gyandeeps Can't reproduce today, with both of the parsers
outpunk: @gyandeeps reproduced, the same thing with the default parser
outpunk: @gyandeeps any news?
platinumazure: @outpunk Can you add a test for this change, which fails without your code change and passes with? Thanks!
outpunk: @platinumazure I'll try
kaicataldo: Marking as accepted because it is the same bug as https://github.com/eslint/eslint/issues/8839.

@outpunk Hi! I missed this PR and [also made a PR to fix this](https://github.com/eslint/eslint/pull/8840), though I like your solution better.

I don't all my tests need to be ported over because this is a more generic solution (which is why I like it better!). I think the important test from the tests I wrote is to check that this is valid:

```js
{
     code: "function test() {};",
     options: [
         { blankLine: "always", prev: "block-like", next: "block-like" }
     ]
},
```
The other tests are tied more to my specific implementation, so I don't think you need to add those.
outpunk: @kaicataldo thanks! I'll add your tests in a couple of days — I'm a bit busy until next week.
ilyavolodin: @outpunk Friendly ping
outpunk: Thanks, will do tomorrow 

> On 21 Jul 2017, at 21:56, Ilya Volodin <notifications@github.com> wrote:
> 
> @outpunk Friendly ping
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.
> 

eslintbot: LGTM
outpunk: @platinumazure I've added the test
kaicataldo: Restarting the Travis CI tests because it looks like they timed out.

Patch:
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 645e328d9..7c8224968 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -10,5 +10,6 @@ extends:
 rules:
     eslint-plugin/consistent-output: "error"
     eslint-plugin/no-identical-tests: "error"
+    eslint-plugin/prefer-output-null: "error"
     eslint-plugin/prefer-placeholders: "error"
     eslint-plugin/report-message-format: ["error", '[^a-z].*\.$']
diff --git a/Makefile.js b/Makefile.js
index 3e04619d1..06cbef959 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -115,7 +115,7 @@ function validateJsonFile(filePath) {
  */
 function fileType(extension) {
     return function(filename) {
-        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
+        return filename.slice(filename.lastIndexOf(".") + 1) === extension;
     };
 }
 
diff --git a/conf/config-schema.js b/conf/config-schema.js
index 4ef958c40..626e1d54c 100644
--- a/conf/config-schema.js
+++ b/conf/config-schema.js
@@ -12,7 +12,9 @@ const baseConfigProperties = {
     parserOptions: { type: "object" },
     plugins: { type: "array" },
     rules: { type: "object" },
-    settings: { type: "object" }
+    settings: { type: "object" },
+
+    ecmaFeatures: { type: "object" } // deprecated; logs a warning when used
 };
 
 const overrideProperties = Object.assign(
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 1a2e2f8e7..35c8acdbe 100755
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -6,13 +6,10 @@
 
 "use strict";
 
-/* eslint sort-keys: ["error", "asc"], quote-props: ["error", "consistent"] */
-/* eslint-disable sort-keys */
+/* eslint sort-keys: ["error", "asc"] */
 
 module.exports = {
     rules: {
-
-        /* eslint-enable sort-keys */
         "accessor-pairs": "off",
         "array-bracket-newline": "off",
         "array-bracket-spacing": "off",
@@ -25,23 +22,23 @@ module.exports = {
         "block-spacing": "off",
         "brace-style": "off",
         "callback-return": "off",
-        "camelcase": "off",
+        camelcase: "off",
         "capitalized-comments": "off",
         "class-methods-use-this": "off",
         "comma-dangle": "off",
         "comma-spacing": "off",
         "comma-style": "off",
-        "complexity": "off",
+        complexity: "off",
         "computed-property-spacing": "off",
         "consistent-return": "off",
         "consistent-this": "off",
         "constructor-super": "error",
-        "curly": "off",
+        curly: "off",
         "default-case": "off",
         "dot-location": "off",
         "dot-notation": "off",
         "eol-last": "off",
-        "eqeqeq": "off",
+        eqeqeq: "off",
         "for-direction": "off",
         "func-call-spacing": "off",
         "func-name-matching": "off",
@@ -55,7 +52,7 @@ module.exports = {
         "id-blacklist": "off",
         "id-length": "off",
         "id-match": "off",
-        "indent": "off",
+        indent: "off",
         "indent-legacy": "off",
         "init-declarations": "off",
         "jsx-quotes": "off",
@@ -234,13 +231,13 @@ module.exports = {
         "prefer-spread": "off",
         "prefer-template": "off",
         "quote-props": "off",
-        "quotes": "off",
-        "radix": "off",
+        quotes: "off",
+        radix: "off",
         "require-await": "off",
         "require-jsdoc": "off",
         "require-yield": "error",
         "rest-spread-spacing": "off",
-        "semi": "off",
+        semi: "off",
         "semi-spacing": "off",
         "semi-style": "off",
         "sort-imports": "off",
@@ -252,7 +249,7 @@ module.exports = {
         "space-infix-ops": "off",
         "space-unary-ops": "off",
         "spaced-comment": "off",
-        "strict": "off",
+        strict: "off",
         "switch-colon-spacing": "off",
         "symbol-description": "off",
         "template-curly-spacing": "off",
@@ -265,6 +262,6 @@ module.exports = {
         "wrap-iife": "off",
         "wrap-regex": "off",
         "yield-star-spacing": "off",
-        "yoda": "off"
+        yoda: "off"
     }
 };
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 3c790cf3b..87412dd2a 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -3,8 +3,6 @@
  * @author Nicholas C. Zakas
  */
 
-/* eslint no-use-before-define: 0 */
-
 "use strict";
 
 //------------------------------------------------------------------------------
@@ -418,6 +416,8 @@ function applyExtends(config, configContext, filePath, relativeTo) {
                 );
             }
             debug(`Loading ${parentPath}`);
+
+            // eslint-disable-next-line no-use-before-define
             return ConfigOps.merge(load(parentPath, configContext, relativeTo), previousValue);
         } catch (e) {
 
@@ -502,8 +502,8 @@ function resolve(filePath, relativeTo) {
 
     if (filePath.startsWith("plugin:")) {
         const configFullName = filePath;
-        const pluginName = filePath.substr(7, filePath.lastIndexOf("/") - 7);
-        const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
+        const pluginName = filePath.slice(7, filePath.lastIndexOf("/"));
+        const configName = filePath.slice(filePath.lastIndexOf("/") + 1);
 
         normalizedPackageName = normalizePackageName(pluginName, "eslint-plugin");
         debug(`Attempting to resolve ${normalizedPackageName}`);
@@ -546,7 +546,7 @@ function loadFromDisk(resolvedPath, configContext) {
         }
 
         // validate the configuration before continuing
-        validator.validate(config, resolvedPath, configContext.linterContext.rules, configContext.linterContext.environments);
+        validator.validate(config, resolvedPath.configFullName, configContext.linterContext.rules, configContext.linterContext.environments);
 
         /*
          * If an `extends` property is defined, it represents a configuration file to use as
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index fb5a96564..22bc1efbd 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -10,6 +10,7 @@
 //------------------------------------------------------------------------------
 
 const ajv = require("../util/ajv"),
+    lodash = require("lodash"),
     configSchema = require("../../conf/config-schema.js"),
     util = require("util");
 
@@ -179,6 +180,25 @@ function formatErrors(errors) {
     }).map(message => `\t- ${message}.\n`).join("");
 }
 
+/**
+ * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
+ * for each unique file path, but repeated invocations with the same file path have no effect.
+ * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
+ * @param {string} source The name of the configuration source to report the warning for.
+ * @returns {void}
+ */
+const emitEcmaFeaturesWarning = lodash.memoize(source => {
+
+    /*
+     * util.deprecate seems to be the only way to emit a warning in Node 4.x while respecting the --no-warnings flag.
+     * (In Node 6+, process.emitWarning could be used instead.)
+     */
+    util.deprecate(
+        () => {},
+        `[eslint] The 'ecmaFeatures' config file property is deprecated, and has no effect. (found in ${source})`
+    )();
+});
+
 /**
  * Validates the top level properties of the config object.
  * @param {Object} config The config object to validate.
@@ -189,7 +209,11 @@ function validateConfigSchema(config, source) {
     validateSchema = validateSchema || ajv.compile(configSchema);
 
     if (!validateSchema(config)) {
-        throw new Error(`${source}:\n\tESLint configuration is invalid:\n${formatErrors(validateSchema.errors)}`);
+        throw new Error(`ESLint configuration in ${source} is invalid:\n${formatErrors(validateSchema.errors)}`);
+    }
+
+    if (Object.prototype.hasOwnProperty.call(config, "ecmaFeatures")) {
+        emitEcmaFeaturesWarning(source);
     }
 }
 
diff --git a/lib/config/plugins.js b/lib/config/plugins.js
index adfd8a1bb..9884f3603 100644
--- a/lib/config/plugins.js
+++ b/lib/config/plugins.js
@@ -43,7 +43,7 @@ class Plugins {
      * @returns {string} The name of the plugin without prefix.
      */
     static removePrefix(pluginName) {
-        return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.substring(PLUGIN_NAME_PREFIX.length) : pluginName;
+        return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.slice(PLUGIN_NAME_PREFIX.length) : pluginName;
     }
 
     /**
diff --git a/lib/linter.js b/lib/linter.js
index ce759756e..bb17ac216 100755
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -70,8 +70,8 @@ function parseBooleanConfig(string, comment) {
         let value;
 
         if (pos !== -1) {
-            value = name.substring(pos + 1, name.length);
-            name = name.substring(0, pos);
+            value = name.slice(pos + 1);
+            name = name.slice(0, pos);
         }
 
         items[name] = {
@@ -338,7 +338,7 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
         const match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(value);
 
         if (match) {
-            value = value.substring(match.index + match[1].length);
+            value = value.slice(match.index + match[1].length);
 
             if (comment.type === "Block") {
                 switch (match[1]) {
@@ -519,8 +519,11 @@ function createStubRule(message) {
      */
     function createRuleModule(context) {
         return {
-            Program(node) {
-                context.report(node, message);
+            Program() {
+                context.report({
+                    loc: { line: 1, column: 0 },
+                    message
+                });
             }
         };
     }
@@ -1288,13 +1291,18 @@ const externalMethods = {
 Object.keys(externalMethods).forEach(methodName => {
     const exMethodName = externalMethods[methodName];
 
-    // All functions expected to have less arguments than 5.
-    Linter.prototype[methodName] = function(a, b, c, d, e) {
-        if (this.sourceCode) {
-            return this.sourceCode[exMethodName](a, b, c, d, e);
-        }
-        return null;
-    };
+    // Applies the SourceCode methods to the Linter prototype
+    Object.defineProperty(Linter.prototype, methodName, {
+        value() {
+            if (this.sourceCode) {
+                return this.sourceCode[exMethodName].apply(this.sourceCode, arguments);
+            }
+            return null;
+        },
+        configurable: true,
+        writable: true,
+        enumerable: false
+    });
 });
 
 module.exports = Linter;
diff --git a/lib/rule-context.js b/lib/rule-context.js
index 66987b867..0a2dc188d 100644
--- a/lib/rule-context.js
+++ b/lib/rule-context.js
@@ -20,6 +20,7 @@ const PASSTHROUGHS = [
     "getDeclaredVariables",
     "getFilename",
     "getScope",
+    "getSourceCode",
     "markVariableAsUsed",
 
     // DEPRECATED
@@ -58,7 +59,7 @@ const PASSTHROUGHS = [
  */
 
 //------------------------------------------------------------------------------
-// Rule Definition
+// Module Definition
 //------------------------------------------------------------------------------
 
 /**
@@ -132,13 +133,13 @@ function getFix(descriptor, sourceCode) {
 
 /**
  * Rule context class
- * Acts as an abstraction layer between rules and the main eslint object.
+ * Acts as an abstraction layer between rules and the main linter object.
  */
 class RuleContext {
 
     /**
      * @param {string} ruleId The ID of the rule using this object.
-     * @param {eslint} eslint The eslint object.
+     * @param {Linter} linter The linter object.
      * @param {number} severity The configured severity level of the rule.
      * @param {Array} options The configuration information to be added to the rule.
      * @param {Object} settings The configuration settings passed from the config file.
@@ -147,7 +148,7 @@ class RuleContext {
      * @param {Object} meta The metadata of the rule
      * @param {Object} parserServices The parser services for the rule.
      */
-    constructor(ruleId, eslint, severity, options, settings, parserOptions, parserPath, meta, parserServices) {
+    constructor(ruleId, linter, severity, options, settings, parserOptions, parserPath, meta, parserServices) {
 
         // public.
         this.id = ruleId;
@@ -161,22 +162,14 @@ class RuleContext {
         this.parserServices = Object.freeze(Object.assign({}, parserServices));
 
         // private.
-        this.eslint = eslint;
-        this.severity = severity;
+        this._linter = linter;
+        this._severity = severity;
 
         Object.freeze(this);
     }
 
     /**
-     * Passthrough to eslint.getSourceCode().
-     * @returns {SourceCode} The SourceCode object for the code.
-     */
-    getSourceCode() {
-        return this.eslint.getSourceCode();
-    }
-
-    /**
-     * Passthrough to eslint.report() that automatically assigns the rule ID and severity.
+     * Passthrough to Linter#report() that automatically assigns the rule ID and severity.
      * @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
      *      descriptor.
      * @param {Object=} location The location of the error.
@@ -192,38 +185,57 @@ class RuleContext {
             const descriptor = nodeOrDescriptor;
             const fix = getFix(descriptor, this.getSourceCode());
 
-            this.eslint.report(
+            if (descriptor.loc) {
+                this._linter.report(
+                    this.id,
+                    this._severity,
+                    descriptor.node,
+                    descriptor.loc,
+                    descriptor.message,
+                    descriptor.data,
+                    fix,
+                    this.meta
+                );
+            } else {
+                this._linter.report(
+                    this.id,
+                    this._severity,
+                    descriptor.node,
+
+                    /* loc not provided */
+                    descriptor.message,
+                    descriptor.data,
+                    fix,
+                    this.meta
+                );
+            }
+
+        } else {
+
+            // old style call
+            this._linter.report(
                 this.id,
-                this.severity,
-                descriptor.node,
-                descriptor.loc || descriptor.node.loc.start,
-                descriptor.message,
-                descriptor.data,
-                fix,
+                this._severity,
+                nodeOrDescriptor,
+                location,
+                message,
+                opts,
                 this.meta
             );
-
-            return;
         }
-
-        // old style call
-        this.eslint.report(
-            this.id,
-            this.severity,
-            nodeOrDescriptor,
-            location,
-            message,
-            opts,
-            this.meta
-        );
     }
 }
 
-// Copy over passthrough methods. All functions will have 5 or fewer parameters.
-PASSTHROUGHS.forEach(function(name) {
-    this[name] = function(a, b, c, d, e) {
-        return this.eslint[name](a, b, c, d, e);
-    };
-}, RuleContext.prototype);
+// Copy over passthrough methods.
+PASSTHROUGHS.forEach(name => {
+    Object.defineProperty(RuleContext.prototype, name, {
+        value() {
+            return this._linter[name].apply(this._linter, arguments);
+        },
+        configurable: true,
+        writable: true,
+        enumerable: false
+    });
+});
 
 module.exports = RuleContext;
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index f686c18ee..89d98f833 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -729,7 +729,7 @@ module.exports = {
                 if (!parentVarNode || parentVarNode.loc.start.line !== node.loc.start.line) {
                     if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) {
                         if (parent.type === "VariableDeclarator" && parentVarNode.loc.start.line === parent.loc.start.line) {
-                            nodeIndent = nodeIndent + (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]);
+                            nodeIndent += (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]);
                         } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
                             const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
 
@@ -765,7 +765,7 @@ module.exports = {
                         }
                     }
                 } else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent) && parent.type !== "MemberExpression" && parent.type !== "ExpressionStatement" && parent.type !== "AssignmentExpression" && parent.type !== "Property") {
-                    nodeIndent = nodeIndent + indentSize;
+                    nodeIndent += indentSize;
                 }
 
                 checkFirstNodeLineIndent(node, nodeIndent);
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index ec3f79170..0d4c7188b 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -1229,7 +1229,7 @@ module.exports = {
             },
 
             Property(node) {
-                if (!node.computed && !node.shorthand && !node.method && node.kind === "init") {
+                if (!node.shorthand && !node.method && node.kind === "init") {
                     const colon = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isColonToken);
 
                     offsets.ignoreToken(sourceCode.getTokenAfter(colon));
diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js
index 30d650c3a..73e514335 100644
--- a/lib/rules/no-multi-spaces.js
+++ b/lib/rules/no-multi-spaces.js
@@ -44,68 +44,11 @@ module.exports = {
     },
 
     create(context) {
-
-        // the index of the last comment that was checked
-        const sourceCode = context.getSourceCode(),
-            exceptions = { Property: true },
-            options = context.options[0] || {},
-            ignoreEOLComments = options.ignoreEOLComments;
-        let hasExceptions = true,
-            lastCommentIndex = 0;
-
-        if (options && options.exceptions) {
-            Object.keys(options.exceptions).forEach(key => {
-                if (options.exceptions[key]) {
-                    exceptions[key] = true;
-                } else {
-                    delete exceptions[key];
-                }
-            });
-            hasExceptions = Object.keys(exceptions).length > 0;
-        }
-
-        /**
-         * Checks if a given token is the last token of the line or not.
-         * @param {Token} token The token to check.
-         * @returns {boolean} Whether or not a token is at the end of the line it occurs in.
-         * @private
-         */
-        function isLastTokenOfLine(token) {
-            const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
-
-            // nextToken is null if the comment is the last token in the program.
-            if (!nextToken) {
-                return true;
-            }
-
-            return !astUtils.isTokenOnSameLine(token, nextToken);
-        }
-
-        /**
-         * Determines if a given source index is in a comment or not by checking
-         * the index against the comment range. Since the check goes straight
-         * through the file, once an index is passed a certain comment, we can
-         * go to the next comment to check that.
-         * @param {int} index The source index to check.
-         * @param {ASTNode[]} comments An array of comment nodes.
-         * @returns {boolean} True if the index is within a comment, false if not.
-         * @private
-         */
-        function isIndexInComment(index, comments) {
-            while (lastCommentIndex < comments.length) {
-                const comment = comments[lastCommentIndex];
-
-                if (comment.range[0] < index && index < comment.range[1]) {
-                    return true;
-                } else if (index > comment.range[1]) {
-                    lastCommentIndex++;
-                } else {
-                    break;
-                }
-            }
-
-            return false;
-        }
+        const sourceCode = context.getSourceCode();
+        const options = context.options[0] || {};
+        const ignoreEOLComments = options.ignoreEOLComments;
+        const exceptions = Object.assign({ Property: true }, options.exceptions);
+        const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
 
         /**
          * Formats value of given comment token for error message by truncating its length.
@@ -116,75 +59,67 @@ module.exports = {
         function formatReportedCommentValue(token) {
             const valueLines = token.value.split("\n");
             const value = valueLines[0];
-            const formattedValue = `${value.substring(0, 12)}...`;
+            const formattedValue = `${value.slice(0, 12)}...`;
 
             return valueLines.length === 1 && value.length <= 12 ? value : formattedValue;
         }
 
-        /**
-         * Creates a fix function that removes the multiple spaces between the two tokens
-         * @param {Token} leftToken left token
-         * @param {Token} rightToken right token
-         * @returns {Function} fix function
-         * @private
-         */
-        function createFix(leftToken, rightToken) {
-            return function(fixer) {
-                return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
-            };
-        }
-
         //--------------------------------------------------------------------------
         // Public
         //--------------------------------------------------------------------------
 
         return {
             Program() {
+                sourceCode.tokensAndComments.forEach((leftToken, leftIndex, tokensAndComments) => {
+                    if (leftIndex === tokensAndComments.length - 1) {
+                        return;
+                    }
+                    const rightToken = tokensAndComments[leftIndex + 1];
 
-                const source = sourceCode.getText(),
-                    allComments = sourceCode.getAllComments(),
-                    pattern = /[^\s].*? {2,}/g;
-                let parent;
-
-                while (pattern.test(source)) {
-
-                    // do not flag anything inside of comments
-                    if (!isIndexInComment(pattern.lastIndex, allComments)) {
-
-                        const token = sourceCode.getTokenByRangeStart(pattern.lastIndex, { includeComments: true });
-
-                        if (token) {
-                            if (ignoreEOLComments && astUtils.isCommentToken(token) && isLastTokenOfLine(token)) {
-                                return;
-                            }
+                    // Ignore tokens that have less than 2 spaces between them or are on different lines
+                    if (leftToken.range[1] + 2 > rightToken.range[0] || leftToken.loc.end.line < rightToken.loc.start.line) {
+                        return;
+                    }
 
-                            const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
+                    // Ignore comments that are the last token on their line if `ignoreEOLComments` is active.
+                    if (
+                        ignoreEOLComments &&
+                        astUtils.isCommentToken(rightToken) &&
+                        (
+                            leftIndex === tokensAndComments.length - 2 ||
+                            rightToken.loc.end.line < tokensAndComments[leftIndex + 2].loc.start.line
+                        )
+                    ) {
+                        return;
+                    }
 
-                            if (hasExceptions) {
-                                parent = sourceCode.getNodeByRangeIndex(pattern.lastIndex - 1);
-                            }
+                    // Ignore tokens that are in a node in the "exceptions" object
+                    if (hasExceptions) {
+                        const parentNode = sourceCode.getNodeByRangeIndex(rightToken.range[0] - 1);
 
-                            if (!parent || !exceptions[parent.type]) {
-                                let value = token.value;
-
-                                if (token.type === "Block") {
-                                    value = `/*${formatReportedCommentValue(token)}*/`;
-                                } else if (token.type === "Line") {
-                                    value = `//${formatReportedCommentValue(token)}`;
-                                }
-
-                                context.report({
-                                    node: token,
-                                    loc: token.loc.start,
-                                    message: "Multiple spaces found before '{{value}}'.",
-                                    data: { value },
-                                    fix: createFix(previousToken, token)
-                                });
-                            }
+                        if (parentNode && exceptions[parentNode.type]) {
+                            return;
                         }
+                    }
 
+                    let displayValue;
+
+                    if (rightToken.type === "Block") {
+                        displayValue = `/*${formatReportedCommentValue(rightToken)}*/`;
+                    } else if (rightToken.type === "Line") {
+                        displayValue = `//${formatReportedCommentValue(rightToken)}`;
+                    } else {
+                        displayValue = rightToken.value;
                     }
-                }
+
+                    context.report({
+                        node: rightToken,
+                        loc: rightToken.loc.start,
+                        message: "Multiple spaces found before '{{displayValue}}'.",
+                        data: { displayValue },
+                        fix: fixer => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ")
+                    });
+                });
             }
         };
 
diff --git a/lib/rules/padding-line-between-statements.js b/lib/rules/padding-line-between-statements.js
index 707cf33d8..413327dfd 100644
--- a/lib/rules/padding-line-between-statements.js
+++ b/lib/rules/padding-line-between-statements.js
@@ -93,7 +93,7 @@ function isBlockLikeStatement(sourceCode, node) {
 
     // Checks the last token is a closing brace of blocks.
     const lastToken = sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
-    const belongingNode = astUtils.isClosingBraceToken(lastToken)
+    const belongingNode = lastToken && astUtils.isClosingBraceToken(lastToken)
         ? sourceCode.getNodeByRangeIndex(lastToken.range[0])
         : null;
 
diff --git a/lib/rules/space-infix-ops.js b/lib/rules/space-infix-ops.js
index ad514b28f..b92c889c7 100644
--- a/lib/rules/space-infix-ops.js
+++ b/lib/rules/space-infix-ops.js
@@ -112,7 +112,7 @@ module.exports = {
             const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
 
             if (nonSpacedNode) {
-                if (!(int32Hint && sourceCode.getText(node).substr(-2) === "|0")) {
+                if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
                     report(node, nonSpacedNode);
                 }
             }
diff --git a/package.json b/package.json
index 984592b1f..462170b0d 100644
--- a/package.json
+++ b/package.json
@@ -84,7 +84,7 @@
     "coveralls": "^2.13.1",
     "dateformat": "^2.0.0",
     "ejs": "^2.5.6",
-    "eslint-plugin-eslint-plugin": "^0.7.4",
+    "eslint-plugin-eslint-plugin": "^0.8.0",
     "eslint-plugin-node": "^5.1.0",
     "eslint-release": "^0.10.1",
     "eslump": "1.6.0",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index cdeac9ad4..f1d848ca3 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -10,7 +10,7 @@ rules:
     indent: ["error", 4, {SwitchCase: 1}]
     block-spacing: "error"
     brace-style: ["error", "1tbs"]
-    camelcase: ["error", { properties: "never" }]
+    camelcase: "error"
     callback-return: ["error", ["cb", "callback", "next"]]
     class-methods-use-this: "error"
     comma-dangle: "error"
@@ -20,6 +20,7 @@ rules:
     consistent-return: "error"
     curly: ["error", "all"]
     default-case: "error"
+    dot-location: ["error", "property"]
     dot-notation: ["error", { allowKeywords: true }]
     eol-last: "error"
     eqeqeq: "error"
@@ -27,7 +28,9 @@ rules:
     func-call-spacing: "error"
     func-style: ["error", "declaration"]
     generator-star-spacing: "error"
+    getter-return: "error"
     guard-for-in: "error"
+    handle-callback-err: ["error", "err"]
     key-spacing: ["error", { beforeColon: false, afterColon: true }]
     keyword-spacing: "error"
     lines-around-comment: ["error", {
@@ -41,7 +44,9 @@ rules:
     new-parens: "error"
     no-alert: "error"
     no-array-constructor: "error"
+    no-buffer-constructor: "error"
     no-caller: "error"
+    no-catch-shadow: "error"
     no-confusing-arrow: "error"
     no-console: "error"
     no-delete-var: "error"
@@ -59,6 +64,7 @@ rules:
     no-labels: "error"
     no-lone-blocks: "error"
     no-loop-func: "error"
+    no-mixed-requires: "error"
     no-mixed-spaces-and-tabs: ["error", false]
     no-multi-spaces: "error"
     no-multi-str: "error"
@@ -67,19 +73,28 @@ rules:
     no-new: "error"
     no-new-func: "error"
     no-new-object: "error"
+    no-new-require: "error"
     no-new-wrappers: "error"
     no-octal: "error"
     no-octal-escape: "error"
+    no-path-concat: "error"
     no-process-exit: "error"
     no-proto: "error"
     no-redeclare: "error"
+    no-restricted-properties: [
+        "error",
+        { property: "substring", message: "Use .slice instead of .substring." },
+        { property: "substr", message: "Use .slice instead of .substr." }
+    ]
     no-return-assign: "error"
     no-script-url: "error"
     no-self-assign: "error"
+    no-self-compare: "error"
     no-sequences: "error"
     no-shadow: "error"
     no-shadow-restricted-names: "error"
     no-tabs: "error"
+    no-throw-literal: "error"
     no-trailing-spaces: "error"
     no-undef: ["error", {typeof: true}]
     no-undef-init: "error"
@@ -90,16 +105,20 @@ rules:
     no-unused-expressions: "error"
     no-unused-vars: ["error", {vars: "all", args: "after-used"}]
     no-use-before-define: "error"
+    no-useless-call: "error"
     no-useless-computed-key: "error"
     no-useless-concat: "error"
     no-useless-constructor: "error"
     no-useless-escape: "error"
+    no-useless-rename: "error"
     no-useless-return: "error"
+    no-whitespace-before-property: "error"
     no-with: "error"
     no-var: "error"
     object-curly-spacing: ["error", "always"]
     object-shorthand: "error"
     one-var-declaration-per-line: "error"
+    operator-assignment: "error"
     operator-linebreak: "error"
     padding-line-between-statements: [
         "error",
@@ -116,6 +135,7 @@ rules:
     ]
     prefer-arrow-callback: "error"
     prefer-const: "error"
+    prefer-numeric-literals: "error"
     prefer-promise-reject-errors: "error"
     prefer-template: "error"
     quotes: ["error", "double"]
@@ -124,6 +144,7 @@ rules:
     require-jsdoc: "error"
     semi: "error"
     semi-spacing: ["error", {before: false, after: true}]
+    semi-style: "error"
     space-before-blocks: "error"
     space-before-function-paren: ["error", "never"]
     space-in-parens: "error"
@@ -131,8 +152,11 @@ rules:
     space-unary-ops: ["error", {words: true, nonwords: false}]
     spaced-comment: ["error", "always", { exceptions: ["-"]}]
     strict: ["error", "global"]
+    switch-colon-spacing: "error"
+    symbol-description: "error"
     template-curly-spacing: ["error", "never"]
     template-tag-spacing: "error"
+    unicode-bom: "error"
     valid-jsdoc: ["error", {
         prefer: { "return": "returns"},
         preferType: {
@@ -146,10 +170,3 @@ rules:
     wrap-iife: "error"
     yield-star-spacing: "error"
     yoda: ["error", "never"]
-
-    # Previously on by default in node environment
-    no-catch-shadow: "off"
-    no-mixed-requires: "error"
-    no-new-require: "error"
-    no-path-concat: "error"
-    handle-callback-err: ["error", "err"]


Test patch:
diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index fc6781fdc..acd3bee7c 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -9,7 +9,7 @@
 /*
  * This is a wrapper around mocha to allow for DRY unittests for eslint
  * Format:
- * RuleTester.add("{ruleName}", {
+ * RuleTester.run("{ruleName}", {
  *      valid: [
  *          "{code}",
  *          { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings} }
@@ -250,7 +250,6 @@ class RuleTester {
         const testerConfig = this.testerConfig,
             requiredScenarios = ["valid", "invalid"],
             scenarioErrors = [],
-            result = {},
             linter = this.linter;
 
         if (lodash.isNil(test) || typeof test !== "object") {
@@ -269,16 +268,13 @@ class RuleTester {
             ].concat(scenarioErrors).join("\n"));
         }
 
-        /* eslint-disable no-shadow */
-
         /**
          * Run the rule for the given item
-         * @param {string} ruleName name of the rule
          * @param {string|Object} item Item to run the rule against
          * @returns {Object} Eslint run result
          * @private
          */
-        function runRuleForItem(ruleName, item) {
+        function runRuleForItem(item) {
             let config = lodash.cloneDeep(testerConfig),
                 code, filename, beforeAST, afterAST;
 
@@ -350,27 +346,27 @@ class RuleTester {
 
             try {
                 linter.rules.get = function(ruleId) {
-                    const rule = originalGet.call(linter.rules, ruleId);
+                    const originalRule = originalGet.call(linter.rules, ruleId);
 
-                    if (typeof rule === "function") {
+                    if (typeof originalRule === "function") {
                         return function(context) {
                             Object.freeze(context);
                             freezeDeeply(context.options);
                             freezeDeeply(context.settings);
                             freezeDeeply(context.parserOptions);
 
-                            return rule(context);
+                            return originalRule(context);
                         };
                     }
                     return {
-                        meta: rule.meta,
+                        meta: originalRule.meta,
                         create(context) {
                             Object.freeze(context);
                             freezeDeeply(context.options);
                             freezeDeeply(context.settings);
                             freezeDeeply(context.parserOptions);
 
-                            return rule.create(context);
+                            return originalRule.create(context);
                         }
                     };
 
@@ -404,13 +400,12 @@ class RuleTester {
         /**
          * Check if the template is valid or not
          * all valid cases go through this
-         * @param {string} ruleName name of the rule
          * @param {string|Object} item Item to run the rule against
          * @returns {void}
          * @private
          */
-        function testValidTemplate(ruleName, item) {
-            const result = runRuleForItem(ruleName, item);
+        function testValidTemplate(item) {
+            const result = runRuleForItem(item);
             const messages = result.messages;
 
             assert.equal(messages.length, 0, util.format("Should have no errors but had %d: %s",
@@ -444,16 +439,15 @@ class RuleTester {
         /**
          * Check if the template is invalid or not
          * all invalid cases go through this.
-         * @param {string} ruleName name of the rule
          * @param {string|Object} item Item to run the rule against
          * @returns {void}
          * @private
          */
-        function testInvalidTemplate(ruleName, item) {
+        function testInvalidTemplate(item) {
             assert.ok(item.errors || item.errors === 0,
                 `Did not specify errors for an invalid test of ${ruleName}`);
 
-            const result = runRuleForItem(ruleName, item);
+            const result = runRuleForItem(item);
             const messages = result.messages;
 
 
@@ -543,7 +537,7 @@ class RuleTester {
                 test.valid.forEach(valid => {
                     RuleTester.it(typeof valid === "object" ? valid.code : valid, () => {
                         linter.defineRules(this.rules);
-                        testValidTemplate(ruleName, valid);
+                        testValidTemplate(valid);
                     });
                 });
             });
@@ -552,13 +546,11 @@ class RuleTester {
                 test.invalid.forEach(invalid => {
                     RuleTester.it(invalid.code, () => {
                         linter.defineRules(this.rules);
-                        testInvalidTemplate(ruleName, invalid);
+                        testInvalidTemplate(invalid);
                     });
                 });
             });
         });
-
-        return result.suite;
     }
 }
 
diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js
index 0774500f0..1c45aa429 100644
--- a/tests/bin/eslint.js
+++ b/tests/bin/eslint.js
@@ -280,6 +280,28 @@ describe("bin/eslint.js", () => {
         });
     });
 
+
+    describe("emitting a warning for ecmaFeatures", () => {
+        it("does not emit a warning when it does not find an ecmaFeatures option", () => {
+            const child = runESLint(["Makefile.js"]);
+
+            const exitCodePromise = assertExitCode(child, 0);
+            const outputPromise = getOutput(child).then(output => assert.strictEqual(output.stderr, ""));
+
+            return Promise.all([exitCodePromise, outputPromise]);
+        });
+        it("emits a warning when it finds an ecmaFeatures option", () => {
+            const child = runESLint(["-c", "tests/fixtures/config-file/ecma-features/.eslintrc.yml", "Makefile.js"]);
+
+            const exitCodePromise = assertExitCode(child, 0);
+            const outputPromise = getOutput(child).then(output => {
+                assert.include(output.stderr, "The 'ecmaFeatures' config file property is deprecated, and has no effect.");
+            });
+
+            return Promise.all([exitCodePromise, outputPromise]);
+        });
+    });
+
     afterEach(() => {
 
         // Clean up all the processes after every test.
diff --git a/tests/fixtures/config-file/ecma-features/.eslintrc.yml b/tests/fixtures/config-file/ecma-features/.eslintrc.yml
new file mode 100644
index 000000000..c8538f3c4
--- /dev/null
+++ b/tests/fixtures/config-file/ecma-features/.eslintrc.yml
@@ -0,0 +1,1 @@
+ecmaFeatures: {}
diff --git a/tests/fixtures/config-file/invalid/invalid-top-level-property.yml b/tests/fixtures/config-file/invalid/invalid-top-level-property.yml
new file mode 100644
index 000000000..7ea8ce744
--- /dev/null
+++ b/tests/fixtures/config-file/invalid/invalid-top-level-property.yml
@@ -0,0 +1,1 @@
+invalidProperty: 3
diff --git a/tests/lib/cli-engine.js b/tests/lib/cli-engine.js
index 7529d8edf..104118888 100644
--- a/tests/lib/cli-engine.js
+++ b/tests/lib/cli-engine.js
@@ -355,6 +355,8 @@ describe("CLIEngine", () => {
                                 message: "'foo' is not defined.",
                                 line: 1,
                                 column: 11,
+                                endLine: 1,
+                                endColumn: 14,
                                 nodeType: "Identifier",
                                 source: "var bar = foo"
                             }
@@ -1413,6 +1415,8 @@ describe("CLIEngine", () => {
                                 {
                                     column: 18,
                                     line: 1,
+                                    endColumn: 21,
+                                    endLine: 1,
                                     message: "'foo' is not defined.",
                                     nodeType: "Identifier",
                                     ruleId: "no-undef",
diff --git a/tests/lib/config.js b/tests/lib/config.js
index 59e1a47d0..f429dfa76 100644
--- a/tests/lib/config.js
+++ b/tests/lib/config.js
@@ -2,7 +2,6 @@
  * @fileoverview Tests for config object.
  * @author Seth McLaughlin
  */
-/* eslint no-undefined: "off" */
 "use strict";
 
 //------------------------------------------------------------------------------
@@ -875,7 +874,7 @@ describe("Config", () => {
                         parserOptions: {},
                         env: {},
                         globals: {},
-                        parser: undefined,
+                        parser: void 0,
                         rules: {
                             "home-folder-rule": 2
                         }
@@ -901,7 +900,7 @@ describe("Config", () => {
                         parserOptions: {},
                         env: {},
                         globals: {},
-                        parser: undefined,
+                        parser: void 0,
                         rules: {
                             "project-level-rule": 2
                         }
@@ -928,7 +927,7 @@ describe("Config", () => {
                         parserOptions: {},
                         env: {},
                         globals: {},
-                        parser: undefined,
+                        parser: void 0,
                         rules: {
                             quotes: [2, "double"]
                         }
@@ -953,7 +952,7 @@ describe("Config", () => {
                         parserOptions: {},
                         env: {},
                         globals: {},
-                        parser: undefined,
+                        parser: void 0,
                         rules: {
                             "project-level-rule": 2,
                             "subfolder-level-rule": 2
diff --git a/tests/lib/config/config-file.js b/tests/lib/config/config-file.js
index 23a206b05..adddd07a2 100644
--- a/tests/lib/config/config-file.js
+++ b/tests/lib/config/config-file.js
@@ -16,6 +16,7 @@ const assert = require("chai").assert,
     os = require("os"),
     yaml = require("js-yaml"),
     shell = require("shelljs"),
+    espree = require("espree"),
     ConfigFile = require("../../../lib/config/config-file"),
     Linter = require("../../../lib/linter"),
     Config = require("../../../lib/config");
@@ -48,17 +49,6 @@ function getFixturePath(filepath) {
     return path.resolve(__dirname, "../../fixtures/config-file", filepath);
 }
 
-/**
- * Reads a JS configuration object from a string to ensure that it parses.
- * Used for testing configuration file output.
- * @param {string} code The code to eval.
- * @returns {*} The result of the evaluation.
- * @private
- */
-function readJSModule(code) {
-    return eval(`var module = {};\n${code}`); // eslint-disable-line no-eval
-}
-
 /**
  * Helper function to write configs to temp file.
  * @param {Object} config Config to write out to temp file.
@@ -993,6 +983,18 @@ describe("ConfigFile", () => {
                 });
             });
         });
+
+        it("throws an error including the config file name if the config file is invalid", () => {
+            const configFilePath = getFixturePath("invalid/invalid-top-level-property.yml");
+
+            try {
+                ConfigFile.load(configFilePath, configContext);
+            } catch (err) {
+                assert.include(err.message, `ESLint configuration in ${configFilePath} is invalid`);
+                return;
+            }
+            assert.fail();
+        });
     });
 
     describe("resolve()", () => {
@@ -1217,7 +1219,7 @@ describe("ConfigFile", () => {
         });
 
         leche.withData([
-            ["JavaScript", "foo.js", readJSModule],
+            ["JavaScript", "foo.js", espree.parse],
             ["JSON", "bar.json", JSON.parse],
             ["YAML", "foo.yaml", yaml.safeLoad],
             ["YML", "foo.yml", yaml.safeLoad]
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index 5e42823b2..6fc24f0ad 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -370,31 +370,31 @@ describe("Validator", () => {
             it("should throw if override does not specify files", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
             });
 
             it("should throw if override has an empty files array", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have less than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have less than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
             });
 
             it("should throw if override has nested overrides", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n");
             });
 
             it("should throw if override extends", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n");
             });
 
             it("should throw if override tries to set root", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
             });
         });
 
diff --git a/tests/lib/ignored-paths.js b/tests/lib/ignored-paths.js
index 1ef1f29cd..f09eab1d7 100644
--- a/tests/lib/ignored-paths.js
+++ b/tests/lib/ignored-paths.js
@@ -85,7 +85,7 @@ function countDefaultPatterns(ignoredPaths) {
     let count = ignoredPaths.defaultPatterns.length;
 
     if (!ignoredPaths.options || (ignoredPaths.options.dotfiles !== true)) {
-        count = count + 2; // Two patterns for ignoring dotfiles
+        count += 2; // Two patterns for ignoring dotfiles
     }
     return count;
 }
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index d52292abc..672088c71 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -1105,7 +1105,7 @@ describe("Linter", () => {
             assert.strictEqual(messages[0].endColumn, 2);
         });
 
-        it("should not have 'endLine' and 'endColumn' when 'loc' property doe not have 'end' property.", () => {
+        it("should not have 'endLine' and 'endColumn' when 'loc' property does not have 'end' property.", () => {
             linter.on("Program", node => {
                 linter.report(
                     "test-rule",
@@ -1122,6 +1122,13 @@ describe("Linter", () => {
             assert.strictEqual(messages[0].endColumn, void 0);
         });
 
+        it("should infer an 'endLine' and 'endColumn' property when using the object-based context.report API", () => {
+            const messages = linter.verify("foo", { rules: { "no-undef": "error" } });
+
+            assert.strictEqual(messages.length, 1);
+            assert.strictEqual(messages[0].endLine, 1);
+            assert.strictEqual(messages[0].endColumn, 4);
+        });
     });
 
     describe("when evaluating code", () => {
@@ -2697,7 +2704,19 @@ describe("Linter", () => {
 
         it("should report multiple missing rules", () => {
             assert.isArray(resultsMultiple);
-            assert.equal(resultsMultiple[1].ruleId, "barfoo");
+
+            assert.deepEqual(
+                resultsMultiple[1],
+                {
+                    ruleId: "barfoo",
+                    message: "Definition for rule 'barfoo' was not found",
+                    line: 1,
+                    column: 1,
+                    severity: 1,
+                    source: "var answer = 6 * 7;",
+                    nodeType: void 0
+                }
+            );
         });
     });
 
diff --git a/tests/lib/rules/getter-return.js b/tests/lib/rules/getter-return.js
index 82542411c..13018bdea 100644
--- a/tests/lib/rules/getter-return.js
+++ b/tests/lib/rules/getter-return.js
@@ -51,14 +51,14 @@ ruleTester.run("getter-return", rule, {
         // option: {allowImplicit: false}
         { code: "Object.defineProperty(foo, \"bar\", { get: function () {return true;}});" },
         { code: "Object.defineProperty(foo, \"bar\", { get: function () { ~function (){ return true; }();return true;}});" },
-        { code: "Object.defineProperies(foo, { bar: { get: function () {return true;}} });" },
-        { code: "Object.defineProperies(foo, { bar: { get: function () { ~function (){ return true; }(); return true;}} });" },
+        { code: "Object.defineProperties(foo, { bar: { get: function () {return true;}} });" },
+        { code: "Object.defineProperties(foo, { bar: { get: function () { ~function (){ return true; }(); return true;}} });" },
 
         // option: {allowImplicit: true}
         { code: "Object.defineProperty(foo, \"bar\", { get: function () {return true;}});", options },
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){return;}});", options },
-        { code: "Object.defineProperies(foo, { bar: { get: function () {return true;}} });", options },
-        { code: "Object.defineProperies(foo, { bar: { get: function () {return;}} });", options },
+        { code: "Object.defineProperties(foo, { bar: { get: function () {return true;}} });", options },
+        { code: "Object.defineProperties(foo, { bar: { get: function () {return;}} });", options },
 
         // not getter.
         { code: "var get = function(){};" },
@@ -97,9 +97,9 @@ ruleTester.run("getter-return", rule, {
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){}});", errors: [{ noReturnMessage }] },
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){if(bar) {return true;}}});", errors: [{ message: "Expected method 'get' to always return a value." }] },
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){ ~function () { return true; }()}});", errors: [{ noReturnMessage }] },
-        { code: "Object.defineProperies(foo, { bar: { get: function () {}} });", options, errors: [{ noReturnMessage }] },
-        { code: "Object.defineProperies(foo, { bar: { get: function (){if(bar) {return true;}}}});", options, errors: [{ message: "Expected method 'get' to always return a value." }] },
-        { code: "Object.defineProperies(foo, { bar: { get: function () {~function () { return true; }()}} });", options, errors: [{ noReturnMessage }] },
+        { code: "Object.defineProperties(foo, { bar: { get: function () {}} });", options, errors: [{ noReturnMessage }] },
+        { code: "Object.defineProperties(foo, { bar: { get: function (){if(bar) {return true;}}}});", options, errors: [{ message: "Expected method 'get' to always return a value." }] },
+        { code: "Object.defineProperties(foo, { bar: { get: function () {~function () { return true; }()}} });", options, errors: [{ noReturnMessage }] },
 
         // option: {allowImplicit: true}
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){}});", options, errors: [{ message: "Expected to return a value in method 'get'." }] }
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index fb621cb49..d1fd9c1ca 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -2288,6 +2288,22 @@ ruleTester.run("indent", rule, {
             `,
             options: [2]
         },
+        {
+            code: unIndent`
+                ({
+                    foo:
+                        bar
+                })
+            `
+        },
+        {
+            code: unIndent`
+                ({
+                    [foo]:
+                        bar
+                })
+            `
+        },
         {
 
             // Comments in switch cases
diff --git a/tests/lib/rules/no-multi-spaces.js b/tests/lib/rules/no-multi-spaces.js
index b983430a4..d3dcf61f5 100644
--- a/tests/lib/rules/no-multi-spaces.js
+++ b/tests/lib/rules/no-multi-spaces.js
@@ -98,7 +98,10 @@ ruleTester.run("no-multi-spaces", rule, {
 
         "foo\n\f  bar",
         "foo\n\u2003  bar",
-        "foo\n \f  bar"
+        "foo\n \f  bar",
+
+        // https://github.com/eslint/eslint/issues/9001
+        "a".repeat(2e5)
     ],
 
     invalid: [
diff --git a/tests/lib/rules/padding-line-between-statements.js b/tests/lib/rules/padding-line-between-statements.js
index e4a0d8109..543c06dd9 100644
--- a/tests/lib/rules/padding-line-between-statements.js
+++ b/tests/lib/rules/padding-line-between-statements.js
@@ -2359,6 +2359,12 @@ ruleTester.run("padding-line-between-statements", rule, {
             options: [
                 { blankLine: "always", prev: "*", next: ["if", "for", "return", "switch", "case", "break", "throw", "while", "default"] }
             ]
+        },
+        {
+            code: "function test() {};",
+            options: [
+                { blankLine: "always", prev: "block-like", next: "block-like" }
+            ]
         }
     ],
     invalid: [
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 86bf40cde..84e6febae 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -648,7 +648,7 @@ describe("RuleTester", () => {
                 ],
                 invalid: []
             });
-        }, /ESLint configuration is invalid./);
+        }, /ESLint configuration in rule-tester is invalid./);
     });
 
     it("throw an error when an invalid config value is included", () => {
diff --git a/tests/lib/util/source-code.js b/tests/lib/util/source-code.js
index cfc6f1f69..c3506ef1e 100644
--- a/tests/lib/util/source-code.js
+++ b/tests/lib/util/source-code.js
@@ -61,37 +61,39 @@ describe("SourceCode", () => {
             assert.equal(sourceCode.lines[1], "bar;");
         });
 
-        /* eslint-disable no-new */
-
         it("should throw an error when called with an AST that's missing tokens", () => {
 
-            assert.throws(() => {
-                new SourceCode("foo;", { comments: [], loc: {}, range: [] });
-            }, /missing the tokens array/);
+            assert.throws(
+                () => new SourceCode("foo;", { comments: [], loc: {}, range: [] }),
+                /missing the tokens array/
+            );
 
         });
 
         it("should throw an error when called with an AST that's missing comments", () => {
 
-            assert.throws(() => {
-                new SourceCode("foo;", { tokens: [], loc: {}, range: [] });
-            }, /missing the comments array/);
+            assert.throws(
+                () => new SourceCode("foo;", { tokens: [], loc: {}, range: [] }),
+                /missing the comments array/
+            );
 
         });
 
         it("should throw an error when called with an AST that's missing location", () => {
 
-            assert.throws(() => {
-                new SourceCode("foo;", { comments: [], tokens: [], range: [] });
-            }, /missing location information/);
+            assert.throws(
+                () => new SourceCode("foo;", { comments: [], tokens: [], range: [] }),
+                /missing location information/
+            );
 
         });
 
         it("should throw an error when called with an AST that's missing range", () => {
 
-            assert.throws(() => {
-                new SourceCode("foo;", { comments: [], tokens: [], loc: {} });
-            }, /missing range information/);
+            assert.throws(
+                () => new SourceCode("foo;", { comments: [], tokens: [], loc: {} }),
+                /missing range information/
+            );
         });
 
         it("should store all tokens and comments sorted by range", () => {