Repository: eslint/eslint (js)

Issue:
no-unneeded-ternary with arrow function results in parsing error
**What version of eslint?**
5.16.0
**What operating system, Node.js, and npm version?**
Mac OS X Mojave ⬢ 8.11.2 📦 5.6.0

**What parser (default, Babel-ESLint, etc.) are you using?**
babel-eslint 9.0.0

Using the `no-unneeded-ternary` rule.
When formatting code that is using a ternary like below, the function is not wrapped, causing a parse error and fails.
```
const foo = true
const bar = foo ? foo : () => {}
```
Causes this error
```
  2:21  error  Parsing error: Unexpected token

  1 | const foo = true
> 2 | const bar = foo || () => {}
    |                     ^
  3 |

✖ 1 problem (1 error, 0 warnings)
```

*EXPECTED*
```
const bar = foo || (() => {})
```

**Please show your full configuration:**
I am using standardJS 12.0.1, which sets the config as follows:
```
{
  "parserOptions": {
    "ecmaVersion": 2018,
    "ecmaFeatures": {
      "jsx": true
    },
    "sourceType": "module"
  },

  "env": {
    "es6": true,
    "node": true
  },

  "plugins": [
    "import",
    "node",
    "promise",
    "standard"
  ],

  "globals": {
    "document": false,
    "navigator": false,
    "window": false
  },

  "rules": {
    "accessor-pairs": "error",
    "arrow-spacing": ["error", { "before": true, "after": true }],
    "block-spacing": ["error", "always"],
    "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
    "camelcase": ["error", { "properties": "never" }],
    "comma-dangle": ["error", {
      "arrays": "never",
      "objects": "never",
      "imports": "never",
      "exports": "never",
      "functions": "never"
    }],
    "comma-spacing": ["error", { "before": false, "after": true }],
    "comma-style": ["error", "last"],
    "constructor-super": "error",
    "curly": ["error", "multi-line"],
    "dot-location": ["error", "property"],
    "eol-last": "error",
    "eqeqeq": ["error", "always", { "null": "ignore" }],
    "func-call-spacing": ["error", "never"],
    "generator-star-spacing": ["error", { "before": true, "after": true }],
    "handle-callback-err": ["error", "^(err|error)$" ],
    "indent": ["error", 2, {
      "SwitchCase": 1,
      "VariableDeclarator": 1,
      "outerIIFEBody": 1,
      "MemberExpression": 1,
      "FunctionDeclaration": { "parameters": 1, "body": 1 },
      "FunctionExpression": { "parameters": 1, "body": 1 },
      "CallExpression": { "arguments": 1 },
      "ArrayExpression": 1,
      "ObjectExpression": 1,
      "ImportDeclaration": 1,
      "flatTernaryExpressions": false,
      "ignoreComments": false
    }],
    "key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
    "keyword-spacing": ["error", { "before": true, "after": true }],
    "new-cap": ["error", { "newIsCap": true, "capIsNew": false }],
    "new-parens": "error",
    "no-array-constructor": "error",
    "no-caller": "error",
    "no-class-assign": "error",
    "no-compare-neg-zero": "error",
    "no-cond-assign": "error",
    "no-const-assign": "error",
    "no-constant-condition": ["error", { "checkLoops": false }],
    "no-control-regex": "error",
    "no-debugger": "error",
    "no-delete-var": "error",
    "no-dupe-args": "error",
    "no-dupe-class-members": "error",
    "no-dupe-keys": "error",
    "no-duplicate-case": "error",
    "no-empty-character-class": "error",
    "no-empty-pattern": "error",
    "no-eval": "error",
    "no-ex-assign": "error",
    "no-extend-native": "error",
    "no-extra-bind": "error",
    "no-extra-boolean-cast": "error",
    "no-extra-parens": ["error", "functions"],
    "no-fallthrough": "error",
    "no-floating-decimal": "error",
    "no-func-assign": "error",
    "no-global-assign": "error",
    "no-implied-eval": "error",
    "no-inner-declarations": ["error", "functions"],
    "no-invalid-regexp": "error",
    "no-irregular-whitespace": "error",
    "no-iterator": "error",
    "no-label-var": "error",
    "no-labels": ["error", { "allowLoop": false, "allowSwitch": false }],
    "no-lone-blocks": "error",
    "no-mixed-operators": ["error", {
      "groups": [
        ["==", "!=", "===", "!==", ">", ">=", "<", "<="],
        ["&&", "||"],
        ["in", "instanceof"]
      ],
      "allowSamePrecedence": true
    }],
    "no-mixed-spaces-and-tabs": "error",
    "no-multi-spaces": "error",
    "no-multi-str": "error",
    "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }],
    "no-negated-in-lhs": "error",
    "no-new": "error",
    "no-new-func": "error",
    "no-new-object": "error",
    "no-new-require": "error",
    "no-new-symbol": "error",
    "no-new-wrappers": "error",
    "no-obj-calls": "error",
    "no-octal": "error",
    "no-octal-escape": "error",
    "no-path-concat": "error",
    "no-proto": "error",
    "no-redeclare": "error",
    "no-regex-spaces": "error",
    "no-return-assign": ["error", "except-parens"],
    "no-return-await": "error",
    "no-self-assign": "error",
    "no-self-compare": "error",
    "no-sequences": "error",
    "no-shadow-restricted-names": "error",
    "no-sparse-arrays": "error",
    "no-tabs": "error",
    "no-template-curly-in-string": "error",
    "no-this-before-super": "error",
    "no-throw-literal": "error",
    "no-trailing-spaces": "error",
    "no-undef": "error",
    "no-undef-init": "error",
    "no-unexpected-multiline": "error",
    "no-unmodified-loop-condition": "error",
    "no-unneeded-ternary": ["error", { "defaultAssignment": false }],
    "no-unreachable": "error",
    "no-unsafe-finally": "error",
    "no-unsafe-negation": "error",
    "no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }],
    "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
    "no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }],
    "no-useless-call": "error",
    "no-useless-computed-key": "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",
    "object-curly-spacing": ["error", "always"],
    "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }],
    "one-var": ["error", { "initialized": "never" }],
    "operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" } }],
    "padded-blocks": ["error", { "blocks": "never", "switches": "never", "classes": "never" }],
    "prefer-promise-reject-errors": "error",
    "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
    "rest-spread-spacing": ["error", "never"],
    "semi": ["error", "never"],
    "semi-spacing": ["error", { "before": false, "after": true }],
    "space-before-blocks": ["error", "always"],
    "space-before-function-paren": ["error", "always"],
    "space-in-parens": ["error", "never"],
    "space-infix-ops": "error",
    "space-unary-ops": ["error", { "words": true, "nonwords": false }],
    "spaced-comment": ["error", "always", {
      "line": { "markers": ["*package", "!", "/", ",", "="] },
      "block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] }
    }],
    "symbol-description": "error",
    "template-curly-spacing": ["error", "never"],
    "template-tag-spacing": ["error", "never"],
    "unicode-bom": ["error", "never"],
    "use-isnan": "error",
    "valid-typeof": ["error", { "requireStringLiterals": true }],
    "wrap-iife": ["error", "any", { "functionPrototypeMethods": true }],
    "yield-star-spacing": ["error", "both"],
    "yoda": ["error", "never"],

    "import/export": "error",
    "import/first": "error",
    "import/no-duplicates": "error",
    "import/no-named-default": "error",
    "import/no-webpack-loader-syntax": "error",

    "node/no-deprecated-api": "error",
    "node/process-exit-as-throw": "error",

    "promise/param-names": "error",

    "standard/array-bracket-even-spacing": ["error", "either"],
    "standard/computed-property-even-spacing": ["error", "even"],
    "standard/no-callback-literal": "error",
    "standard/object-curly-even-spacing": ["error", "either"]
  }
}
```

