Repository: eslint/eslint
Pull request #10546 (2018-06-30 03:41:56)

Adjust grammar of error/warnings fixable
Changes grammar of stylish to read more intuitively.

<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[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.)
-->

Adjusting a potentially confusing CLI message. Where (when reading quickly) a user may be confused about how many errors/warnings are potentially fixable with the `--fix` option. A comma may imply a new statement whereas and continues the existing one. See below it’s not immediately clear if there are three errors or six warnings or nine.

```bash
3 errors, 6 warnings potentially fixable with the `--fix` option.
```

<!--
    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)**
Changed CLI output and to remove the comma in favour of an `and`.

**Is there anything you'd like reviewers to focus on?**
Nope.



Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index e17201ac0..71f801a84 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -25,5 +25,17 @@ module.exports = {
         "eslint-plugin/test-case-shorthand-strings": "error",
         "rulesdir/multiline-comment-style": "error",
         "rulesdir/no-useless-catch": "error"
-    }
+    },
+    overrides: [
+        {
+            files: ["tests/**/*"],
+            env: { mocha: true },
+            rules: {
+                "no-restricted-syntax": ["error", {
+                    selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']",
+                    message: "`assert.doesNotThrow()` should be replaced with a comment next to the code."
+                }]
+            }
+        }
+    ]
 };
