Repository: eslint/eslint (js)

Issue:
Do not fail on unrecognized logical operators
ESLint throws an error when trying to use the [nullish coalescing operator](https://github.com/tc39/proposal-nullish-coalescing) (`foo ?? bar`):

```
Error: unreachable
      at CodePathState.popChoiceContext (node_modules/eslint/lib/code-path-analysis/code-path-state.js:444:23)
      at processCodePathToExit (node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js:434:19)
      at CodePathAnalyzer.leaveNode (node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js:627:9)
      at Traverser.leave (node_modules/eslint/lib/linter.js:959:32)
      at Traverser.__execute (node_modules/estraverse/estraverse.js:397:31)
      at Traverser.traverse (node_modules/estraverse/estraverse.js:491:28)
      at Traverser.traverse (node_modules/eslint/lib/util/traverser.js:31:22)
      at Linter._verifyWithoutProcessors (node_modules/eslint/lib/linter.js:953:19)
      at preprocess.map.textBlock (node_modules/eslint/lib/linter.js:994:35)
      at Array.map (<anonymous>)
      at Linter.verify (node_modules/eslint/lib/linter.js:993:42)
      ...
```

That operator is part of a proposal in Stage 1, so according to the ESLint policy it doesn't have official support, but it shouldn't fail when it's present in the AST (from a different parser).

This problem happens for any operator other than `&&` and `||` in a logical expression, which are explicitly handled in the code.

This is just to track the discussion we're having in #10399. 

**Affected ESLint versions**: v4.19.1 (latest stable) and v5.0.0-rc.0 (latest).

Pull request:
Fix: do not fail on unknown operators from custom parsers (fixes #10475)
**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[X] Add something to the core
[ ] Other, please explain:

**What changes did you make? (Give an overview)**

I modified the code path analysis code to explicitly check the operator of the logical expressions in order to create new code paths. This will allow users to use a custom parser that adds a new operator (like `??`) without breaking the code. The error users see now is

```
Error: unreachable
      at CodePathState.popChoiceContext (node_modules/eslint/lib/code-path-analysis/code-path-state.js:444:23)
      at processCodePathToExit (node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js:434:19)
      at CodePathAnalyzer.leaveNode (node_modules/eslint/lib/code-path-analysis/code-path-analyzer.js:627:9)
      at Traverser.leave (node_modules/eslint/lib/linter.js:959:32)
      at Traverser.__execute (node_modules/estraverse/estraverse.js:397:31)
      at Traverser.traverse (node_modules/estraverse/estraverse.js:491:28)
      at Traverser.traverse (node_modules/eslint/lib/util/traverser.js:31:22)
      at Linter._verifyWithoutProcessors (node_modules/eslint/lib/linter.js:953:19)
      at preprocess.map.textBlock (node_modules/eslint/lib/linter.js:994:35)
      at Array.map (<anonymous>)
      at Linter.verify (node_modules/eslint/lib/linter.js:993:42)
      ...
```

Fixes #10475 (see for more details) and provides a more generic solution to #10399 (which is against the ESLint policy about providing support for pre-Stage 3 proposals).


Patch:
diff --git a/docs/rules/func-name-matching.md b/docs/rules/func-name-matching.md
index 564671d57..4d502869c 100644
--- a/docs/rules/func-name-matching.md
+++ b/docs/rules/func-name-matching.md
@@ -4,10 +4,6 @@
 
 This rule requires function names to match the name of the variable or property to which they are assigned. The rule will ignore property assignments where the property name is a literal that is not a valid identifier in the ECMAScript version specified in your configuration (default ES5).
 
-## Options
-
-This rule takes an optional string of "always" or "never" (when omitted, it defaults to "always"), and an optional options object with one key, `includeCommonJSModuleExports`, and a boolean value. This option defaults to `false`, which means that `module.exports` and `module["exports"]` are ignored by this rule. If `includeCommonJSModuleExports` is set to true, `module.exports` and `module["exports"]` will be checked by this rule.
-
 Examples of **incorrect** code for this rule:
 
 ```js
@@ -21,14 +17,6 @@ var obj = {foo: function bar() {}};
 ({['foo']: function bar() {}});
 ```
 
-```js
-/*eslint func-name-matching: ["error", { "includeCommonJSModuleExports": true }]*/
-/*eslint func-name-matching: ["error", "always", { "includeCommonJSModuleExports": true }]*/ // these are equivalent
-
-module.exports = function foo(name) {};
-module['exports'] = function foo(name) {};
-```
-
 ```js
 /*eslint func-name-matching: ["error", "never"] */
 
@@ -97,6 +85,52 @@ module.exports = function foo(name) {};
 module['exports'] = function foo(name) {};
 ```
 
+## Options
+
+This rule takes an optional string of "always" or "never" (when omitted, it defaults to "always"), and an optional options object with two properties `considerPropertyDescriptor` and `includeCommonJSModuleExports`.
+
+### considerPropertyDescriptor
+
+A boolean value that defaults to `false`. If `considerPropertyDescriptor` is set to true, the check will take into account the use `Object.create`, `Object.defineProperty`, `Object.defineProperties`, and `Reflect.defineProperty`.
+
+Examples of **correct** code for the `{ considerPropertyDescriptor: true }` option:
+
+```js
+/*eslint func-name-matching: ["error", { "considerPropertyDescriptor": true }]*/
+/*eslint func-name-matching: ["error", "always", { "considerPropertyDescriptor": true }]*/ // these are equivalent
+var obj = {};
+Object.create(obj, {foo:{value: function foo() {}}});
+Object.defineProperty(obj, 'bar', {value: function bar() {}});
+Object.defineProperties(obj, {baz:{value: function baz() {} }});
+Reflect.defineProperty(obj, 'foo', {value: function foo() {}});
+```
+
+Examples of **incorrect** code for the `{ considerPropertyDescriptor: true }` option:
+
+```js
+/*eslint func-name-matching: ["error", { "considerPropertyDescriptor": true }]*/
+/*eslint func-name-matching: ["error", "always", { "considerPropertyDescriptor": true }]*/ // these are equivalent
+var obj = {};
+Object.create(obj, {foo:{value: function bar() {}}});
+Object.defineProperty(obj, 'bar', {value: function baz() {}});
+Object.defineProperties(obj, {baz:{value: function foo() {} }});
+Reflect.defineProperty(obj, 'foo', {value: function value() {}});
+```
+
+### includeCommonJSModuleExports
+
+A boolean value that defaults to `false`. If `includeCommonJSModuleExports` is set to true, `module.exports` and `module["exports"]` will be checked by this rule.
+
+Examples of **incorrect** code for the `{ includeCommonJSModuleExports: true }` option:
+
+```js
+/*eslint func-name-matching: ["error", { "includeCommonJSModuleExports": true }]*/
+/*eslint func-name-matching: ["error", "always", { "includeCommonJSModuleExports": true }]*/ // these are equivalent
+
+module.exports = function foo(name) {};
+module['exports'] = function foo(name) {};
+```
+
 ## When Not To Use It
 
 Do not use this rule if you want to allow named functions to have different names from the variable or property to which they are assigned.
diff --git a/lib/code-path-analysis/code-path-analyzer.js b/lib/code-path-analysis/code-path-analyzer.js
index 1a4f7870b..80b73faa3 100644
--- a/lib/code-path-analysis/code-path-analyzer.js
+++ b/lib/code-path-analysis/code-path-analyzer.js
@@ -30,6 +30,17 @@ function isCaseNode(node) {
     return Boolean(node.test);
 }
 
+/**
+ * Checks whether the given logical operator is taken into account for the code
+ * path analysis.
+ *
+ * @param {string} operator - The operator found in the LogicalExpression node
+ * @returns {boolean} `true` if the operator is "&&" or "||"
+ */
+function isHandledLogicalOperator(operator) {
+    return operator === "&&" || operator === "||";
+}
+
 /**
  * Checks whether or not a given logical expression node goes different path
  * between the `true` case and the `false` case.
@@ -230,7 +241,10 @@ function preprocess(analyzer, node) {
 
     switch (parent.type) {
         case "LogicalExpression":
-            if (parent.right === node) {
+            if (
+                parent.right === node &&
+                isHandledLogicalOperator(parent.operator)
+            ) {
                 state.makeLogicalRight();
             }
             break;
@@ -361,7 +375,12 @@ function processCodePathToEnter(analyzer, node) {
             break;
 
         case "LogicalExpression":
-            state.pushChoiceContext(node.operator, isForkingByTrueOrFalse(node));
+            if (isHandledLogicalOperator(node.operator)) {
+                state.pushChoiceContext(
+                    node.operator,
+                    isForkingByTrueOrFalse(node)
+                );
+            }
             break;
 
         case "ConditionalExpression":
@@ -430,10 +449,15 @@ function processCodePathToExit(analyzer, node) {
     switch (node.type) {
         case "IfStatement":
         case "ConditionalExpression":
-        case "LogicalExpression":
             state.popChoiceContext();
             break;
 
+        case "LogicalExpression":
+            if (isHandledLogicalOperator(node.operator)) {
+                state.popChoiceContext();
+            }
+            break;
+
         case "SwitchStatement":
             state.popSwitchContext();
             break;
diff --git a/lib/rules/func-name-matching.js b/lib/rules/func-name-matching.js
index 85fd7d4f1..b71799596 100644
--- a/lib/rules/func-name-matching.js
+++ b/lib/rules/func-name-matching.js
@@ -58,6 +58,9 @@ const alwaysOrNever = { enum: ["always", "never"] };
 const optionsObject = {
     type: "object",
     properties: {
+        considerPropertyDescriptor: {
+            type: "boolean"
+        },
         includeCommonJSModuleExports: {
             type: "boolean"
         }
@@ -90,9 +93,26 @@ module.exports = {
     create(context) {
         const options = (typeof context.options[0] === "object" ? context.options[0] : context.options[1]) || {};
         const nameMatches = typeof context.options[0] === "string" ? context.options[0] : "always";
+        const considerPropertyDescriptor = options.considerPropertyDescriptor;
         const includeModuleExports = options.includeCommonJSModuleExports;
         const ecmaVersion = context.parserOptions && context.parserOptions.ecmaVersion ? context.parserOptions.ecmaVersion : 5;
 
+        /**
+         * Check whether node is a certain CallExpression.
+         * @param {string} objName object name
+         * @param {string} funcName function name
+         * @param {ASTNode} node The node to check
+         * @returns {boolean} `true` if node matches CallExpression
+         */
+        function isPropertyCall(objName, funcName, node) {
+            if (!node) {
+                return false;
+            }
+            return node.type === "CallExpression" &&
+                node.callee.object.name === objName &&
+                node.callee.property.name === funcName;
+        }
+
         /**
          * Compares identifiers based on the nameMatches option
          * @param {string} x the first identifier
@@ -147,7 +167,6 @@ module.exports = {
         //--------------------------------------------------------------------------
 
         return {
-
             VariableDeclarator(node) {
                 if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
                     return;
@@ -179,9 +198,38 @@ module.exports = {
                 if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
                     return;
                 }
-                if (node.key.type === "Identifier" && shouldWarn(node.key.name, node.value.id.name)) {
-                    report(node, node.key.name, node.value.id.name, true);
-                } else if (
+
+                if (node.key.type === "Identifier") {
+                    const functionName = node.value.id.name;
+                    let propertyName = node.key.name;
+
+                    if (considerPropertyDescriptor && propertyName === "value") {
+                        if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) {
+                            const property = node.parent.parent.arguments[1];
+
+                            if (isStringLiteral(property) && shouldWarn(property.value, functionName)) {
+                                report(node, property.value, functionName, true);
+                            }
+                        } else if (isPropertyCall("Object", "defineProperties", node.parent.parent.parent.parent)) {
+                            propertyName = node.parent.parent.key.name;
+                            if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
+                                report(node, propertyName, functionName, true);
+                            }
+                        } else if (isPropertyCall("Object", "create", node.parent.parent.parent.parent)) {
+                            propertyName = node.parent.parent.key.name;
+                            if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
+                                report(node, propertyName, functionName, true);
+                            }
+                        } else if (shouldWarn(propertyName, functionName)) {
+                            report(node, propertyName, functionName, true);
+                        }
+                    } else if (shouldWarn(propertyName, functionName)) {
+                        report(node, propertyName, functionName, true);
+                    }
+                    return;
+                }
+
+                if (
                     isStringLiteral(node.key) &&
                     isIdentifier(node.key.value, ecmaVersion) &&
                     shouldWarn(node.key.value, node.value.id.name)
diff --git a/lib/rules/no-shadow-restricted-names.js b/lib/rules/no-shadow-restricted-names.js
index 865cee46a..70052f56f 100644
--- a/lib/rules/no-shadow-restricted-names.js
+++ b/lib/rules/no-shadow-restricted-names.js
@@ -24,36 +24,19 @@ module.exports = {
 
         const RESTRICTED = ["undefined", "NaN", "Infinity", "arguments", "eval"];
 
-        /**
-         * Check if the node name is present inside the restricted list
-         * @param {ASTNode} id id to evaluate
-         * @returns {void}
-         * @private
-         */
-        function checkForViolation(id) {
-            if (RESTRICTED.indexOf(id.name) > -1) {
-                context.report({
-                    node: id,
-                    message: "Shadowing of global property '{{idName}}'.",
-                    data: {
-                        idName: id.name
-                    }
-                });
-            }
-        }
-
         return {
-            VariableDeclarator(node) {
-                checkForViolation(node.id);
-            },
-            ":function"(node) {
-                if (node.id) {
-                    checkForViolation(node.id);
+            "VariableDeclaration, :function, CatchClause"(node) {
+                for (const variable of context.getDeclaredVariables(node)) {
+                    if (variable.defs.length > 0 && RESTRICTED.includes(variable.name)) {
+                        context.report({
+                            node: variable.defs[0].name,
+                            message: "Shadowing of global property '{{idName}}'.",
+                            data: {
+                                idName: variable.name
+                            }
+                        });
+                    }
                 }
-                node.params.forEach(checkForViolation);
-            },
-            "CatchClause[param!=null]"(node) {
-                checkForViolation(node.param);
             }
         };
 
diff --git a/lib/rules/no-unneeded-ternary.js b/lib/rules/no-unneeded-ternary.js
index 7f82c8ee2..b56ad038a 100644
--- a/lib/rules/no-unneeded-ternary.js
+++ b/lib/rules/no-unneeded-ternary.js
@@ -140,7 +140,7 @@ module.exports = {
                         fix: fixer => {
                             let nodeAlternate = astUtils.getParenthesisedText(sourceCode, node.alternate);
 
-                            if (node.alternate.type === "ConditionalExpression") {
+                            if (node.alternate.type === "ConditionalExpression" || node.alternate.type === "YieldExpression") {
                                 const isAlternateParenthesised = astUtils.isParenthesised(sourceCode, node.alternate);
 
                                 nodeAlternate = isAlternateParenthesised ? nodeAlternate : `(${nodeAlternate})`;


Test patch:
diff --git a/tests/fixtures/parsers/unknown-operators/unknown-logical-operator.js b/tests/fixtures/parsers/unknown-operators/unknown-logical-operator.js
new file mode 100644
index 000000000..eadc5979b
--- /dev/null
+++ b/tests/fixtures/parsers/unknown-operators/unknown-logical-operator.js
@@ -0,0 +1,156 @@
+/**
+ * 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:
+ * null ?? 'foo'
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    start: 0,
+    end: 13,
+    loc: {
+        start: {
+            line: 1,
+            column: 0
+        },
+        end: {
+            line: 1,
+            column: 13
+        }
+    },
+    range: [0, 13],
+    comments: [],
+    tokens: [
+        {
+            type: "Null",
+            value: "null",
+            start: 0,
+            end: 4,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 4
+                }
+            },
+            range: [0, 4]
+        },
+        {
+            type: "Punctuator",
+            value: "%%",
+            start: 5,
+            end: 7,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 5
+                },
+                end: {
+                    line: 1,
+                    column: 7
+                }
+            },
+            range: [5, 7]
+        },
+        {
+            type: "String",
+            value: "'foo'",
+            start: 8,
+            end: 13,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 8
+                },
+                end: {
+                    line: 1,
+                    column: 13
+                }
+            },
+            range: [8, 13]
+        }
+    ],
+    sourceType: "script",
+    body: [
+        {
+            type: "ExpressionStatement",
+            start: 0,
+            end: 13,
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 13
+                }
+            },
+            range: [0, 13],
+            expression: {
+                type: "LogicalExpression",
+                start: 0,
+                end: 13,
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 0
+                    },
+                    end: {
+                        line: 1,
+                        column: 13
+                    }
+                },
+                range: [0, 13],
+                left: {
+                    type: "Literal",
+                    start: 0,
+                    end: 4,
+                    loc: {
+                        start: {
+                            line: 1,
+                            column: 0
+                        },
+                        end: {
+                            line: 1,
+                            column: 4
+                        }
+                    },
+                    range: [0, 4],
+                    value: null,
+                    raw: "null",
+                    _babelType: "Literal"
+                },
+                operator: "%%",
+                right: {
+                    type: "Literal",
+                    start: 8,
+                    end: 13,
+                    loc: {
+                        start: {
+                            line: 1,
+                            column: 8
+                        },
+                        end: {
+                            line: 1,
+                            column: 13
+                        }
+                    },
+                    range: [8, 13],
+                    value: "foo",
+                    raw: "'foo'",
+                    _babelType: "Literal"
+                },
+                _babelType: "LogicalExpression"
+            },
+            _babelType: "ExpressionStatement"
+        }
+    ]
+});
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index 15177c5cd..251057021 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -4470,6 +4470,16 @@ describe("Linter", () => {
                 assert.strictEqual(messages[0].message, "Cannot find module 'esprima-xyz'");
             });
 
+            it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
+                const code = "null %% 'foo'";
+                const parser = path.join(parserFixtures, "unknown-operators", "unknown-logical-operator.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/rules/func-name-matching.js b/tests/lib/rules/func-name-matching.js
index 6f33fbe9a..1459dc1e4 100644
--- a/tests/lib/rules/func-name-matching.js
+++ b/tests/lib/rules/func-name-matching.js
@@ -177,6 +177,84 @@ ruleTester.run("func-name-matching", rule, {
         {
             code: "var {a} = function foo() {}",
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "({ value: function value() {} })",
+            options: [{ considerPropertyDescriptor: true }]
+        },
+        {
+            code: "obj.foo = function foo() {};",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "obj.bar.foo = function foo() {};",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "var obj = {foo: function foo() {}};",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "var obj = {foo: function() {}};",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "var obj = { value: function value() {} }",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Object.defineProperty(foo, 'bar', { value: function bar() {} })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Object.defineProperties(foo, { bar: { value: function bar() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Object.create(proto, { bar: { value: function bar() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Object.defineProperty(foo, 'b' + 'ar', { value: function bar() {} })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Object.defineProperties(foo, { ['bar']: { value: function bar() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "Object.create(proto, { ['bar']: { value: function bar() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "Object.defineProperty(foo, 'bar', { value() {} })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "Object.defineProperties(foo, { bar: { value() {} } })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "Object.create(proto, { bar: { value() {} } })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "Reflect.defineProperty(foo, 'bar', { value: function bar() {} })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Reflect.defineProperty(foo, 'b' + 'ar', { value: function baz() {} })",
+            options: ["always", { considerPropertyDescriptor: true }]
+        },
+        {
+            code: "Reflect.defineProperty(foo, 'bar', { value() {} })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            parserOptions: { ecmaVersion: 6 }
         }
     ],
     invalid: [
@@ -305,6 +383,69 @@ ruleTester.run("func-name-matching", rule, {
             errors: [
                 { message: "Function name `foo` should not match property name `foo`" }
             ]
+        },
+        {
+            code: "Object.defineProperty(foo, 'bar', { value: function baz() {} })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `baz` should match property name `bar`" }
+            ]
+        },
+        {
+            code: "Object.defineProperties(foo, { bar: { value: function baz() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `baz` should match property name `bar`" }
+            ]
+        },
+        {
+            code: "Object.create(proto, { bar: { value: function baz() {} } })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `baz` should match property name `bar`" }
+            ]
+        },
+        {
+            code: "var obj = { value: function foo(name) {} }",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `foo` should match property name `value`" }
+            ]
+        },
+        {
+            code: "Object.defineProperty(foo, 'bar', { value: function bar() {} })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `bar` should not match property name `bar`" }
+            ]
+        },
+        {
+            code: "Object.defineProperties(foo, { bar: { value: function bar() {} } })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `bar` should not match property name `bar`" }
+            ]
+        },
+        {
+            code: "Object.create(proto, { bar: { value: function bar() {} } })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `bar` should not match property name `bar`" }
+            ]
+        },
+        {
+            code: "Reflect.defineProperty(foo, 'bar', { value: function baz() {} })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `baz` should match property name `bar`" }
+            ]
+        },
+        {
+            code: "Reflect.defineProperty(foo, 'bar', { value: function bar() {} })",
+            options: ["never", { considerPropertyDescriptor: true }],
+            errors: [
+                { message: "Function name `bar` should not match property name `bar`" }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-shadow-restricted-names.js b/tests/lib/rules/no-shadow-restricted-names.js
index 4655bf715..3c014a70f 100644
--- a/tests/lib/rules/no-shadow-restricted-names.js
+++ b/tests/lib/rules/no-shadow-restricted-names.js
@@ -93,6 +93,23 @@ ruleTester.run("no-shadow-restricted-names", rule, {
                 { message: "Shadowing of global property 'eval'.", type: "Identifier" },
                 { message: "Shadowing of global property 'eval'.", type: "Identifier" }
             ]
+        },
+        {
+            code: "var [undefined] = [1]",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                { message: "Shadowing of global property 'undefined'.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "var {undefined} = obj; var {a: undefined} = obj; var {a: {b: {undefined}}} = obj; var {a, ...undefined} = obj;",
+            parserOptions: { ecmaVersion: 9 },
+            errors: [
+                { message: "Shadowing of global property 'undefined'.", type: "Identifier" },
+                { message: "Shadowing of global property 'undefined'.", type: "Identifier" },
+                { message: "Shadowing of global property 'undefined'.", type: "Identifier" },
+                { message: "Shadowing of global property 'undefined'.", type: "Identifier" }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-unneeded-ternary.js b/tests/lib/rules/no-unneeded-ternary.js
index 05251ccbd..badf529e6 100644
--- a/tests/lib/rules/no-unneeded-ternary.js
+++ b/tests/lib/rules/no-unneeded-ternary.js
@@ -197,6 +197,18 @@ ruleTester.run("no-unneeded-ternary", rule, {
                 column: 7
             }]
         },
+        {
+            code: "function* fn() { foo ? foo : yield bar }",
+            output: "function* fn() { foo || (yield bar) }",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 24
+            }]
+        },
         {
             code: "var a = foo ? foo : 'No';",
             output: "var a = foo || 'No';",