Repository: eslint/eslint (js)

Issue:
rule "no-multi-spaces" changed in eslint 4.4.0
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for bug reports. If you are reporting a bug, please continue on. If you are here for another reason, please see below:

    1. To propose a new rule: http://eslint.org/docs/developer-guide/contributing/new-rules
    2. To request a change: http://eslint.org/docs/developer-guide/contributing/changes
    3. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**Tell us about your environment**

* **ESLint Version:** 4.4.0
* **Node Version:** 8.1.4
* **npm Version:** 4.6.1

**What parser (default, Babel-ESLint, etc.) are you using?** default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
{
    "parserOptions": {
        "ecmaVersion": 2017
    },
	"env": {
        "es6": true
    },
    "extends": "eslint:recommended",
    "globals": {
    },
    "plugins": [
    ],
    "rules": {
        // Possible Errors
		"for-direction": "error",
		"no-await-in-loop": "error",
        "no-console": "off",
        "no-debugger": "warn",
        "no-extra-boolean-cast": "warn",
		"no-extra-parens": "off",
		"no-unsafe-finally": "error",
		"no-prototype-builtins": "off",
		"no-template-curly-in-string": "error",
        "valid-jsdoc": ["warn", {
			"prefer": {
				"return": "returns",
				"argument": "param",
				"arg": "param",
				"constructor": "class",
				"virtual": "abstract"
			},
			"preferType": {
				"Boolean": "boolean",
				"Number": "number",
				"String": "string",
				"object": "Object",
				"array": "Array",
				"date": "Date"
			},
			"matchDescription": ".+",
            "requireParamDescription": true,
            "requireReturnDescription": true,
            "requireReturn": false,
			"requireReturnType": true
        }],

        // Best Practices
		"array-callback-return": "warn",
		"class-methods-use-this": "warn",
//		"complexity": ["warn", 60], !!!
        "consistent-return": "warn",
        "curly": ["error", "all"],
        "default-case": "error",
        "dot-notation": ["error", {"allowKeywords": true, "allowPattern": "^[a-zA-Z]+(_[a-zA-Z]+)+$"}],
        "eqeqeq": "error",
        "guard-for-in": "error",
        "no-alert": "off",
        "no-caller": "error",
        "no-case-declarations": "error",
        "no-div-regex": "error",
        "no-else-return": "warn",
        "no-empty-pattern": "warn",
        "no-eq-null": "error",
        "no-eval": "error",
        "no-extend-native": "error",
        "no-extra-bind": "error",
		"no-extra-label": "warn",
        "no-fallthrough": "warn",
        "no-floating-decimal": "error",
        "no-implicit-coercion": ["warn", {"boolean": true, "number": true, "string": true}],
		"no-implicit-globals": "off", // !!!
        "no-implied-eval": "error",
        "no-iterator": "error",
        "no-labels": ["error", {"allowLoop": false, "allowSwitch": false}],
        "no-lone-blocks": "off",
        "no-loop-func": "error",
        "no-magic-numbers": "off",
	"no-multi-spaces": ["warn", {"ignoreEOLComments": true}],
        "no-multi-str": "error",
        "no-native-reassign": "error",
        "no-new": "error",
        "no-new-func": "error",
        "no-new-wrappers": "error",
        "no-octal-escape": "error",
        "no-octal": "error",
        "no-process-env": "error",
        "no-proto": "error",
        "no-redeclare": "error",
		"no-restricted-globals": [
			"error",
			// event is globally available but causes problems when used (e.preventDefault()) in a jQuery callback
			// but has not been defined as a parameter to the callback
			"event",
			"jQuery"
		],
		"no-restricted-properties": [
			"error", {
				"object": "$",
				"property": "each",
				"message": "Use native es2015 forEach."
	        },
			{
				"object": "$",
				"property": "camelCase"
	        },
			{
				"object": "$",
				"property": "cookie"
	        },
			{
				"object": "$",
				"property": "error"
	        },
			{
				"object": "$",
				"property": "extend"
	        },
			{
				"object": "$",
				"property": "globalEval"
	        },
			{
				"object": "$",
				"property": "grep"
	        },
			{
				"object": "$",
				"property": "inArray"
	        },
			{
				"object": "$",
				"property": "isArray"
	        },
			{
				"object": "$",
				"property": "isEmptyObject"
	        },
			{
				"object": "$",
				"property": "isFunction"
	        },
			{
				"object": "$",
				"property": "isNumeric"
	        },
			{
				"object": "$",
				"property": "isPlainObject"
	        },
			{
				"object": "$",
				"property": "makeArray"
	        },
			{
				"object": "$",
				"property": "map",
				"message": "Use native es2015 map."
	        },
			{
				"object": "$",
				"property": "merge"
	        },
			{
				"object": "$",
				"property": "noop"
	        },
			{
				"object": "$",
				"property": "now"
	        },
			{
				"object": "$",
				"property": "parseJSON"
	        },
			{
				"object": "$",
				"property": "parseXML"
	        },
			{
				"object": "$",
				"property": "trim"
	        },
			{
				"object": "$",
				"property": "type"
	        },
			{
				"object": "$",
				"property": "proxy",
				"message": "Use native Function.bind."
	        },
			{
				"object": "$",
				"property": "when"
	        }
		],
        "no-return-assign": "error",
        "no-script-url": "error",
		"no-self-assign": "error",
        "no-self-compare": "error",
        "no-sequences": "error",
        "no-throw-literal": "error",
		"no-unmodified-loop-condition": "warn",
        "no-unused-expressions": "error",
		"no-unused-labels": "error",
        "no-useless-call": "error",
        "no-useless-concat": "warn",
		"no-useless-return": "warn",
        "no-void": "error",
        "no-warning-comments": "off",
        "no-with": "error",
        "radix": "error",
        "vars-on-top": "error",
        "wrap-iife": ["error", "outside"],
        "yoda": ["error", "never", {"exceptRange": true}],

        // Strict Mode
        "strict": ["error", "global"],

        // Variables
        "no-catch-shadow": "error",
        "no-delete-var": "error",
        "no-label-var": "error",
        "no-shadow-restricted-names": "error",
        "no-shadow": ["warn", {"builtinGlobals": false}],
        "no-undef": "error",
        "no-undef-init": "error",
        "no-undefined": "warn",
        "no-unused-vars": ["warn", {"vars": "all", "args": "after-used"}],
        "no-use-before-define": ["error", "nofunc"],

        // Node.js and CommonJS
        "global-require": "warn",
        "handle-callback-err": "off", // !!!
		"no-buffer-constructor": "warn",
        "no-new-require": "warn",
        "no-path-concat": "warn",

        // Stylistic Issues
		"array-bracket-newline": "off", // !!!
        "array-bracket-spacing": ["warn", "never"],
        "block-spacing": ["warn", "never"],
        "brace-style": ["warn", "1tbs"],
        "camelcase": "warn",
        "comma-spacing": ["warn", {"before": false, "after": true}],
        "comma-style": ["warn", "last"],
        "computed-property-spacing": ["warn", "never"],
        "consistent-this": ["warn", "that"],
        "eol-last": "warn",
		"func-call-spacing": ["warn", "never"],
        "func-names": "off",
        "func-style": "off",
		"id-blacklist": "off",
        "id-length": "off",
        "id-match": "off",
        "indent": ["warn", "tab", {"SwitchCase": 1}],
        "jsx-quotes": "off",
        "key-spacing": ["warn", {"beforeColon": false, "afterColon": true}],
		"keyword-spacing": ["error", {"before": true, "after": true, "overrides": {}}],
        "linebreak-style": "off",
        "lines-around-comment": "off",
        "max-depth": ["warn", 10],
        "max-len":  ["warn", 2000],
//		"max-lines": ["warn", 300], !!!
        "max-nested-callbacks": ["warn", 10],
        "max-params": ["warn", 10],
        "max-statements": ["warn", 150],
		"max-statements-per-line": ["warn", {"max": 2}],
        "new-cap": "warn",
        "new-parens": "warn",
		"newline-per-chained-call": "off",
        "no-array-constructor": "warn",
		"no-bitwise": "warn",
        "no-continue": "warn",
        "no-inline-comments": "off",
        "no-lonely-if": "off",
        "no-mixed-spaces-and-tabs": "warn",
        "no-multiple-empty-lines": "off",
		"no-multi-assign": "warn",
        "no-negated-condition": "off",
        "no-nested-ternary": "warn",
        "no-new-object": "warn",
		"no-plusplus": "off",
        "no-restricted-syntax": ["warn", "WithStatement"],
        "no-ternary": "off",
        "no-trailing-spaces": "warn",
        "no-underscore-dangle": "off",
        "no-unneeded-ternary": "warn",
		"no-whitespace-before-property": "off",
//		"object-curly-newline": ["warn", {"multiline": true}], !!!
        "object-curly-spacing": ["warn", "never"],
		"one-var-declaration-per-line": "off",
        "operator-assignment": "off",
        "operator-linebreak": ["warn", "after", {"overrides": {"?": "ignore", ":": "ignore"}}],
        "padded-blocks": "off",
		"padding-line-between-statements": ["warn",
			// require blank lines before all return statements
			//{"blankLine": "always", "prev": "*", "next": "return"},
			//  require blank lines after every sequence of variable declarations
//			{"blankLine": "always", "prev": ["const", "let", "var"], "next": "*"},
//			{"blankLine": "any",    "prev": ["const", "let", "var"], "next": ["const", "let", "var"]},
			//  switch/case
//			{"blankLine": "always", "prev": ["*"], "next": "case"},
//			{"blankLine": "any",    "prev": ["switch"], "next": ["case"]},
//			{"blankLine": "always", "prev": ["*"], "next": "default"},
//			{"blankLine": "any",    "prev": ["switch"], "next": ["default"]},
			// require blank lines after all directive prologues
			{"blankLine": "always", "prev": "directive", "next": "*"},
			{"blankLine": "any",    "prev": "directive", "next": "directive"}
		],
        "quote-props": ["warn", "consistent"],
        "quotes": ["warn", "single", "avoid-escape"],
        "require-jsdoc": "off",
        "semi": ["warn", "always"],
        "semi-spacing": ["warn", {"before": false, "after": true}],
		"semi-style": ["error", "last"],
		"sort-imports": "off",
        "sort-vars": "off",
        "space-before-blocks": "warn",
        "space-before-function-paren": ["warn", {"anonymous": "always", "named": "never"}],
        "spaced-comment": ["off", "always"],
		"switch-colon-spacing": ["error", {"after": true, "before": false}],
		"unicode-bom": ["error", "never"],
        "wrap-regex": "off",

        // ECMAScript 6
        "arrow-body-style": ["error", "as-needed"],
        "arrow-parens": ["warn", "always"],
        "arrow-spacing": ["warn", {"before": true, "after": true}],
        "constructor-super": "error",
        "generator-star-spacing": ["warn", {"before": true, "after": false}],
        "no-class-assign": "error",
        "no-confusing-arrow": "error",
        "no-const-assign": "error",
        "no-dupe-class-members": "error",
		"no-duplicate-imports":  "error",
        "no-new-symbol": "error",
        "no-this-before-super": "error",
        "no-useless-constructor": "error",
		"no-useless-computed-key": "warn",
		"no-useless-rename": "warn",
        "no-var": "error",
        "object-shorthand": "off",
        "prefer-arrow-callback": "off",
		"prefer-const": "warn",
        "prefer-reflect": "off",
        "prefer-rest-params": "off",
        "prefer-spread": "off",
        "prefer-template": "off",
        "require-yield": "error",
        "template-curly-spacing": "error",
        "yield-star-spacing": "error"
    }
}
```

</details>

**What did you do? Please include the actual source code causing the issue.**

<!-- Paste the source code below: -->
```js
...
const jd = json.data;