diff --git a/README.md b/README.md
index 34fd0ef36..572cd8a07 100644
--- a/README.md
+++ b/README.md
@@ -157,7 +157,7 @@ Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](ht
 
 ## Releases
 
-We have scheduled releases every two weeks on Friday or Saturday.
+We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/eslint/eslint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
 
 ## Semantic Versioning Policy
 
diff --git a/docs/developer-guide/README.md b/docs/developer-guide/README.md
index 406568c1c..2070b2da1 100644
--- a/docs/developer-guide/README.md
+++ b/docs/developer-guide/README.md
@@ -34,10 +34,14 @@ You're finally ready to start working with rules. You may want to fix an existin
 
 You've developed library-specific rules for ESLint and you want to share it with the community. You can publish an ESLint plugin on npm.
 
-## Section 6: [Node.js API](nodejs-api.md)
+## Section 6: [Working with Parsers](working-with-parsers.md)
+
+If you aren't going to use the default parser of ESLint, this section explains about using custom parsers.
+
+## Section 7: [Node.js API](nodejs-api.md)
 
 If you're interested in writing a tool that uses ESLint, then you can use the Node.js API to get programmatic access to functionality.
 
-## Section 7: [Contributing](contributing/)
+## Section 8: [Contributing](contributing/)
 
 Once you've made changes that you want to share with the community, the next step is to submit those changes back via a pull request.
diff --git a/docs/rules/no-implied-eval.md b/docs/rules/no-implied-eval.md
index a8e007a32..d300ff61d 100644
--- a/docs/rules/no-implied-eval.md
+++ b/docs/rules/no-implied-eval.md
@@ -2,7 +2,7 @@
 
 It's considered a good practice to avoid using `eval()` in JavaScript. There are security and performance implications involved with doing so, which is why many linters (including ESLint) recommend disallowing `eval()`. However, there are some other ways to pass a string and have it interpreted as JavaScript code that have similar concerns.
 
-The first is using `setTimeout()`, `setInterval()` or `execScript()` (Internet Explorer only), both of which can accept a string of JavaScript code as their first argument. For example:
+The first is using `setTimeout()`, `setInterval()` or `execScript()` (Internet Explorer only), all of which can accept a string of JavaScript code as their first argument. For example:
 
 ```js
 setTimeout("alert('Hi!');", 100);
diff --git a/docs/rules/no-irregular-whitespace.md b/docs/rules/no-irregular-whitespace.md
index e3f65159a..a1c854d1e 100644
--- a/docs/rules/no-irregular-whitespace.md
+++ b/docs/rules/no-irregular-whitespace.md
@@ -4,6 +4,8 @@ Invalid or irregular whitespace causes issues with ECMAScript 5 parsers and also
 
 Various whitespace characters can be inputted by programmers by mistake for example from copying or keyboard shortcuts. Pressing Alt + Space on macOS adds in a non breaking space character for example.
 
+A simple fix for this problem could be to rewrite the offending line from scratch. This might also be a problem introduced by the text editor: if rewriting the line does not fix it, try using a different editor.
+
 Known issues these spaces cause:
 
 * Zero Width Space
diff --git a/docs/user-guide/command-line-interface.md b/docs/user-guide/command-line-interface.md
index c9b17b5ff..9a7ade281 100644
--- a/docs/user-guide/command-line-interface.md
+++ b/docs/user-guide/command-line-interface.md
@@ -402,6 +402,8 @@ Store the info about processed files in order to only operate on the changed one
 
 **Note:** If you run ESLint with `--cache` and then run ESLint without `--cache`, the `.eslintcache` file will be deleted. This is necessary because the results of the lint might change and make `.eslintcache` invalid. If you want to control when the cache file is deleted, then use `--cache-location` to specify an alternate location for the cache file.
 
+**Note:**: As of now, only the results for successfully linted files are stored in the cache. Files which fail linting are not stored in the cache, so they will be linted every time.
+
 #### `--cache-file`
 
 Path to the cache file. If none specified `.eslintcache` will be used. The file will be created in the directory where the `eslint` command is executed. **Deprecated**: Use `--cache-location` instead.
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 8ee985689..b1284f92a 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -448,6 +448,25 @@ foo(); /* eslint-disable-line example/rule-name */
 
 **Note:** Comments that disable warnings for a portion of a file tell ESLint not to report rule violations for the disabled code. ESLint still parses the entire file, however, so disabled code still needs to be syntactically valid JavaScript.
 
+
+### Disabling Rules Only for a Group of Files
+
+To disable rules inside of a configuration file for a group of files, use the `overrides` key along with a `files` key. For example:
+
+```json
+{
+  "rules": {...},
+  "overrides": [
+    {
+      "files": ["*-test.js","*.spec.js"],
+      "rules": {
+        "no-unused-expressions": "off"
+      }
+    }
+  ]
+}
+```
+
 ## Adding Shared Settings
 
 ESLint supports adding shared settings into configuration file. You can add `settings` object to ESLint configuration file and it will be supplied to every rule that will be executed. This may be useful if you are adding custom rules and want them to have access to the same information and be easily configurable.
diff --git a/lib/code-path-analysis/code-path-analyzer.js b/lib/code-path-analysis/code-path-analyzer.js
index 80b73faa3..f4a7fee7f 100644
--- a/lib/code-path-analysis/code-path-analyzer.js
+++ b/lib/code-path-analysis/code-path-analyzer.js
@@ -60,7 +60,7 @@ function isForkingByTrueOrFalse(node) {
             return parent.test === node;
 
         case "LogicalExpression":
-            return true;
+            return isHandledLogicalOperator(parent.operator);
 
         default:
             return false;
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 185d4650d..6b151f46a 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -23,10 +23,15 @@ const ruleValidators = new WeakMap();
 let validateSchema;
 
 // Defitions for deprecation warnings.
-const deprecationWarningMessages = Object.freeze({
+const deprecationWarningMessages = {
     ESLINT_LEGACY_ECMAFEATURES: "The 'ecmaFeatures' config file property is deprecated, and has no effect.",
     ESLINT_LEGACY_OBJECT_REST_SPREAD: "The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead."
-});
+};
+const severityMap = {
+    error: 2,
+    warn: 1,
+    off: 0
+};
 
 /**
  * Gets a complete options schema for a rule.
@@ -65,12 +70,14 @@ function getRuleOptionsSchema(rule) {
  */
 function validateRuleSeverity(options) {
     const severity = Array.isArray(options) ? options[0] : options;
+    const normSeverity = typeof severity === "string" ? severityMap[severity.toLowerCase()] : severity;
 
-    if (severity !== 0 && severity !== 1 && severity !== 2 && !(typeof severity === "string" && /^(?:off|warn|error)$/i.test(severity))) {
-        throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, "")}').\n`);
+    if (normSeverity === 0 || normSeverity === 1 || normSeverity === 2) {
+        return normSeverity;
     }
 