<!-- Paste the command you used to run ESLint: -->
```bash
npx eslint theFile.js
```

**Are you willing to submit a pull request to fix this bug?**
Tbh I wouldn't know where to start, sorry.

Originally opened this in standardJS repo but seems like an eslint issue so re-opening here.
https://github.com/standard/standard/issues/1266

Pull request description:
Fix: autofix of no-unneeded-ternary made syntax error (fixes #11579)
<!--
    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

**What changes did you make? (Give an overview)**

This PR fixes #11579.
The new logic uses operator precedence instead of whitelist of node types in order to check the necessity of parenthesizing.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular.

Patch:
diff --git a/README.md b/README.md
index dbd2a5d47..27a6592a4 100644
--- a/README.md
+++ b/README.md
@@ -245,9 +245,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
 <!--sponsorsstart-->
 <h3>Gold Sponsors</h3>
-<p><a href="https://www.gitkraken.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fgitkraken.com&height=96" alt="GitKraken" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fsalesforce.com&height=96" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://www.gitkraken.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fgitkraken.com&height=96" alt="GitKraken" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fsalesforce.com&height=96" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F68ed8b70-ebf2-11e6-9958-cb7e79408c56.png&height=96" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffiresticktricks.com&height=96" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fmarfeel.com&height=96" alt="Marfeel" height="32"></a> <a href="https://usave.co.uk/utilities/broadband/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fusave.co.uk&height=96" alt="Usave" height="32"></a></p>
+<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffiresticktricks.com&height=96" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F431b60c0-6062-11e9-b486-e1bb2d3f0d94.png&height=96" alt="Marfeel" height="32"></a> <a href="https://usave.co.uk/utilities/broadband/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fusave.co.uk&height=96" alt="Usave" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F31835450-5a98-11e9-adb1-e17fccd4c65e.png&height=96" alt="TekHattan" height="32"></a></p>
 <!--sponsorsend-->
 
 ## Technology Sponsors
diff --git a/docs/rules/no-caller.md b/docs/rules/no-caller.md
index ff408688a..f5688df38 100644
--- a/docs/rules/no-caller.md
+++ b/docs/rules/no-caller.md
@@ -10,7 +10,7 @@ function foo() {
 
 ## Rule Details
 
-This rule is aimed at discouraging the use of deprecated and sub-optimal code, but disallowing the use of `arguments.caller` and `arguments.callee`. As such, it will warn when `arguments.caller` and `arguments.callee` are used.
+This rule is aimed at discouraging the use of deprecated and sub-optimal code by disallowing the use of `arguments.caller` and `arguments.callee`. As such, it will warn when `arguments.caller` and `arguments.callee` are used.
 
 Examples of **incorrect** code for this rule:
 
diff --git a/lib/rules/no-unneeded-ternary.js b/lib/rules/no-unneeded-ternary.js
index e75a36430..4dbb78a3e 100644
--- a/lib/rules/no-unneeded-ternary.js
+++ b/lib/rules/no-unneeded-ternary.js
@@ -17,6 +17,7 @@ const OPERATOR_INVERSES = {
 
     // Operators like < and >= are not true inverses, since both will return false with NaN.
 };
+const OR_PRECEDENCE = astUtils.getPrecedence({ type: "LogicalExpression", operator: "||" });
 
 //------------------------------------------------------------------------------
 // Rule Definition
@@ -141,15 +142,16 @@ module.exports = {
                         loc: node.consequent.loc.start,
                         message: "Unnecessary use of conditional expression for default assignment.",
                         fix: fixer => {
-                            let nodeAlternate = astUtils.getParenthesisedText(sourceCode, node.alternate);
-
-                            if (node.alternate.type === "ConditionalExpression" || node.alternate.type === "YieldExpression") {
-                                const isAlternateParenthesised = astUtils.isParenthesised(sourceCode, node.alternate);
-
-                                nodeAlternate = isAlternateParenthesised ? nodeAlternate : `(${nodeAlternate})`;
-                            }
-
-                            return fixer.replaceText(node, `${astUtils.getParenthesisedText(sourceCode, node.test)} || ${nodeAlternate}`);
+                            const shouldParenthesizeAlternate = (
+                                astUtils.getPrecedence(node.alternate) < OR_PRECEDENCE &&
+                                !astUtils.isParenthesised(sourceCode, node.alternate)
+                            );
+                            const alternateText = shouldParenthesizeAlternate
+                                ? `(${sourceCode.getText(node.alternate)})`
+                                : astUtils.getParenthesisedText(sourceCode, node.alternate);
+                            const testText = astUtils.getParenthesisedText(sourceCode, node.test);
+
+                            return fixer.replaceText(node, `${testText} || ${alternateText}`);
                         }
                     });
                 }


Test patch:
diff --git a/tests/lib/rules/no-unneeded-ternary.js b/tests/lib/rules/no-unneeded-ternary.js
index badf529e6..5196b576b 100644
--- a/tests/lib/rules/no-unneeded-ternary.js
+++ b/tests/lib/rules/no-unneeded-ternary.js
@@ -230,6 +230,90 @@ ruleTester.run("no-unneeded-ternary", rule, {
                 line: 1,
                 column: 24
             }]
+        },
+        {
+            code: "var a = b ? b : c => c;",
+            output: "var a = b || (c => c);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : c = 0;",
+            output: "var a = b || (c = 0);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c => c);",
+            output: "var a = b || (c => c);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c = 0);",
+            output: "var a = b || (c = 0);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c) => (c);",
+            output: "var a = b || ((c) => (c));",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : c, d; // this is ((b ? b : c), (d))",
+            output: "var a = b || c, d; // this is ((b ? b : c), (d))",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c, d);",
+            output: "var a = b || (c, d);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
         }
     ]
 });