formObject.setValue(BEC.BLANKLINESBEFORE,<tabs>jd.blankLinesBefore);
formObject.setValue(BEC.BLANKLINESAFTER,<tabs>jd.blankLinesAfter);
formObject.setValue(BEC.BREAKBEFORE,<tabs>jd.pageBreakBefore);
formObject.setValue(BEC.BREAKAFTER,<tabs>jd.pageBreakAfter);
...
```

**What did you expect to happen?**
In previous versions to eslint 4.4.0 the above example did not report a warning when aligning the second arguments in the setValue function calls. In eslint 4.4.0 a warning is reported and I see no way to exclude this very common use case from being reported.

**What actually happened? Please include the actual, raw output from ESLint.**
```
warning: Multiple spaces found before 'jd' (no-multi-spaces) at src\modules\las_mod_positions\formHelper.js:654:47:
  652 |                 const jd = json.data;
  653 |
> 654 |                 formObject.setValue(BEC.BLANKLINESBEFORE,                       jd.blankLinesBefore);
      |                                                                                 ^
  655 |                 formObject.setValue(BEC.BLANKLINESAFTER,                        jd.blankLinesAfter);
  656 |                 formObject.setValue(BEC.BREAKBEFORE,                            jd.pageBreakBefore);
  657 |                 formObject.setValue(BEC.BREAKAFTER,                                     jd.pageBreakAfter);