-    return severity;
+    throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, "")}').\n`);
+
 }
 
 /**
@@ -116,7 +123,7 @@ function validateRuleOptions(rule, ruleId, options, source) {
     try {
         const severity = validateRuleSeverity(options);
 
-        if (severity !== 0 && !(typeof severity === "string" && severity.toLowerCase() === "off")) {
+        if (severity !== 0) {
             validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
         }
     } catch (err) {
diff --git a/lib/formatters/stylish.js b/lib/formatters/stylish.js
index e586fe857..48d70ecb3 100644
--- a/lib/formatters/stylish.js
+++ b/lib/formatters/stylish.js
@@ -89,7 +89,7 @@ module.exports = function(results) {
 
         if (fixableErrorCount > 0 || fixableWarningCount > 0) {
             output += chalk[summaryColor].bold([
-                "  ", fixableErrorCount, pluralize(" error", fixableErrorCount), ", ",
+                "  ", fixableErrorCount, pluralize(" error", fixableErrorCount), " and ",
                 fixableWarningCount, pluralize(" warning", fixableWarningCount),
                 " potentially fixable with the `--fix` option.\n"
             ].join(""));
diff --git a/package.json b/package.json
index d1b7f605e..40a8b7229 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
     "file-entry-cache": "^2.0.0",
     "functional-red-black-tree": "^1.0.1",
     "glob": "^7.1.2",
-    "globals": "^11.5.0",
+    "globals": "^11.7.0",
     "ignore": "^3.3.3",
     "imurmurhash": "^0.1.4",
     "inquirer": "^5.2.0",


Test patch:
diff --git a/tests/.eslintrc.yml b/tests/.eslintrc.yml
deleted file mode 100644
index f2531eaa4..000000000
--- a/tests/.eslintrc.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-env:
-  mocha: true
-
-rules:
-  no-restricted-syntax:
-    - error
-    - selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']"
-      message: "`assert.doesNotThrow()` should be replaced with a comment next to the code."
diff --git a/tests/fixtures/parsers/unknown-operators/unknown-logical-operator-nested.js b/tests/fixtures/parsers/unknown-operators/unknown-logical-operator-nested.js
new file mode 100644
index 000000000..248480710
--- /dev/null
+++ b/tests/fixtures/parsers/unknown-operators/unknown-logical-operator-nested.js
@@ -0,0 +1,227 @@
+/**
+ * The output AST has been modified to represent an unknown operator
+ * (replaced ?? with %%)
+ *
+ * Parser: babel-eslint v8.2.3
+ * (with this fix for tokens: https://github.com/babel/babel-eslint/pull/632)
+ * Source code:
+ * foo && bar ?? baz
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    start: 0,
+    end: 17,
+    loc: {
+        start: {
+            line: 1,
+            column: 0
+        },
+        end: {
+            line: 1,
+            column: 17
+        }
+    },
+    range: [0, 17],
+    comments: [],
+    tokens: [
+        {
+            type: "Identifier",
+            value: "foo",
+            start: 0,
+            end: 3,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 3
+                }
+            },
+            range: [0, 3]
+        },
+        {
+            type: "Punctuator",
+            value: "&&",
+            start: 4,
+            end: 6,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 4
+                },
+                end: {
+                    line: 1,
+                    column: 6
+                }
+            },
+            range: [4, 6]
+        },
+        {
+            type: "Identifier",
+            value: "bar",
+            start: 7,
+            end: 10,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 7
+                },
+                end: {
+                    line: 1,
+                    column: 10
+                }
+            },
+            range: [7, 10]
+        },
+        {
+            type: "Punctuator",
+            value: "%%",
+            start: 11,
+            end: 13,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 11
+                },
+                end: {
+                    line: 1,
+                    column: 13
+                }
+            },
+            range: [11, 13]
+        },
+        {
+            type: "Identifier",
+            value: "baz",
+            start: 14,
+            end: 17,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 14
+                },
+                end: {
+                    line: 1,
+                    column: 17
+                }
+            },
+            range: [14, 17]
+        }
+    ],
+    sourceType: "module",
+    body: [
+        {
+            type: "ExpressionStatement",
+            start: 0,
+            end: 17,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 17
+                }
+            },
+            range: [0, 17],
+            expression: {
+                type: "LogicalExpression",
+                start: 0,
+                end: 17,
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 0
+                    },
+                    end: {
+                        line: 1,
+                        column: 17
+                    }
+                },
+                range: [0, 17],
+                left: {
+                    type: "LogicalExpression",
+                    start: 0,
+                    end: 10,
+                    loc: {
+                        start: {
+                            line: 1,
+                            column: 0
+                        },
+                        end: {
+                            line: 1,
+                            column: 10
+                        }
+                    },
+                    range: [0, 10],
+                    left: {
+                        type: "Identifier",
+                        start: 0,
+                        end: 3,
+                        loc: {
+                            start: {
+                                line: 1,
+                                column: 0
+                            },
+                            end: {
+                                line: 1,
+                                column: 3
+                            },
+                            identifierName: "foo"
+                        },
+                        range: [0, 3],
+                        name: "foo",
+                        _babelType: "Identifier"
+                    },
+                    operator: "&&",
+                    right: {
+                        type: "Identifier",
+                        start: 7,
+                        end: 10,
+                        loc: {
+                            start: {
+                                line: 1,
+                                column: 7
+                            },
+                            end: {
+                                line: 1,
+                                column: 10
+                            },
+                            identifierName: "bar"
+                        },
+                        range: [7, 10],
+                        name: "bar",
+                        _babelType: "Identifier"
+                    },
+                    _babelType: "LogicalExpression"
+                },
+                operator: "%%",
+                right: {
+                    type: "Identifier",
+                    start: 14,
+                    end: 17,
+                    loc: {
+                        start: {
+                            line: 1,
+                            column: 14
+                        },
+                        end: {
+                            line: 1,
+                            column: 17
+                        },
+                        identifierName: "baz"
+                    },
+                    range: [14, 17],
+                    name: "baz",
+                    _babelType: "Identifier"
+                },
+                _babelType: "LogicalExpression"
+            },
+            _babelType: "ExpressionStatement"
+        }
+    ]
+});
diff --git a/tests/lib/formatters/stylish.js b/tests/lib/formatters/stylish.js
index 813788016..2a88a6dae 100644
--- a/tests/lib/formatters/stylish.js
+++ b/tests/lib/formatters/stylish.js
@@ -107,7 +107,7 @@ describe("formatter:stylish", () => {
             it("should return a string in the correct format", () => {
                 const result = formatter(code);
 
-                assert.strictEqual(result, "\nfoo.js\n  5:10  error  Unexpected foo  foo\n\n\u2716 1 problem (1 error, 0 warnings)\n  1 error, 0 warnings potentially fixable with the `--fix` option.\n");
+                assert.strictEqual(result, "\nfoo.js\n  5:10  error  Unexpected foo  foo\n\n\u2716 1 problem (1 error, 0 warnings)\n  1 error and 0 warnings potentially fixable with the `--fix` option.\n");
                 assert.strictEqual(chalkStub.yellow.bold.callCount, 0);
                 assert.strictEqual(chalkStub.red.bold.callCount, 2);
             });
@@ -146,7 +146,7 @@ describe("formatter:stylish", () => {
             it("should return a string in the correct format", () => {
                 const result = formatter(code);
 
-                assert.strictEqual(result, "\nfoo.js\n  5:10  warning  Unexpected foo  foo\n\n\u2716 1 problem (0 errors, 1 warning)\n  0 errors, 1 warning potentially fixable with the `--fix` option.\n");
+                assert.strictEqual(result, "\nfoo.js\n  5:10  warning  Unexpected foo  foo\n\n\u2716 1 problem (0 errors, 1 warning)\n  0 errors and 1 warning potentially fixable with the `--fix` option.\n");
                 assert.strictEqual(chalkStub.yellow.bold.callCount, 2);
                 assert.strictEqual(chalkStub.red.bold.callCount, 0);
             });
@@ -351,7 +351,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  1 error, 0 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  1 error and 0 warnings potentially fixable with the `--fix` option.\n");
         });
 
         it("should output fixable problems message when warnings are fixable", () => {
@@ -368,7 +368,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  0 errors, 2 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  0 errors and 2 warnings potentially fixable with the `--fix` option.\n");
         });
 
         it("should output the total number of fixable errors and warnings", () => {
@@ -394,7 +394,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  9 errors, 3 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  9 errors and 3 warnings potentially fixable with the `--fix` option.\n");
         });
     });
 });
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index 251057021..e06f3cd9a 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -4480,6 +4480,16 @@ describe("Linter", () => {
                 assert.strictEqual(messages.length, 0);
             });
 
+            it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
+                const code = "foo && bar %% baz";
+                const parser = path.join(parserFixtures, "unknown-operators", "unknown-logical-operator-nested.js");
+
+                // This shouldn't throw
+                const messages = linter.verify(code, { parser }, filename, true);
+
+                assert.strictEqual(messages.length, 0);
+            });
+
             it("should strip leading line: prefix from parser error", () => {
                 const parser = path.join(parserFixtures, "line-error.js");
                 const messages = linter.verify(";", { parser }, "filename");
diff --git a/tests/lib/testers/no-test-runners.js b/tests/lib/testers/no-test-runners.js
index a14ac3bab..eac1da3fd 100644
--- a/tests/lib/testers/no-test-runners.js
+++ b/tests/lib/testers/no-test-runners.js
@@ -4,7 +4,6 @@
  */
 "use strict";
 
-/* global describe, it */
 /* eslint-disable no-global-assign*/
 const assert = require("assert");
 const RuleTester = require("../../../lib/testers/rule-tester");
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 0a51dc26b..4265ab1b9 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -4,8 +4,6 @@
  */
 "use strict";
 
-/* global describe, it */
-
 //------------------------------------------------------------------------------
 // Requirements
 //------------------------------------------------------------------------------