Repository: eslint/eslint (js)

Issue:
[6.5.0] `no-useless-rename` with `babel-eslint` crash at rest properties
**Tell us about your environment**

* **ESLint Version:** 6.5.0
* **Node Version:** 12.10.0
* **npm Version:** 6.10.3

**What parser (default, Babel-ESLint, etc.) are you using?**
babel eslint

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
{
  "extends": [
    "airbnb",
    "plugin:react/all"
  ],
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "parser": "babel-eslint",
  "plugins": [
    "react",
    "simple-import-sort"
  ],
  "rules": {
    "arrow-parens": [
      "error",
      "always"
    ],
    "camelcase": "error",
    "comma-dangle": [
      "error",
      "never"
    ],
    "curly": [
      "error",
      "all"
    ],
    "func-names": "error",
    "indent": [
      "error",
      2
    ],
    "max-len": [
      "error",
      {
        "code": 120,
        "ignoreUrls": true,
        "tabWidth": 2
      }
    ],
    "no-param-reassign": "off",
    "no-underscore-dangle": "off",
    "no-plusplus": "off",
    "no-console": "error",
    "no-unused-vars": [
      "error",
      {
        "argsIgnorePattern": "dispatch"
      }
    ],
    "simple-import-sort/sort": "error",
    "sort-keys": [
      "error",
      "asc",
      {
        "natural": true,
        "caseSensitive": false
      }
    ],
    "sort-vars": "error",
    "react/jsx-indent": [
      "error",
      2
    ],
    "react/jsx-indent-props": [
      "error",
      2
    ],
    "react/jsx-max-depth": "off",
    "react/jsx-no-literals": "off",
    "react/jsx-one-expression-per-line": "off",
    "react/destructuring-assignment": "off",
    "react/jsx-key": "off",
    "react/no-did-mount-set-state": "off",
    "react/no-set-state": "off",
    "react/prefer-stateless-function": "off",
    "react/jsx-props-no-spreading": "off",
    "jsx-a11y/click-events-have-key-events": "off",
    "jsx-a11y/no-static-element-interactions": "off",
    // TODO: All rules below here will eventually be accounted for
    "react/jsx-handler-names": "off",
    "react/prop-types": "off",
    "react/jsx-fragments": "off"
  }
}
```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
```js
import { Button as MuiButton, withStyles } from '@material-ui/core';
import { darken } from '@material-ui/core/styles';
import * as React from 'react';
import { Link } from 'react-router-dom';

class Button extends React.Component {
  // TODO: Implement
  shouldComponentUpdate() {
    return true;
  }

  render() {
    const {
      classes, href, icon, ...otherProps
    } = this.props;

    const buttonContent = (
      <div className={classes.buttonChildrenContainer}>
        {icon && (
          <div className={classes.buttonIcon}>
            <img
              alt="button-icon"
              className={classes.buttonIconImg}
              src={icon}
            />
          </div>
        )}
        <div className={classes.buttonText}>{this.props.children}</div>
      </div>
    );

    return href ? (
      <Link to={`/${href}`}>
        <MuiButton
          className={classes.root}
          color="primary"
          fullWidth
          variant="contained"
          {...otherProps}
        >
          {buttonContent}
        </MuiButton>
      </Link>
    ) : (
      <MuiButton
        className={classes.root}
        color="primary"
        fullWidth
        variant="contained"
        {...this.props}
      >
        {buttonContent}
      </MuiButton>
    );
  }
}

const styles = (theme) => ({
  buttonChildrenContainer: {
    display: 'flex',
    height: 48,
    width: '100%'
  },
  buttonIcon: {
    backgroundColor: 'white',
    borderRadius: '3px 0 0 3px',
    padding: '10px 4px',
    width: 48
  },
  buttonIconImg: {
    maxHeight: '100%',
    maxWidth: '100%'
  },
  buttonText: {
    flex: 1,
    fontSize: 16,
    paddingTop: 10,
    textAlign: 'center'
  },
  root: {
    '&:hover': {
      backgroundColor: darken(theme.palette.secondary.main, 0.1)
    },
    backgroundColor: theme.palette.secondary.main,
    boxShadow: 'none',
    padding: 2,
    textTransform: 'none'
  }
});