```


Pull request description:
Fix: no-multi-spaces to avoid reporting consecutive tabs (fixes #9079)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Bug fix (https://github.com/eslint/eslint/issues/9079)

<!--
    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 (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)**

An unintended side-effect of the refactor in 0f9727902fce753c87f45d439c521c93850d7dd8 caused `no-multi-spaces` to start reporting any consecutive whitespace characters between tokens, rather than just reporting consecutive spaces. This commit fixes the rule to only report consecutive spaces.

To be honest, reporting any consecutive whitespace seems like it would also be a reasonable behavior -- I'm not really convinced that it's better for the rule to only report spaces. But that would be a semver-major change.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular

Patch:
diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js
index 73e514335..84f1b5018 100644
--- a/lib/rules/no-multi-spaces.js
+++ b/lib/rules/no-multi-spaces.js
@@ -76,8 +76,11 @@ module.exports = {
                     }
                     const rightToken = tokensAndComments[leftIndex + 1];
 
-                    // 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) {
+                    // Ignore tokens that don't have 2 spaces between them or are on different lines
+                    if (
+                        !sourceCode.text.slice(leftToken.range[1], rightToken.range[0]).includes("  ") ||
+                        leftToken.loc.end.line < rightToken.loc.start.line
+                    ) {
                         return;
                     }
 


Test patch:
diff --git a/tests/lib/rules/no-multi-spaces.js b/tests/lib/rules/no-multi-spaces.js
index d3dcf61f5..ea726fbdd 100644
--- a/tests/lib/rules/no-multi-spaces.js
+++ b/tests/lib/rules/no-multi-spaces.js
@@ -101,7 +101,9 @@ ruleTester.run("no-multi-spaces", rule, {
         "foo\n \f  bar",
 
         // https://github.com/eslint/eslint/issues/9001
-        "a".repeat(2e5)
+        "a".repeat(2e5),
+
+        "foo\t\t+bar"
     ],
 
     invalid: [