export default withStyles(styles, { withTheme: true })(Button);
```

<!-- Paste the command you used to run ESLint: -->
```bash
./node_modules/.bin/eslint --ext .js --ext .jsx .
```

**What did you expect to happen?**
Everything to lint fine as it does in `6.4.0`

**What actually happened? Please include the actual, raw output from ESLint.**
```
▶ npm run lint --prefix packages/client

> client@1.0.0 lint /Users/jakeleventhal/Projects/ecominate/packages/client
> ../../node_modules/.bin/eslint --ext .js --ext .jsx .

TypeError: Cannot read property 'type' of undefined
Occurred while linting /Users/jakeleventhal/Projects/ecominate/packages/client/src/view/SignInUp/Components/Button.jsx:13
    at checkDestructured (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/rules/no-useless-rename.js:104:43)
    at /Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach (<anonymous>)
    at Object.emit (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/node-event-generator.js:253:26)
    at NodeEventGenerator.applySelectors (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/node-event-generator.js:282:22)
    at NodeEventGenerator.enterNode (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/node-event-generator.js:296:14)
    at CodePathAnalyzer.enterNode (/Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js:646:23)
    at /Users/jakeleventhal/Projects/ecominate/node_modules/eslint/lib/linter/linter.js:935:32
    at Array.forEach (<anonymous>)
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! client@1.0.0 lint: `../../node_modules/.bin/eslint --ext .js --ext .jsx .`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the client@1.0.0 lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/jakeleventhal/.npm/_logs/2019-09-29T06_40_12_714Z-debug.log
```

**Are you willing to submit a pull request to fix this bug?**
No

Pull request:
Fix: no-useless-rename handles ExperimentalRestProperty (fixes #12335)
<!--
    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.)
-->

<!--
    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:
-->

fixes #12335

**What changes did you make? (Give an overview)**

https://github.com/eslint/eslint/pull/12322 removed the check for `ExperimentalRestProperty`, causing the rule to crash with babel-eslint. This will be fixed by https://github.com/babel/babel-eslint/pull/785 on the babel-eslint side.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular!


Patch:
diff --git a/README.md b/README.md
index c553cc810..fbd99b12d 100644
--- a/README.md
+++ b/README.md
@@ -264,7 +264,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <h3>Gold Sponsors</h3>
 <p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.facebook.com"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/c35a9d7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
+<p><a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/lib/rules/no-useless-rename.js b/lib/rules/no-useless-rename.js
index 67af97c8b..f31459bd8 100644
--- a/lib/rules/no-useless-rename.js
+++ b/lib/rules/no-useless-rename.js
@@ -93,6 +93,14 @@ module.exports = {
 
             for (const property of node.properties) {
 
+                /*
+                 * TODO: Remove after babel-eslint removes ExperimentalRestProperty
+                 * https://github.com/eslint/eslint/issues/12335
+                 */
+                if (property.type === "ExperimentalRestProperty") {
+                    continue;
+                }
+
                 /**
                  * Properties using shorthand syntax and rest elements can not be renamed.
                  * If the property is computed, we have no idea if a rename is useless or not.


Test patch:
diff --git a/tests/fixtures/parsers/babel-eslint10/object-pattern-with-rest-element.js b/tests/fixtures/parsers/babel-eslint10/object-pattern-with-rest-element.js
new file mode 100644
index 000000000..84aea926b
--- /dev/null
+++ b/tests/fixtures/parsers/babel-eslint10/object-pattern-with-rest-element.js
@@ -0,0 +1,151 @@
+/**
+ * Parser: babel-eslint@10.0.3
+ * Source code:
+ * const { ...foo } = bar;
+ */
+exports.parse = () => ({
+  type: "Program",
+  start: 0,
+  end: 24,
+  loc: { start: { line: 1, column: 0 }, end: { line: 2, column: 0 } },
+  range: [0, 24],
+  comments: [],
+  tokens: [
+      {
+          type: "Keyword",
+          value: "const",
+          start: 0,
+          end: 5,
+          loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } },
+          range: [0, 5]
+      },
+      {
+          type: "Punctuator",
+          value: "{",
+          start: 6,
+          end: 7,
+          loc: { start: { line: 1, column: 6 }, end: { line: 1, column: 7 } },
+          range: [6, 7]
+      },
+      {
+          type: "Punctuator",
+          value: "...",
+          start: 8,
+          end: 11,
+          loc: { start: { line: 1, column: 8 }, end: { line: 1, column: 11 } },
+          range: [8, 11]
+      },
+      {
+          type: "Identifier",
+          value: "foo",
+          start: 11,
+          end: 14,
+          loc: { start: { line: 1, column: 11 }, end: { line: 1, column: 14 } },
+          range: [11, 14]
+      },
+      {
+          type: "Punctuator",
+          value: "}",
+          start: 15,
+          end: 16,
+          loc: { start: { line: 1, column: 15 }, end: { line: 1, column: 16 } },
+          range: [15, 16]
+      },
+      {
+          type: "Punctuator",
+          value: "=",
+          start: 17,
+          end: 18,
+          loc: { start: { line: 1, column: 17 }, end: { line: 1, column: 18 } },
+          range: [17, 18]
+      },
+      {
+          type: "Identifier",
+          value: "bar",
+          start: 19,
+          end: 22,
+          loc: { start: { line: 1, column: 19 }, end: { line: 1, column: 22 } },
+          range: [19, 22]
+      },
+      {
+          type: "Punctuator",
+          value: ";",
+          start: 22,
+          end: 23,
+          loc: { start: { line: 1, column: 22 }, end: { line: 1, column: 23 } },
+          range: [22, 23]
+      }
+  ],
+  sourceType: "module",
+  body: [
+      {
+          type: "VariableDeclaration",
+          start: 0,
+          end: 23,
+          loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 23 } },
+          range: [0, 23],
+          declarations: [
+              {
+                  type: "VariableDeclarator",
+                  start: 6,
+                  end: 22,
+                  loc: { start: { line: 1, column: 6 }, end: { line: 1, column: 22 } },
+                  range: [6, 22],
+                  id: {
+                      type: "ObjectPattern",
+                      start: 6,
+                      end: 16,
+                      loc: {
+                          start: { line: 1, column: 6 },
+                          end: { line: 1, column: 16 }
+                      },
+                      range: [6, 16],
+                      properties: [
+                          {
+                              type: "ExperimentalRestProperty",
+                              start: 8,
+                              end: 14,
+                              loc: {
+                                  start: { line: 1, column: 8 },
+                                  end: { line: 1, column: 14 }
+                              },
+                              range: [8, 14],
+                              argument: {
+                                  type: "Identifier",
+                                  start: 11,
+                                  end: 14,
+                                  loc: {
+                                      start: { line: 1, column: 11 },
+                                      end: { line: 1, column: 14 },
+                                      identifierName: "foo"
+                                  },
+                                  range: [11, 14],
+                                  name: "foo",
+                                  _babelType: "Identifier"
+                              },
+                              _babelType: "RestElement"
+                          }
+                      ],
+                      _babelType: "ObjectPattern"
+                  },
+                  init: {
+                      type: "Identifier",
+                      start: 19,
+                      end: 22,
+                      loc: {
+                          start: { line: 1, column: 19 },
+                          end: { line: 1, column: 22 },
+                          identifierName: "bar"
+                      },
+                      range: [19, 22],
+                      name: "bar",
+                      _babelType: "Identifier"
+                  },
+                  _babelType: "VariableDeclarator"
+              }
+          ],
+          kind: "const",
+          _babelType: "VariableDeclaration"
+      }
+  ]
+});
diff --git a/tests/lib/rules/no-useless-rename.js b/tests/lib/rules/no-useless-rename.js
index c9be46d69..0dccda278 100644
--- a/tests/lib/rules/no-useless-rename.js
+++ b/tests/lib/rules/no-useless-rename.js
@@ -113,6 +113,15 @@ ruleTester.run("no-useless-rename", rule, {
         {
             code: "export {foo as foo, bar as bar} from 'foo';",
             options: [{ ignoreExport: true }]
+        },
+
+        /*
+         * TODO: Remove after babel-eslint removes ExperimentalRestProperty
+         * https://github.com/eslint/eslint/issues/12335
+         */
+        {
+            code: "const { ...foo } = bar;",
+            parser: require.resolve("../../fixtures/parsers/babel-eslint10/object-pattern-with-rest-element")
         }
     ],